Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : // Copyright (C) 2013 Andrea Mazzoleni
3 :
4 : #include "internal.h"
5 : #include "memory.h"
6 : #include "cpu.h"
7 :
8 : /**
9 : * Forwarders for parity generation.
10 : *
11 : * Set by the raid_mode() call.
12 : *
13 : * Index 0 is for one parity, 1 for the two parities, and so on.
14 : */
15 : static raid_gen_fn* raid_gen_ptr[RAID_PARITY_MAX];
16 :
17 : struct raid_gen_algo {
18 : raid_gen_fn *gen;
19 : const char *tag; /**< Hardware descriptive tag. */
20 : };
21 :
22 : /**
23 : * Registered algorithms for parity generation.
24 : *
25 : * Set by the raid_gen_register() calls.
26 : *
27 : * Indexes are the RAID_ALGO_* constants
28 : */
29 : static struct raid_gen_algo raid_gen_algo[RAID_ALGO_MAX];
30 :
31 1327991 : void raid_gen(int nd, int np, size_t size, void **v)
32 : {
33 : /* enforce limit on size */
34 1327991 : BUG_ON(size % 64 != 0);
35 :
36 : /* enforce limit on number of failures */
37 1327991 : BUG_ON(np < 1);
38 1327991 : BUG_ON(np > RAID_PARITY_MAX);
39 :
40 1327991 : raid_gen_ptr[np - 1](nd, size, v);
41 1327991 : }
42 :
43 : /**
44 : * Forwarders for data recovery.
45 : *
46 : * Set by the raid_mode() call.
47 : *
48 : * Index 0 is for one parity, 1 for the two parities, and so on.
49 : */
50 : static raid_rec_fn* raid_rec_ptr[RAID_PARITY_MAX];
51 :
52 : struct raid_rec_algo {
53 : raid_rec_fn *rec;
54 : const char *tag; /**< Hardware descriptive tag. */
55 : };
56 :
57 : /**
58 : * Registered algorithms for data recovery.
59 : *
60 : * Set by the raid_rec_register() calls.
61 : *
62 : * Indexes are the RAID_ALGO_* constants
63 : */
64 : static struct raid_rec_algo raid_rec_algo[RAID_PARITY_MAX];
65 :
66 870 : void raid_rec(int nr, int *ir, int nd, int np, size_t size, void **v)
67 : {
68 : int nrd; /* number of data blocks to recover */
69 : int nrp; /* number of parity blocks to recover */
70 :
71 : /* enforce limit on size */
72 870 : BUG_ON(size % 64 != 0);
73 :
74 : /* enforce limit on number of failures */
75 870 : BUG_ON(nr > np);
76 870 : BUG_ON(np > RAID_PARITY_MAX);
77 :
78 : /* enforce order in index vector */
79 870 : BUG_ON(nr >= 2 && ir[0] >= ir[1]);
80 870 : BUG_ON(nr >= 3 && ir[1] >= ir[2]);
81 870 : BUG_ON(nr >= 4 && ir[2] >= ir[3]);
82 870 : BUG_ON(nr >= 5 && ir[3] >= ir[4]);
83 870 : BUG_ON(nr >= 6 && ir[4] >= ir[5]);
84 :
85 : /* enforce limit on index vector */
86 870 : BUG_ON(nr > 0 && ir[nr-1] >= nd + np);
87 :
88 : /* count the number of data blocks to recover */
89 870 : nrd = 0;
90 2613 : while (nrd < nr && ir[nrd] < nd)
91 1743 : ++nrd;
92 :
93 : /* all the remaining are parity */
94 870 : nrp = nr - nrd;
95 :
96 : /* enforce limit on number of failures */
97 870 : BUG_ON(nrd > nd);
98 870 : BUG_ON(nrp > np);
99 :
100 : /* if failed data is present */
101 870 : if (nrd != 0) {
102 : int ip[RAID_PARITY_MAX];
103 : int i, j, k;
104 :
105 : /* setup the vector of parities to use */
106 6041 : for (i = 0, j = 0, k = 0; i < np; ++i) {
107 5173 : if (j < nrp && ir[nrd + j] == nd + i) {
108 : /* this parity has to be recovered */
109 22 : ++j;
110 : } else {
111 : /* this parity is used for recovering */
112 5151 : ip[k] = i;
113 5151 : ++k;
114 : }
115 : }
116 :
117 : /*
118 : * Recover the nrd data blocks specified in ir[],
119 : * using the first nrd parity in ip[] for recovering
120 : */
121 868 : raid_rec_ptr[nrd - 1](nrd, ir, ip, nd, size, v);
122 : }
123 :
124 : /* recompute all the parities up to the last bad one */
125 870 : if (nrp != 0)
126 12 : raid_gen(nd, ir[nr - 1] - nd + 1, size, v);
127 870 : }
128 :
129 227485 : void raid_data(int nr, int *id, int *ip, int nd, size_t size, void **v)
130 : {
131 : /* enforce limit on size */
132 227485 : BUG_ON(size % 64 != 0);
133 :
134 : /* enforce limit on number of failures */
135 227485 : BUG_ON(nr > nd);
136 227485 : BUG_ON(nr > RAID_PARITY_MAX);
137 :
138 : /* enforce order in index vector for data */
139 227485 : BUG_ON(nr >= 2 && id[0] >= id[1]);
140 227485 : BUG_ON(nr >= 3 && id[1] >= id[2]);
141 227485 : BUG_ON(nr >= 4 && id[2] >= id[3]);
142 227485 : BUG_ON(nr >= 5 && id[3] >= id[4]);
143 227485 : BUG_ON(nr >= 6 && id[4] >= id[5]);
144 :
145 : /* enforce limit on index vector for data */
146 227485 : BUG_ON(nr > 0 && id[nr-1] >= nd);
147 :
148 : /* enforce order in index vector for parity */
149 227485 : BUG_ON(nr >= 2 && ip[0] >= ip[1]);
150 227485 : BUG_ON(nr >= 3 && ip[1] >= ip[2]);
151 227485 : BUG_ON(nr >= 4 && ip[2] >= ip[3]);
152 227485 : BUG_ON(nr >= 5 && ip[3] >= ip[4]);
153 227485 : BUG_ON(nr >= 6 && ip[4] >= ip[5]);
154 :
155 : /* if failed data is present */
156 227485 : if (nr != 0)
157 227483 : raid_rec_algo[nr - 1].rec(nr, id, ip, nd, size, v);
158 227485 : }
159 :
160 35 : const char *raid_gen_tag(int na)
161 : {
162 35 : BUG_ON(na < 0 || na >= RAID_ALGO_MAX);
163 :
164 35 : return raid_gen_algo[na].tag;
165 : }
166 :
167 30 : const char *raid_rec_tag(int na)
168 : {
169 : /* there is no custom recover for vandermonde */
170 30 : if (na == RAID_ALGO_VANDERMONDE_PAR3)
171 0 : na = RAID_ALGO_CAUCHY_PAR3;
172 :
173 30 : BUG_ON(na < 0 || na >= RAID_PARITY_MAX);
174 :
175 30 : return raid_rec_algo[na].tag;
176 : }
177 :
178 : /**
179 : * Generator matrix currently used.
180 : */
181 : const uint8_t (*raid_gfgen)[256];
182 :
183 642 : void raid_mode(int mode)
184 : {
185 642 : BUG_ON(mode != RAID_MODE_VANDERMONDE && mode != RAID_MODE_CAUCHY);
186 :
187 642 : if (mode == RAID_MODE_VANDERMONDE) {
188 5 : raid_gen_ptr[0] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR1].gen;
189 5 : raid_gen_ptr[1] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR2].gen;
190 5 : raid_gen_ptr[2] = raid_gen_algo[RAID_ALGO_VANDERMONDE_PAR3].gen;
191 5 : raid_gen_ptr[3] = 0;
192 5 : raid_gen_ptr[4] = 0;
193 5 : raid_gen_ptr[5] = 0;
194 5 : raid_rec_ptr[0] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR1].rec;
195 5 : raid_rec_ptr[1] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR2].rec;
196 5 : raid_rec_ptr[2] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR3].rec;
197 5 : raid_rec_ptr[3] = 0;
198 5 : raid_rec_ptr[4] = 0;
199 5 : raid_rec_ptr[5] = 0;
200 5 : raid_gfgen = gfvandermonde;
201 : } else {
202 637 : raid_gen_ptr[0] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR1].gen;
203 637 : raid_gen_ptr[1] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR2].gen;
204 637 : raid_gen_ptr[2] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR3].gen;
205 637 : raid_gen_ptr[3] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR4].gen;
206 637 : raid_gen_ptr[4] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR5].gen;
207 637 : raid_gen_ptr[5] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR6].gen;
208 637 : raid_rec_ptr[0] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR1].rec;
209 637 : raid_rec_ptr[1] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR2].rec;
210 637 : raid_rec_ptr[2] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR3].rec;
211 637 : raid_rec_ptr[3] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR4].rec;
212 637 : raid_rec_ptr[4] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR5].rec;
213 637 : raid_rec_ptr[5] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR6].rec;
214 637 : raid_gfgen = gfcauchy;
215 : }
216 642 : }
217 :
218 119 : void raid_gen_force(int np, raid_gen_fn *fn)
219 : {
220 119 : BUG_ON(np < 1 || np > RAID_PARITY_MAX);
221 :
222 119 : raid_gen_ptr[np - 1] = fn;
223 119 : }
224 :
225 6143 : void raid_gen_register(int na, const char *tag, raid_gen_fn *gen)
226 : {
227 6143 : BUG_ON(na < 0 || na >= RAID_ALGO_MAX);
228 :
229 6143 : raid_gen_algo[na].tag = tag;
230 6143 : raid_gen_algo[na].gen = gen;
231 6143 : }
232 :
233 3888 : void raid_rec_register(int na, const char *tag, raid_rec_fn *rec)
234 : {
235 3888 : BUG_ON(na < 0 || na >= RAID_PARITY_MAX);
236 :
237 3888 : raid_rec_algo[na].tag = tag;
238 3888 : raid_rec_algo[na].rec = rec;
239 3888 : }
240 :
241 : /*
242 : * Initializes and selects the best algorithm.
243 : */
244 322 : void raid_init(void)
245 : {
246 322 : raid_register_int();
247 : #if defined(CONFIG_X86) && defined(CONFIG_SSE2)
248 322 : raid_register_x86();
249 : #endif
250 :
251 : /* set the default mode */
252 322 : raid_mode(RAID_MODE_CAUCHY);
253 322 : }
254 :
255 : /*
256 : * Reference parity computation.
257 : */
258 6 : void raid_gen_ref(int nd, int np, size_t size, void **vv)
259 : {
260 6 : uint8_t **v = (uint8_t **)vv;
261 : size_t i;
262 :
263 5382 : for (i = 0; i < size; ++i) {
264 : uint8_t p[RAID_PARITY_MAX];
265 : int j, d;
266 :
267 36096 : for (j = 0; j < np; ++j)
268 30720 : p[j] = 0;
269 :
270 93696 : for (d = 0; d < nd; ++d) {
271 88320 : uint8_t b = v[d][i];
272 :
273 584448 : for (j = 0; j < np; ++j)
274 496128 : p[j] ^= gfmul[b][gfgen[j][d]];
275 : }
276 :
277 36096 : for (j = 0; j < np; ++j)
278 30720 : v[nd + j][i] = p[j];
279 : }
280 6 : }
281 :
282 : /*
283 : * Size of the blocks to test.
284 : */
285 : #define TEST_SIZE 4096
286 :
287 : /*
288 : * Number of data blocks to test.
289 : */
290 : #define TEST_COUNT (65536 / TEST_SIZE)
291 :
292 : /*
293 : * Parity generation test.
294 : */
295 6 : static int raid_test_par(int nd, int np, size_t size, void **v, void **ref)
296 : {
297 : int i;
298 : void *t[TEST_COUNT + RAID_PARITY_MAX];
299 :
300 : /* setup data */
301 102 : for (i = 0; i < nd; ++i)
302 96 : t[i] = ref[i];
303 :
304 : /* setup parity */
305 27 : for (i = 0; i < np; ++i)
306 21 : t[nd + i] = v[nd + i];
307 :
308 6 : raid_gen(nd, np, size, t);
309 :
310 : /* compare parity */
311 27 : for (i = 0; i < np; ++i) {
312 21 : if (memcmp(t[nd + i], ref[nd + i], size) != 0) {
313 : /* LCOV_EXCL_START */
314 : return -1;
315 : /* LCOV_EXCL_STOP */
316 : }
317 : }
318 :
319 6 : return 0;
320 : }
321 :
322 : /*
323 : * Recovering test.
324 : */
325 18 : static int raid_test_rec(int nr, int *ir, int nd, int np, size_t size, void **v, void **ref)
326 : {
327 : int i, j;
328 : void *t[TEST_COUNT + RAID_PARITY_MAX];
329 :
330 : /* setup data and parity vector */
331 369 : for (i = 0, j = 0; i < nd + np; ++i) {
332 351 : if (j < nr && ir[j] == i) {
333 : /* this block has to be recovered */
334 63 : t[i] = v[i];
335 63 : ++j;
336 : } else {
337 : /* this block is used for recovering */
338 288 : t[i] = ref[i];
339 : }
340 : }
341 :
342 18 : raid_rec(nr, ir, nd, np, size, t);
343 :
344 : /* compare all data and parity */
345 369 : for (i = 0; i < nd + np; ++i) {
346 351 : if (t[i] != ref[i]
347 63 : && memcmp(t[i], ref[i], size) != 0) {
348 : /* LCOV_EXCL_START */
349 : return -1;
350 : /* LCOV_EXCL_STOP */
351 : }
352 : }
353 :
354 18 : return 0;
355 : }
356 :
357 : /*
358 : * Recovering test for data.
359 : */
360 18 : static int raid_test_data(int nr, int *id, int *ip, int nd, int np, size_t size, void **v, void **ref)
361 : {
362 : int i, j;
363 : void *t[TEST_COUNT + RAID_PARITY_MAX];
364 :
365 : /* setup data vector */
366 306 : for (i = 0, j = 0; i < nd; ++i) {
367 288 : if (j < nr && id[j] == i) {
368 : /* this block has to be recovered */
369 39 : t[i] = v[i];
370 39 : ++j;
371 : } else {
372 : /* this block is left unchanged */
373 249 : t[i] = ref[i];
374 : }
375 : }
376 :
377 : /* setup parity vector */
378 81 : for (i = 0, j = 0; i < np; ++i) {
379 63 : if (j < nr && ip[j] == i) {
380 : /* this block is used for recovering */
381 39 : t[nd + i] = ref[nd + i];
382 39 : ++j;
383 : } else {
384 : /* this block should not be read or written */
385 24 : t[nd + i] = 0;
386 : }
387 : }
388 :
389 18 : raid_data(nr, id, ip, nd, size, t);
390 :
391 : /* compare all data and parity */
392 306 : for (i = 0; i < nd; ++i) {
393 288 : if (t[i] != ref[i]
394 39 : && t[i] != 0
395 39 : && memcmp(t[i], ref[i], size) != 0) {
396 : /* LCOV_EXCL_START */
397 : return -1;
398 : /* LCOV_EXCL_STOP */
399 : }
400 : }
401 :
402 18 : return 0;
403 : }
404 :
405 : /*
406 : * Scan test.
407 : */
408 7 : static int raid_test_scan(int nr, int *ir, int nd, int np, size_t size, void **v, void **ref)
409 : {
410 : int i, j, ret;
411 : void *t[TEST_COUNT + RAID_PARITY_MAX];
412 : int is[RAID_PARITY_MAX];
413 :
414 : /* setup data and parity vector */
415 140 : for (i = 0, j = 0; i < nd + np; ++i) {
416 133 : if (j < nr && ir[j] == i) {
417 : /* this block is bad */
418 15 : t[i] = v[i];
419 15 : ++j;
420 : } else {
421 : /* this block is used for recovering */
422 118 : t[i] = ref[i];
423 : }
424 : }
425 :
426 7 : ret = raid_scan(is, nd, np, size, t);
427 :
428 : /* compare identified bad blocks */
429 7 : if (ret != nr)
430 1 : return -1;
431 21 : for (i = 0; i < nr; ++i) {
432 15 : if (ir[i] != is[i]) {
433 : /* LCOV_EXCL_START */
434 : return -1;
435 : /* LCOV_EXCL_STOP */
436 : }
437 : }
438 :
439 6 : return 0;
440 : }
441 :
442 1 : int raid_selftest(void)
443 1 : {
444 1 : const int nd = TEST_COUNT;
445 1 : const size_t size = TEST_SIZE;
446 1 : const int nv = nd + RAID_PARITY_MAX * 2 + 1;
447 : void *v_alloc;
448 : void **v;
449 1 : void *ref[nd + RAID_PARITY_MAX];
450 : int ir[RAID_PARITY_MAX];
451 : int ip[RAID_PARITY_MAX];
452 : int i, np;
453 1 : int ret = 0;
454 :
455 : /* ensure to have enough space for data */
456 1 : BUG_ON(nd * size > 65536);
457 :
458 1 : v = raid_malloc_vector(nd, nv, size, &v_alloc);
459 1 : if (!v) {
460 : /* LCOV_EXCL_START */
461 : return -1;
462 : /* LCOV_EXCL_STOP */
463 : }
464 :
465 1 : memset(v[nv - 1], 0, size);
466 1 : raid_zero(v[nv - 1]);
467 :
468 : /* use the multiplication table as data */
469 17 : for (i = 0; i < nd; ++i)
470 16 : ref[i] = ((uint8_t *)gfmul) + size * i;
471 :
472 : /* setup reference parity */
473 7 : for (i = 0; i < RAID_PARITY_MAX; ++i)
474 6 : ref[nd + i] = v[nd + RAID_PARITY_MAX + i];
475 :
476 : /* compute reference parity */
477 1 : raid_gen_ref(nd, RAID_PARITY_MAX, size, ref);
478 :
479 : /* test for each parity level */
480 7 : for (np = 1; np <= RAID_PARITY_MAX; ++np) {
481 : /* test parity generation */
482 6 : ret = raid_test_par(nd, np, size, v, ref);
483 6 : if (ret != 0) {
484 : /* LCOV_EXCL_START */
485 : goto bail;
486 : /* LCOV_EXCL_STOP */
487 : }
488 :
489 : /* test recovering with broken ending data disks */
490 27 : for (i = 0; i < np; ++i) {
491 : /* bad data */
492 21 : ir[i] = nd - np + i;
493 :
494 : /* good parity */
495 21 : ip[i] = i;
496 : }
497 :
498 6 : ret = raid_test_rec(np, ir, nd, np, size, v, ref);
499 6 : if (ret != 0) {
500 : /* LCOV_EXCL_START */
501 : goto bail;
502 : /* LCOV_EXCL_STOP */
503 : }
504 :
505 6 : ret = raid_test_data(np, ir, ip, nd, np, size, v, ref);
506 6 : if (ret != 0) {
507 : /* LCOV_EXCL_START */
508 : goto bail;
509 : /* LCOV_EXCL_STOP */
510 : }
511 :
512 : /* test recovering with broken leading data and broken leading parity */
513 15 : for (i = 0; i < np / 2; ++i) {
514 : /* bad data */
515 9 : ir[i] = i;
516 :
517 : /* good parity */
518 9 : ip[i] = (np + 1) / 2 + i;
519 : }
520 :
521 : /* bad parity */
522 18 : for (i = 0; i < (np + 1) / 2; ++i)
523 12 : ir[np / 2 + i] = nd + i;
524 :
525 6 : ret = raid_test_rec(np, ir, nd, np, size, v, ref);
526 6 : if (ret != 0) {
527 : /* LCOV_EXCL_START */
528 : goto bail;
529 : /* LCOV_EXCL_STOP */
530 : }
531 :
532 6 : ret = raid_test_data(np / 2, ir, ip, nd, np, size, v, ref);
533 6 : if (ret != 0) {
534 : /* LCOV_EXCL_START */
535 : goto bail;
536 : /* LCOV_EXCL_STOP */
537 : }
538 :
539 : /* test recovering with broken leading data and broken ending parity */
540 15 : for (i = 0; i < np / 2; ++i) {
541 : /* bad data */
542 9 : ir[i] = i;
543 :
544 : /* good parity */
545 9 : ip[i] = i;
546 : }
547 :
548 : /* bad parity */
549 18 : for (i = 0; i < (np + 1) / 2; ++i)
550 12 : ir[np / 2 + i] = nd + np - (np + 1) / 2 + i;
551 :
552 6 : ret = raid_test_rec(np, ir, nd, np, size, v, ref);
553 6 : if (ret != 0) {
554 : /* LCOV_EXCL_START */
555 : goto bail;
556 : /* LCOV_EXCL_STOP */
557 : }
558 :
559 6 : ret = raid_test_data(np / 2, ir, ip, nd, np, size, v, ref);
560 6 : if (ret != 0) {
561 : /* LCOV_EXCL_START */
562 : goto bail;
563 : /* LCOV_EXCL_STOP */
564 : }
565 :
566 : /* scan test with broken data and parity */
567 15 : for (i = 0; i < np / 2; ++i) {
568 : /* bad data */
569 9 : ir[i] = i;
570 : }
571 12 : for (i = 0; i < (np - 1) / 2; ++i) {
572 : /* bad parity */
573 6 : ir[np / 2 + i] = nd + i;
574 : }
575 21 : for (i = 0; i < np - 1; ++i) {
576 : /*
577 : * Make blocks bad
578 : * we cannot fill them with 0, because the original
579 : * data may be already filled with 0
580 : */
581 15 : memset(v[ir[i]], 0x55, size);
582 : }
583 :
584 6 : ret = raid_test_scan(np - 1, ir, nd, np, size, v, ref);
585 6 : if (ret != 0) {
586 : /* LCOV_EXCL_START */
587 : goto bail;
588 : /* LCOV_EXCL_STOP */
589 : }
590 : }
591 :
592 : /* scan test with no parity */
593 1 : ret = raid_test_scan(0, 0, nd, 0, size, v, ref);
594 1 : if (ret != -1) {
595 : /* LCOV_EXCL_START */
596 : goto bail;
597 : /* LCOV_EXCL_STOP */
598 : }
599 :
600 1 : ret = 0;
601 :
602 1 : bail:
603 1 : free(v);
604 1 : free(v_alloc);
605 :
606 1 : return ret;
607 : }
608 :
609 : #ifdef __KERNEL__ /* to build the user mode test */
610 : static int speedtest = 1;
611 :
612 : static int __init raid_cauchy_init(void)
613 : {
614 : int ret;
615 :
616 : raid_init();
617 :
618 : pr_info("raid: Using xor_blocks\n");
619 : #ifdef RAID_USE_RAID6_PQ
620 : pr_info("raid: Using raid6\n");
621 : #endif
622 :
623 : ret = raid_selftest();
624 : if (ret != 0)
625 : return ret;
626 :
627 : pr_info("raid: Self test passed\n");
628 :
629 : if (speedtest) {
630 : pr_info("raid: Speed test\n");
631 : raid_speedtest(0);
632 : pr_info("raid: Speed test with optimized memory layout\n");
633 : raid_speedtest(64); /* 64 is the typical cache line size */
634 : }
635 :
636 : return 0;
637 : }
638 :
639 : static void raid_cauchy_exit(void)
640 : {
641 : }
642 :
643 : subsys_initcall(raid_cauchy_init);
644 : module_exit(raid_cauchy_exit);
645 : module_param(speedtest, int, 0);
646 : MODULE_PARM_DESC(speedtest, "Runs a startup speed test");
647 : MODULE_AUTHOR("Andrea Mazzoleni <amadvance@gmail.com>");
648 : MODULE_LICENSE("GPL");
649 : MODULE_DESCRIPTION("RAID Cauchy functions");
650 : #endif
651 :
|