Line data Source code
1 : /*
2 : * Copyright (C) 2013 Andrea Mazzoleni
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : */
14 :
15 : #include "internal.h"
16 : #include "memory.h"
17 : #include "cpu.h"
18 :
19 : /*
20 : * Initializes and selects the best algorithm.
21 : */
22 240 : void raid_init(void)
23 : {
24 240 : raid_gen3_ptr = raid_gen3_int8;
25 240 : raid_gen_ptr[3] = raid_gen4_int8;
26 240 : raid_gen_ptr[4] = raid_gen5_int8;
27 240 : raid_gen_ptr[5] = raid_gen6_int8;
28 :
29 : if (sizeof(void *) == 4) {
30 240 : raid_gen_ptr[0] = raid_gen1_int32;
31 240 : raid_gen_ptr[1] = raid_gen2_int32;
32 240 : raid_genz_ptr = raid_genz_int32;
33 : } else {
34 : raid_gen_ptr[0] = raid_gen1_int64;
35 : raid_gen_ptr[1] = raid_gen2_int64;
36 : raid_genz_ptr = raid_genz_int64;
37 : }
38 :
39 240 : raid_rec_ptr[0] = raid_rec1_int8;
40 240 : raid_rec_ptr[1] = raid_rec2_int8;
41 240 : raid_rec_ptr[2] = raid_recX_int8;
42 240 : raid_rec_ptr[3] = raid_recX_int8;
43 240 : raid_rec_ptr[4] = raid_recX_int8;
44 240 : raid_rec_ptr[5] = raid_recX_int8;
45 :
46 : #ifdef CONFIG_X86
47 : #ifdef CONFIG_SSE2
48 240 : if (raid_cpu_has_sse2()) {
49 240 : raid_gen_ptr[0] = raid_gen1_sse2;
50 : #ifdef CONFIG_X86_64
51 : if (raid_cpu_has_slowextendedreg()) {
52 : raid_gen_ptr[1] = raid_gen2_sse2;
53 : raid_genz_ptr = raid_genz_sse2;
54 : } else {
55 : raid_gen_ptr[1] = raid_gen2_sse2ext;
56 : raid_genz_ptr = raid_genz_sse2ext;
57 : }
58 : #else
59 240 : raid_gen_ptr[1] = raid_gen2_sse2;
60 240 : raid_genz_ptr = raid_genz_sse2;
61 : #endif
62 : }
63 : #endif
64 :
65 : #ifdef CONFIG_SSSE3
66 240 : if (raid_cpu_has_ssse3()) {
67 : #ifdef CONFIG_X86_64
68 : raid_gen3_ptr = raid_gen3_ssse3ext;
69 : raid_gen_ptr[3] = raid_gen4_ssse3ext;
70 : raid_gen_ptr[4] = raid_gen5_ssse3ext;
71 : raid_gen_ptr[5] = raid_gen6_ssse3ext;
72 : #else
73 239 : raid_gen3_ptr = raid_gen3_ssse3;
74 239 : raid_gen_ptr[3] = raid_gen4_ssse3;
75 239 : raid_gen_ptr[4] = raid_gen5_ssse3;
76 239 : raid_gen_ptr[5] = raid_gen6_ssse3;
77 : #endif
78 239 : raid_rec_ptr[0] = raid_rec1_ssse3;
79 239 : raid_rec_ptr[1] = raid_rec2_ssse3;
80 239 : raid_rec_ptr[2] = raid_recX_ssse3;
81 239 : raid_rec_ptr[3] = raid_recX_ssse3;
82 239 : raid_rec_ptr[4] = raid_recX_ssse3;
83 239 : raid_rec_ptr[5] = raid_recX_ssse3;
84 : }
85 : #endif
86 :
87 : #ifdef CONFIG_AVX2
88 240 : if (raid_cpu_has_avx2()) {
89 2 : raid_gen_ptr[0] = raid_gen1_avx2;
90 2 : raid_gen_ptr[1] = raid_gen2_avx2;
91 : #ifdef CONFIG_X86_64
92 : raid_gen3_ptr = raid_gen3_avx2ext;
93 : raid_genz_ptr = raid_genz_avx2ext;
94 : raid_gen_ptr[3] = raid_gen4_avx2ext;
95 : raid_gen_ptr[4] = raid_gen5_avx2ext;
96 : raid_gen_ptr[5] = raid_gen6_avx2ext;
97 : #endif
98 2 : raid_rec_ptr[0] = raid_rec1_avx2;
99 2 : raid_rec_ptr[1] = raid_rec2_avx2;
100 2 : raid_rec_ptr[2] = raid_recX_avx2;
101 2 : raid_rec_ptr[3] = raid_recX_avx2;
102 2 : raid_rec_ptr[4] = raid_recX_avx2;
103 2 : raid_rec_ptr[5] = raid_recX_avx2;
104 : }
105 : #endif
106 : #endif /* CONFIG_X86 */
107 :
108 : /* set the default mode */
109 240 : raid_mode(RAID_MODE_CAUCHY);
110 240 : }
111 :
112 : /*
113 : * Reference parity computation.
114 : */
115 6 : void raid_gen_ref(int nd, int np, size_t size, void **vv)
116 : {
117 6 : uint8_t **v = (uint8_t **)vv;
118 : size_t i;
119 :
120 5382 : for (i = 0; i < size; ++i) {
121 : uint8_t p[RAID_PARITY_MAX];
122 : int j, d;
123 :
124 36096 : for (j = 0; j < np; ++j)
125 30720 : p[j] = 0;
126 :
127 93696 : for (d = 0; d < nd; ++d) {
128 88320 : uint8_t b = v[d][i];
129 :
130 584448 : for (j = 0; j < np; ++j)
131 496128 : p[j] ^= gfmul[b][gfgen[j][d]];
132 : }
133 :
134 36096 : for (j = 0; j < np; ++j)
135 30720 : v[nd + j][i] = p[j];
136 : }
137 6 : }
138 :
139 : /*
140 : * Size of the blocks to test.
141 : */
142 : #define TEST_SIZE 4096
143 :
144 : /*
145 : * Number of data blocks to test.
146 : */
147 : #define TEST_COUNT (65536 / TEST_SIZE)
148 :
149 : /*
150 : * Parity generation test.
151 : */
152 6 : static int raid_test_par(int nd, int np, size_t size, void **v, void **ref)
153 : {
154 : int i;
155 : void *t[TEST_COUNT + RAID_PARITY_MAX];
156 :
157 : /* setup data */
158 102 : for (i = 0; i < nd; ++i)
159 96 : t[i] = ref[i];
160 :
161 : /* setup parity */
162 27 : for (i = 0; i < np; ++i)
163 21 : t[nd + i] = v[nd + i];
164 :
165 6 : raid_gen(nd, np, size, t);
166 :
167 : /* compare parity */
168 27 : for (i = 0; i < np; ++i) {
169 21 : if (memcmp(t[nd + i], ref[nd + i], size) != 0) {
170 : /* LCOV_EXCL_START */
171 : return -1;
172 : /* LCOV_EXCL_STOP */
173 : }
174 : }
175 :
176 6 : return 0;
177 : }
178 :
179 : /*
180 : * Recovering test.
181 : */
182 18 : static int raid_test_rec(int nr, int *ir, int nd, int np, size_t size, void **v, void **ref)
183 : {
184 : int i, j;
185 : void *t[TEST_COUNT + RAID_PARITY_MAX];
186 :
187 : /* setup data and parity vector */
188 369 : for (i = 0, j = 0; i < nd + np; ++i) {
189 351 : if (j < nr && ir[j] == i) {
190 : /* this block has to be recovered */
191 63 : t[i] = v[i];
192 63 : ++j;
193 : } else {
194 : /* this block is used for recovering */
195 288 : t[i] = ref[i];
196 : }
197 : }
198 :
199 18 : raid_rec(nr, ir, nd, np, size, t);
200 :
201 : /* compare all data and parity */
202 369 : for (i = 0; i < nd + np; ++i) {
203 351 : if (t[i] != ref[i]
204 63 : && memcmp(t[i], ref[i], size) != 0) {
205 : /* LCOV_EXCL_START */
206 : return -1;
207 : /* LCOV_EXCL_STOP */
208 : }
209 : }
210 :
211 18 : return 0;
212 : }
213 :
214 : /*
215 : * Recovering test for data.
216 : */
217 18 : static int raid_test_data(int nr, int *id, int *ip, int nd, int np, size_t size, void **v, void **ref)
218 : {
219 : int i, j;
220 : void *t[TEST_COUNT + RAID_PARITY_MAX];
221 :
222 : /* setup data vector */
223 306 : for (i = 0, j = 0; i < nd; ++i) {
224 288 : if (j < nr && id[j] == i) {
225 : /* this block has to be recovered */
226 39 : t[i] = v[i];
227 39 : ++j;
228 : } else {
229 : /* this block is left unchanged */
230 249 : t[i] = ref[i];
231 : }
232 : }
233 :
234 : /* setup parity vector */
235 81 : for (i = 0, j = 0; i < np; ++i) {
236 63 : if (j < nr && ip[j] == i) {
237 : /* this block is used for recovering */
238 39 : t[nd + i] = ref[nd + i];
239 39 : ++j;
240 : } else {
241 : /* this block should not be read or written */
242 24 : t[nd + i] = 0;
243 : }
244 : }
245 :
246 18 : raid_data(nr, id, ip, nd, size, t);
247 :
248 : /* compare all data and parity */
249 306 : for (i = 0; i < nd; ++i) {
250 288 : if (t[i] != ref[i]
251 39 : && t[i] != 0
252 39 : && memcmp(t[i], ref[i], size) != 0) {
253 : /* LCOV_EXCL_START */
254 : return -1;
255 : /* LCOV_EXCL_STOP */
256 : }
257 : }
258 :
259 18 : return 0;
260 : }
261 :
262 : /*
263 : * Scan test.
264 : */
265 7 : static int raid_test_scan(int nr, int *ir, int nd, int np, size_t size, void **v, void **ref)
266 : {
267 : int i, j, ret;
268 : void *t[TEST_COUNT + RAID_PARITY_MAX];
269 : int is[RAID_PARITY_MAX];
270 :
271 : /* setup data and parity vector */
272 140 : for (i = 0, j = 0; i < nd + np; ++i) {
273 133 : if (j < nr && ir[j] == i) {
274 : /* this block is bad */
275 15 : t[i] = v[i];
276 15 : ++j;
277 : } else {
278 : /* this block is used for recovering */
279 118 : t[i] = ref[i];
280 : }
281 : }
282 :
283 7 : ret = raid_scan(is, nd, np, size, t);
284 :
285 : /* compare identified bad blocks */
286 7 : if (ret != nr)
287 1 : return -1;
288 21 : for (i = 0; i < nr; ++i) {
289 15 : if (ir[i] != is[i]) {
290 : /* LCOV_EXCL_START */
291 : return -1;
292 : /* LCOV_EXCL_STOP */
293 : }
294 : }
295 :
296 6 : return 0;
297 : }
298 :
299 : /*
300 : * Basic functionality self test.
301 : */
302 1 : int raid_selftest(void)
303 : {
304 1 : const int nd = TEST_COUNT;
305 1 : const size_t size = TEST_SIZE;
306 1 : const int nv = nd + RAID_PARITY_MAX * 2 + 1;
307 : void *v_alloc;
308 : void **v;
309 1 : void *ref[nd + RAID_PARITY_MAX];
310 : int ir[RAID_PARITY_MAX];
311 : int ip[RAID_PARITY_MAX];
312 : int i, np;
313 1 : int ret = 0;
314 :
315 : /* ensure to have enough space for data */
316 1 : BUG_ON(nd * size > 65536);
317 :
318 1 : v = raid_malloc_vector(nd, nv, size, &v_alloc);
319 1 : if (!v) {
320 : /* LCOV_EXCL_START */
321 : return -1;
322 : /* LCOV_EXCL_STOP */
323 : }
324 :
325 1 : memset(v[nv - 1], 0, size);
326 1 : raid_zero(v[nv - 1]);
327 :
328 : /* use the multiplication table as data */
329 17 : for (i = 0; i < nd; ++i)
330 16 : ref[i] = ((uint8_t *)gfmul) + size * i;
331 :
332 : /* setup reference parity */
333 7 : for (i = 0; i < RAID_PARITY_MAX; ++i)
334 6 : ref[nd + i] = v[nd + RAID_PARITY_MAX + i];
335 :
336 : /* compute reference parity */
337 1 : raid_gen_ref(nd, RAID_PARITY_MAX, size, ref);
338 :
339 : /* test for each parity level */
340 7 : for (np = 1; np <= RAID_PARITY_MAX; ++np) {
341 : /* test parity generation */
342 6 : ret = raid_test_par(nd, np, size, v, ref);
343 6 : if (ret != 0) {
344 : /* LCOV_EXCL_START */
345 : goto bail;
346 : /* LCOV_EXCL_STOP */
347 : }
348 :
349 : /* test recovering with broken ending data disks */
350 27 : for (i = 0; i < np; ++i) {
351 : /* bad data */
352 21 : ir[i] = nd - np + i;
353 :
354 : /* good parity */
355 21 : ip[i] = i;
356 : }
357 :
358 6 : ret = raid_test_rec(np, ir, nd, np, size, v, ref);
359 6 : if (ret != 0) {
360 : /* LCOV_EXCL_START */
361 : goto bail;
362 : /* LCOV_EXCL_STOP */
363 : }
364 :
365 6 : ret = raid_test_data(np, ir, ip, nd, np, size, v, ref);
366 6 : if (ret != 0) {
367 : /* LCOV_EXCL_START */
368 : goto bail;
369 : /* LCOV_EXCL_STOP */
370 : }
371 :
372 : /* test recovering with broken leading data and broken leading parity */
373 15 : for (i = 0; i < np / 2; ++i) {
374 : /* bad data */
375 9 : ir[i] = i;
376 :
377 : /* good parity */
378 9 : ip[i] = (np + 1) / 2 + i;
379 : }
380 :
381 : /* bad parity */
382 18 : for (i = 0; i < (np + 1) / 2; ++i)
383 12 : ir[np / 2 + i] = nd + i;
384 :
385 6 : ret = raid_test_rec(np, ir, nd, np, size, v, ref);
386 6 : if (ret != 0) {
387 : /* LCOV_EXCL_START */
388 : goto bail;
389 : /* LCOV_EXCL_STOP */
390 : }
391 :
392 6 : ret = raid_test_data(np / 2, ir, ip, nd, np, size, v, ref);
393 6 : if (ret != 0) {
394 : /* LCOV_EXCL_START */
395 : goto bail;
396 : /* LCOV_EXCL_STOP */
397 : }
398 :
399 : /* test recovering with broken leading data and broken ending parity */
400 15 : for (i = 0; i < np / 2; ++i) {
401 : /* bad data */
402 9 : ir[i] = i;
403 :
404 : /* good parity */
405 9 : ip[i] = i;
406 : }
407 :
408 : /* bad parity */
409 18 : for (i = 0; i < (np + 1) / 2; ++i)
410 12 : ir[np / 2 + i] = nd + np - (np + 1) / 2 + i;
411 :
412 6 : ret = raid_test_rec(np, ir, nd, np, size, v, ref);
413 6 : if (ret != 0) {
414 : /* LCOV_EXCL_START */
415 : goto bail;
416 : /* LCOV_EXCL_STOP */
417 : }
418 :
419 6 : ret = raid_test_data(np / 2, ir, ip, nd, np, size, v, ref);
420 6 : if (ret != 0) {
421 : /* LCOV_EXCL_START */
422 : goto bail;
423 : /* LCOV_EXCL_STOP */
424 : }
425 :
426 : /* scan test with broken data and parity */
427 15 : for (i = 0; i < np / 2; ++i) {
428 : /* bad data */
429 9 : ir[i] = i;
430 : }
431 12 : for (i = 0; i < (np - 1) / 2; ++i) {
432 : /* bad parity */
433 6 : ir[np / 2 + i] = nd + i;
434 : }
435 21 : for (i = 0; i < np - 1; ++i) {
436 : /* make blocks bad */
437 : /* we cannot fill them with 0, because the original */
438 : /* data may be already filled with 0 */
439 15 : memset(v[ir[i]], 0x55, size);
440 : }
441 :
442 6 : ret = raid_test_scan(np - 1, ir, nd, np, size, v, ref);
443 6 : if (ret != 0) {
444 : /* LCOV_EXCL_START */
445 : goto bail;
446 : /* LCOV_EXCL_STOP */
447 : }
448 : }
449 :
450 : /* scan test with no parity */
451 1 : ret = raid_test_scan(0, 0, nd, 0, size, v, ref);
452 1 : if (ret != -1) {
453 : /* LCOV_EXCL_START */
454 : goto bail;
455 : /* LCOV_EXCL_STOP */
456 : }
457 :
458 1 : ret = 0;
459 :
460 : bail:
461 1 : free(v);
462 1 : free(v_alloc);
463 :
464 1 : return ret;
465 : }
466 :
|