]> git.ipfire.org Git - thirdparty/mdadm.git/blame - restripe.c
restripe: support saving when not all devices are present.
[thirdparty/mdadm.git] / restripe.c
CommitLineData
e86c9dd6
NB
1/*
2 * mdadm - manage Linux "md" devices aka RAID arrays.
3 *
4 * Copyright (C) 2006 Neil Brown <neilb@suse.de>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Author: Neil Brown
22 * Email: <neilb@suse.de>
23 */
24
25#include "mdadm.h"
a6288483 26#include <stdint.h>
e86c9dd6
NB
27
28/* To restripe, we read from old geometry to a buffer, and
29 * read from buffer to new geometry.
a6288483
N
30 * When reading, we might have missing devices and so could need
31 * to reconstruct.
32 * When writing, we need to create correct parity and Q.
e86c9dd6
NB
33 *
34 */
35
e0d95aac
N
36static int geo_map(int block, unsigned long long stripe, int raid_disks,
37 int level, int layout)
e86c9dd6 38{
48327135 39 /* On the given stripe, find which disk in the array will have
e86c9dd6 40 * block numbered 'block'.
48327135
NB
41 * '-1' means the parity block.
42 * '-2' means the Q syndrome.
e86c9dd6
NB
43 */
44 int pd;
45
46 switch(level*100 + layout) {
47 case 000:
48 case 400:
e0d95aac 49 case 500 + ALGORITHM_PARITY_N:
e86c9dd6
NB
50 /* raid 4 isn't messed around by parity blocks */
51 if (block == -1)
52 return raid_disks-1; /* parity block */
53 return block;
54 case 500 + ALGORITHM_LEFT_ASYMMETRIC:
55 pd = (raid_disks-1) - stripe % raid_disks;
56 if (block == -1) return pd;
57 if (block >= pd)
58 block++;
59 return block;
60
61 case 500 + ALGORITHM_RIGHT_ASYMMETRIC:
62 pd = stripe % raid_disks;
63 if (block == -1) return pd;
64 if (block >= pd)
65 block++;
66 return block;
67
68 case 500 + ALGORITHM_LEFT_SYMMETRIC:
69 pd = (raid_disks - 1) - stripe % raid_disks;
70 if (block == -1) return pd;
71 return (pd + 1 + block) % raid_disks;
72
73 case 500 + ALGORITHM_RIGHT_SYMMETRIC:
74 pd = stripe % raid_disks;
75 if (block == -1) return pd;
76 return (pd + 1 + block) % raid_disks;
77
e0d95aac
N
78 case 500 + ALGORITHM_PARITY_0:
79 return block + 1;
80
81
82 case 600 + ALGORITHM_PARITY_N_6:
83 if (block == -2)
84 return raid_disks - 1;
85 if (block == -1)
86 return raid_disks - 2; /* parity block */
87 return block;
88 case 600 + ALGORITHM_LEFT_ASYMMETRIC_6:
89 if (block == -2)
90 return raid_disks - 1;
91 raid_disks--;
92 pd = (raid_disks-1) - stripe % raid_disks;
93 if (block == -1) return pd;
94 if (block >= pd)
95 block++;
96 return block;
97
98 case 600 + ALGORITHM_RIGHT_ASYMMETRIC_6:
99 if (block == -2)
100 return raid_disks - 1;
101 raid_disks--;
102 pd = stripe % raid_disks;
103 if (block == -1) return pd;
104 if (block >= pd)
105 block++;
106 return block;
107
108 case 600 + ALGORITHM_LEFT_SYMMETRIC_6:
109 if (block == -2)
110 return raid_disks - 1;
111 raid_disks--;
112 pd = (raid_disks - 1) - stripe % raid_disks;
113 if (block == -1) return pd;
114 return (pd + 1 + block) % raid_disks;
115
116 case 600 + ALGORITHM_RIGHT_SYMMETRIC_6:
117 if (block == -2)
118 return raid_disks - 1;
119 raid_disks--;
120 pd = stripe % raid_disks;
121 if (block == -1) return pd;
122 return (pd + 1 + block) % raid_disks;
123
124 case 600 + ALGORITHM_PARITY_0_6:
125 if (block == -2)
126 return raid_disks - 1;
127 return block + 1;
128
129
130 case 600 + ALGORITHM_PARITY_0:
131 if (block == -1)
132 return 0;
133 if (block == -2)
134 return 1;
135 return block + 2;
136
e86c9dd6
NB
137 case 600 + ALGORITHM_LEFT_ASYMMETRIC:
138 pd = raid_disks - 1 - (stripe % raid_disks);
139 if (block == -1) return pd;
48327135 140 if (block == -2) return (pd+1) % raid_disks;
e86c9dd6
NB
141 if (pd == raid_disks - 1)
142 return block+1;
143 if (block >= pd)
144 return block+2;
145 return block;
146
e0d95aac
N
147 case 600 + ALGORITHM_ROTATING_ZERO_RESTART:
148 /* Different order for calculating Q, otherwize same as ... */
e86c9dd6
NB
149 case 600 + ALGORITHM_RIGHT_ASYMMETRIC:
150 pd = stripe % raid_disks;
151 if (block == -1) return pd;
48327135 152 if (block == -2) return (pd+1) % raid_disks;
e86c9dd6
NB
153 if (pd == raid_disks - 1)
154 return block+1;
155 if (block >= pd)
156 return block+2;
157 return block;
158
159 case 600 + ALGORITHM_LEFT_SYMMETRIC:
160 pd = raid_disks - 1 - (stripe % raid_disks);
161 if (block == -1) return pd;
48327135 162 if (block == -2) return (pd+1) % raid_disks;
e86c9dd6
NB
163 return (pd + 2 + block) % raid_disks;
164
165 case 600 + ALGORITHM_RIGHT_SYMMETRIC:
166 pd = stripe % raid_disks;
167 if (block == -1) return pd;
48327135 168 if (block == -2) return (pd+1) % raid_disks;
e86c9dd6 169 return (pd + 2 + block) % raid_disks;
e0d95aac
N
170
171
172 case 600 + ALGORITHM_ROTATING_N_RESTART:
173 /* Same a left_asymmetric, by first stripe is
174 * D D D P Q rather than
175 * Q D D D P
176 */
177 pd = raid_disks - 1 - ((stripe + 1) % raid_disks);
178 if (block == -1) return pd;
179 if (block == -2) return (pd+1) % raid_disks;
180 if (pd == raid_disks - 1)
181 return block+1;
182 if (block >= pd)
183 return block+2;
184 return block;
185
186 case 600 + ALGORITHM_ROTATING_N_CONTINUE:
187 /* Same as left_symmetric but Q is before P */
188 pd = raid_disks - 1 - (stripe % raid_disks);
189 if (block == -1) return pd;
190 if (block == -2) return (pd+raid_disks-1) % raid_disks;
191 return (pd + 1 + block) % raid_disks;
e86c9dd6
NB
192 }
193 return -1;
194}
e0d95aac
N
195static int is_ddf(int layout)
196{
197 switch (layout)
198 {
199 default:
200 return 0;
201 case ALGORITHM_ROTATING_N_CONTINUE:
202 case ALGORITHM_ROTATING_N_RESTART:
203 case ALGORITHM_ROTATING_ZERO_RESTART:
204 return 1;
205 }
206}
e86c9dd6
NB
207
208
209static void xor_blocks(char *target, char **sources, int disks, int size)
210{
211 int i, j;
212 /* Amazingly inefficient... */
213 for (i=0; i<size; i++) {
214 char c = 0;
215 for (j=0 ; j<disks; j++)
216 c ^= sources[j][i];
217 target[i] = c;
218 }
219}
220
a6288483 221static void qsyndrome(uint8_t *p, uint8_t *q, uint8_t **sources, int disks, int size)
48327135
NB
222{
223 int d, z;
a6288483 224 uint8_t wq0, wp0, wd0, w10, w20;
48327135
NB
225 for ( d = 0; d < size; d++) {
226 wq0 = wp0 = sources[disks-1][d];
227 for ( z = disks-2 ; z >= 0 ; z-- ) {
228 wd0 = sources[z][d];
229 wp0 ^= wd0;
230 w20 = (wq0&0x80) ? 0xff : 0x00;
231 w10 = (wq0 << 1) & 0xff;
232 w20 &= 0x1d;
233 w10 ^= w20;
234 wq0 = w10 ^ wd0;
235 }
236 p[d] = wp0;
237 q[d] = wq0;
238 }
239}
240
a6288483
N
241
242/*
243 * The following was taken from linux/drivers/md/mktables.c, and modified
244 * to create in-memory tables rather than C code
245 */
246static uint8_t gfmul(uint8_t a, uint8_t b)
247{
248 uint8_t v = 0;
249
250 while (b) {
251 if (b & 1)
252 v ^= a;
253 a = (a << 1) ^ (a & 0x80 ? 0x1d : 0);
254 b >>= 1;
255 }
256
257 return v;
258}
259
260static uint8_t gfpow(uint8_t a, int b)
261{
262 uint8_t v = 1;
263
264 b %= 255;
265 if (b < 0)
266 b += 255;
267
268 while (b) {
269 if (b & 1)
270 v = gfmul(v, a);
271 a = gfmul(a, a);
272 b >>= 1;
273 }
274
275 return v;
276}
277
278int tables_ready = 0;
279uint8_t raid6_gfmul[256][256];
280uint8_t raid6_gfexp[256];
281uint8_t raid6_gfinv[256];
282uint8_t raid6_gfexi[256];
283void make_tables(void)
284{
285 int i, j;
286 uint8_t v;
287
288 /* Compute multiplication table */
289 for (i = 0; i < 256; i++)
290 for (j = 0; j < 256; j++)
291 raid6_gfmul[i][j] = gfmul(i, j);
292
293 /* Compute power-of-2 table (exponent) */
294 v = 1;
295 for (i = 0; i < 256; i++) {
296 raid6_gfexp[i] = v;
297 v = gfmul(v, 2);
298 if (v == 1)
299 v = 0; /* For entry 255, not a real entry */
300 }
301
302 /* Compute inverse table x^-1 == x^254 */
303 for (i = 0; i < 256; i++)
304 raid6_gfinv[i] = gfpow(i, 254);
305
306 /* Compute inv(2^x + 1) (exponent-xor-inverse) table */
307 for (i = 0; i < 256; i ++)
308 raid6_gfexi[i] = raid6_gfinv[raid6_gfexp[i] ^ 1];
309
310 tables_ready = 1;
311}
312
313uint8_t *zero;
314/* Following was taken from linux/drivers/md/raid6recov.c */
315
316/* Recover two failed data blocks. */
317void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
318 uint8_t **ptrs)
319{
320 uint8_t *p, *q, *dp, *dq;
321 uint8_t px, qx, db;
322 const uint8_t *pbmul; /* P multiplier table for B data */
323 const uint8_t *qmul; /* Q multiplier table (for both) */
324
325 p = ptrs[disks-2];
326 q = ptrs[disks-1];
327
328 /* Compute syndrome with zero for the missing data pages
329 Use the dead data pages as temporary storage for
330 delta p and delta q */
331 dp = ptrs[faila];
332 ptrs[faila] = zero;
333 dq = ptrs[failb];
334 ptrs[failb] = zero;
335
336 qsyndrome(dp, dq, ptrs, disks-2, bytes);
337
338 /* Restore pointer table */
339 ptrs[faila] = dp;
340 ptrs[failb] = dq;
341
342 /* Now, pick the proper data tables */
343 pbmul = raid6_gfmul[raid6_gfexi[failb-faila]];
344 qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]];
345
346 /* Now do it... */
347 while ( bytes-- ) {
348 px = *p ^ *dp;
349 qx = qmul[*q ^ *dq];
350 *dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */
351 *dp++ = db ^ px; /* Reconstructed A */
352 p++; q++;
353 }
354}
355
356/* Recover failure of one data block plus the P block */
357void raid6_datap_recov(int disks, size_t bytes, int faila, uint8_t **ptrs)
358{
359 uint8_t *p, *q, *dq;
360 const uint8_t *qmul; /* Q multiplier table */
361
362 p = ptrs[disks-2];
363 q = ptrs[disks-1];
364
365 /* Compute syndrome with zero for the missing data page
366 Use the dead data page as temporary storage for delta q */
367 dq = ptrs[faila];
368 ptrs[faila] = zero;
369
370 qsyndrome(p, dq, ptrs, disks-2, bytes);
371
372 /* Restore pointer table */
373 ptrs[faila] = dq;
374
375 /* Now, pick the proper data tables */
376 qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]];
377
378 /* Now do it... */
379 while ( bytes-- ) {
380 *p++ ^= *dq = qmul[*q ^ *dq];
381 q++; dq++;
382 }
383}
384
e86c9dd6
NB
385/* Save data:
386 * We are given:
a6288483 387 * A list of 'fds' of the active disks. Some may be absent.
48327135 388 * A geometry: raid_disks, chunk_size, level, layout
e86c9dd6
NB
389 * A list of 'fds' for mirrored targets. They are already seeked to
390 * right (Write) location
a6288483
N
391 * A start and length which must be stripe-aligned
392 * 'buf' is large enough to hold one stripe, and is aligned
e86c9dd6
NB
393 */
394
395int save_stripes(int *source, unsigned long long *offsets,
396 int raid_disks, int chunk_size, int level, int layout,
397 int nwrites, int *dest,
a6288483
N
398 unsigned long long start, unsigned long long length,
399 char *buf)
e86c9dd6 400{
e86c9dd6
NB
401 int len;
402 int data_disks = raid_disks - (level == 0 ? 0 : level <=5 ? 1 : 2);
403 int disk;
a6288483 404 int i;
e86c9dd6 405
a6288483
N
406 if (!tables_ready)
407 make_tables();
408
409 if (zero == NULL) {
410 zero = malloc(chunk_size);
411 memset(zero, 0, chunk_size);
412 }
413
414 len = data_disks * chunk_size;
e86c9dd6 415 while (length > 0) {
a6288483
N
416 int failed = 0;
417 int fdisk[3], fblock[3];
418 for (disk = 0; disk < raid_disks ; disk++) {
419 unsigned long long offset;
420 int dnum;
421 len = chunk_size;
422
423 offset = (start/chunk_size/data_disks)*chunk_size;
424 dnum = geo_map(disk < data_disks ? disk : data_disks - disk - 1,
425 start/chunk_size/data_disks,
426 raid_disks, level, layout);
427 if (source[dnum] < 0 ||
428 lseek64(source[dnum], offsets[disk]+offset, 0) < 0 ||
429 read(source[dnum], buf+disk * chunk_size, len) != len)
430 if (failed <= 2) {
431 fdisk[failed] = dnum;
432 fblock[failed] = disk;
433 failed++;
434 }
435 }
436 if (failed == 0 || fblock[0] >= data_disks)
437 /* all data disks are good */
438 ;
439 else if (failed == 1 || fblock[1] >= data_disks+1) {
440 /* one failed data disk and good parity */
441 char *bufs[data_disks];
442 for (i=0; i < data_disks; i++)
443 if (fblock[0] == i)
444 bufs[i] = buf + data_disks*chunk_size;
445 else
446 bufs[i] = buf + i*chunk_size;
447
448 xor_blocks(buf + fblock[0]*chunk_size,
449 bufs, data_disks, chunk_size);
450 } else if (failed > 2 || level != 6)
451 /* too much failure */
e86c9dd6 452 return -1;
a6288483
N
453 else {
454 /* RAID6 computations needed. */
455 uint8_t *bufs[data_disks+4];
456 int qdisk;
457 int syndrome_disks;
458 disk = geo_map(-1, start/chunk_size/data_disks,
459 raid_disks, level, layout);
460 qdisk = geo_map(-2, start/chunk_size/data_disks,
461 raid_disks, level, layout);
462 if (is_ddf(layout)) {
463 /* q over 'raid_disks' blocks, in device order.
464 * 'p' and 'q' get to be all zero
465 */
466 for (i = 0; i < raid_disks; i++)
467 if (i == disk || i == qdisk)
468 bufs[i] = zero;
469 else
470 bufs[i] = (uint8_t*)buf+i*chunk_size;
471 syndrome_disks = raid_disks;
472 } else {
473 /* for md, q is over 'data_disks' blocks,
474 * starting immediately after 'q'
475 */
476 for (i = 0; i < data_disks; i++)
477 bufs[i] = (uint8_t*)buf + chunk_size * ((qdisk+1+i) % raid_disks);
478
479 fdisk[0] = (qdisk + 1 + fdisk[0]) * raid_disks;
480 fdisk[1] = (qdisk + 1 + fdisk[1]) * raid_disks;
481 syndrome_disks = data_disks;
482 }
483 bufs[syndrome_disks] = (uint8_t*)buf + chunk_size * disk;
484 bufs[syndrome_disks+1] = (uint8_t*)buf + chunk_size * qdisk;
485 if (fblock[1] == data_disks)
486 /* One data failed, and parity failed */
487 raid6_datap_recov(syndrome_disks+2, chunk_size,
488 fdisk[0], bufs);
489 else
490 /* Two data blocks failed, P,Q OK */
491 raid6_2data_recov(syndrome_disks+2, chunk_size,
492 fdisk[0], fdisk[1], bufs);
493 }
494
e86c9dd6
NB
495 for (i=0; i<nwrites; i++)
496 if (write(dest[i], buf, len) != len)
497 return -1;
a6288483 498
e86c9dd6
NB
499 length -= len;
500 start += len;
e86c9dd6
NB
501 }
502 return 0;
503}
504
505/* Restore data:
506 * We are given:
507 * A list of 'fds' of the active disks. Some may be '-1' for not-available.
353632d9 508 * A geometry: raid_disks, chunk_size, level, layout
e86c9dd6
NB
509 * An 'fd' to read from. It is already seeked to the right (Read) location.
510 * A start and length.
511 * The length must be a multiple of the stripe size.
512 *
513 * We build a full stripe in memory and then write it out.
514 * We assume that there are enough working devices.
515 */
516int restore_stripes(int *dest, unsigned long long *offsets,
517 int raid_disks, int chunk_size, int level, int layout,
353632d9 518 int source, unsigned long long read_offset,
e86c9dd6
NB
519 unsigned long long start, unsigned long long length)
520{
521 char *stripe_buf = malloc(raid_disks * chunk_size);
522 char **stripes = malloc(raid_disks * sizeof(char*));
523 char **blocks = malloc(raid_disks * sizeof(char*));
524 int i;
525
a6288483 526 int data_disks = raid_disks - (level == 0 ? 0 : level <= 5 ? 1 : 2);
e86c9dd6 527
a6288483
N
528 if (zero == NULL) {
529 zero = malloc(chunk_size);
530 if (zero)
531 memset(zero, 0, chunk_size);
532 }
e0d95aac
N
533 if (stripe_buf == NULL || stripes == NULL || blocks == NULL
534 || zero == NULL) {
e86c9dd6
NB
535 free(stripe_buf);
536 free(stripes);
537 free(blocks);
e0d95aac 538 free(zero);
e86c9dd6
NB
539 return -2;
540 }
541 for (i=0; i<raid_disks; i++)
542 stripes[i] = stripe_buf + i * chunk_size;
543 while (length > 0) {
544 int len = data_disks * chunk_size;
545 unsigned long long offset;
48327135 546 int disk, qdisk;
a6288483 547 int syndrome_disks;
e86c9dd6
NB
548 if (length < len)
549 return -3;
550 for (i=0; i < data_disks; i++) {
551 int disk = geo_map(i, start/chunk_size/data_disks,
552 raid_disks, level, layout);
353632d9
NB
553 if (lseek64(source, read_offset, 0) != read_offset)
554 return -1;
e86c9dd6
NB
555 if (read(source, stripes[disk], chunk_size) != chunk_size)
556 return -1;
353632d9 557 read_offset += chunk_size;
e86c9dd6
NB
558 }
559 /* We have the data, now do the parity */
560 offset = (start/chunk_size/data_disks) * chunk_size;
48327135
NB
561 switch (level) {
562 case 4:
563 case 5:
564 disk = geo_map(-1, start/chunk_size/data_disks,
e86c9dd6 565 raid_disks, level, layout);
e0d95aac
N
566 for (i = 0; i < data_disks; i++)
567 blocks[i] = stripes[(disk+1+i) % raid_disks];
e86c9dd6 568 xor_blocks(stripes[disk], blocks, data_disks, chunk_size);
48327135
NB
569 break;
570 case 6:
571 disk = geo_map(-1, start/chunk_size/data_disks,
572 raid_disks, level, layout);
573 qdisk = geo_map(-2, start/chunk_size/data_disks,
574 raid_disks, level, layout);
e0d95aac
N
575 if (is_ddf(layout)) {
576 /* q over 'raid_disks' blocks, in device order.
577 * 'p' and 'q' get to be all zero
578 */
579 for (i = 0; i < raid_disks; i++)
580 if (i == disk || i == qdisk)
a6288483 581 blocks[i] = (char*)zero;
e0d95aac
N
582 else
583 blocks[i] = stripes[i];
a6288483 584 syndrome_disks = raid_disks;
e0d95aac 585 } else {
a6288483 586 /* for md, q is over 'data_disks' blocks,
e0d95aac
N
587 * starting immediately after 'q'
588 */
589 for (i = 0; i < data_disks; i++)
590 blocks[i] = stripes[(qdisk+1+i) % raid_disks];
48327135 591
a6288483 592 syndrome_disks = data_disks;
e0d95aac 593 }
a6288483
N
594 qsyndrome((uint8_t*)stripes[disk],
595 (uint8_t*)stripes[qdisk],
596 (uint8_t**)blocks,
597 syndrome_disks, chunk_size);
48327135 598 break;
e86c9dd6
NB
599 }
600 for (i=0; i < raid_disks ; i++)
601 if (dest[i] >= 0) {
602 if (lseek64(dest[i], offsets[i]+offset, 0) < 0)
603 return -1;
604 if (write(dest[i], stripes[i], chunk_size) != chunk_size)
605 return -1;
606 }
607 length -= len;
608 start += len;
609 }
610 return 0;
611}
612
613#ifdef MAIN
614
48327135
NB
615int test_stripes(int *source, unsigned long long *offsets,
616 int raid_disks, int chunk_size, int level, int layout,
617 unsigned long long start, unsigned long long length)
618{
619 /* ready the data and p (and q) blocks, and check we got them right */
620 char *stripe_buf = malloc(raid_disks * chunk_size);
621 char **stripes = malloc(raid_disks * sizeof(char*));
622 char **blocks = malloc(raid_disks * sizeof(char*));
623 char *p = malloc(chunk_size);
624 char *q = malloc(chunk_size);
625
626 int i;
627 int data_disks = raid_disks - (level == 5 ? 1: 2);
628 for ( i = 0 ; i < raid_disks ; i++)
629 stripes[i] = stripe_buf + i * chunk_size;
630
631 while (length > 0) {
632 int disk;
633
634 for (i = 0 ; i < raid_disks ; i++) {
635 lseek64(source[i], offsets[i]+start, 0);
636 read(source[i], stripes[i], chunk_size);
637 }
638 for (i = 0 ; i < data_disks ; i++) {
639 int disk = geo_map(i, start/chunk_size, raid_disks,
640 level, layout);
641 blocks[i] = stripes[disk];
642 printf("%d->%d\n", i, disk);
643 }
644 switch(level) {
645 case 6:
646 qsyndrome(p, q, blocks, data_disks, chunk_size);
647 disk = geo_map(-1, start/chunk_size, raid_disks,
648 level, layout);
649 if (memcmp(p, stripes[disk], chunk_size) != 0) {
650 printf("P(%d) wrong at %llu\n", disk,
651 start / chunk_size);
652 }
653 disk = geo_map(-2, start/chunk_size, raid_disks,
654 level, layout);
655 if (memcmp(q, stripes[disk], chunk_size) != 0) {
656 printf("Q(%d) wrong at %llu\n", disk,
657 start / chunk_size);
658 }
659 break;
660 }
661 length -= chunk_size;
662 start += chunk_size;
663 }
664 return 0;
665}
666
e86c9dd6
NB
667unsigned long long getnum(char *str, char **err)
668{
669 char *e;
670 unsigned long long rv = strtoull(str, &e, 10);
671 if (e==str || *e) {
672 *err = str;
673 return 0;
674 }
675 return rv;
676}
677
678main(int argc, char *argv[])
679{
680 /* save/restore file raid_disks chunk_size level layout start length devices...
681 */
682 int save;
683 int *fds;
684 char *file;
a6288483 685 char *buf;
e86c9dd6
NB
686 int storefd;
687 unsigned long long *offsets;
688 int raid_disks, chunk_size, level, layout;
689 unsigned long long start, length;
690 int i;
691
692 char *err = NULL;
693 if (argc < 10) {
694 fprintf(stderr, "Usage: test_stripe save/restore file raid_disks"
695 " chunk_size level layout start length devices...\n");
696 exit(1);
697 }
698 if (strcmp(argv[1], "save")==0)
699 save = 1;
700 else if (strcmp(argv[1], "restore") == 0)
701 save = 0;
48327135
NB
702 else if (strcmp(argv[1], "test") == 0)
703 save = 2;
e86c9dd6
NB
704 else {
705 fprintf(stderr, "test_stripe: must give 'save' or 'restore'.\n");
706 exit(2);
707 }
708
709 file = argv[2];
710 raid_disks = getnum(argv[3], &err);
711 chunk_size = getnum(argv[4], &err);
712 level = getnum(argv[5], &err);
713 layout = getnum(argv[6], &err);
714 start = getnum(argv[7], &err);
715 length = getnum(argv[8], &err);
716 if (err) {
717 fprintf(stderr, "test_stripe: Bad number: %s\n", err);
718 exit(2);
719 }
720 if (argc != raid_disks + 9) {
721 fprintf(stderr, "test_stripe: wrong number of devices: want %d found %d\n",
722 raid_disks, argc-9);
723 exit(2);
724 }
725 fds = malloc(raid_disks * sizeof(*fds));
726 offsets = malloc(raid_disks * sizeof(*offsets));
727 memset(offsets, 0, raid_disks * sizeof(*offsets));
728
729 storefd = open(file, O_RDWR);
730 if (storefd < 0) {
731 perror(file);
732 fprintf(stderr, "test_stripe: could not open %s.\n", file);
733 exit(3);
734 }
735 for (i=0; i<raid_disks; i++) {
736 fds[i] = open(argv[9+i], O_RDWR);
737 if (fds[i] < 0) {
738 perror(argv[9+i]);
739 fprintf(stderr,"test_stripe: cannot open %s.\n", argv[9+i]);
740 exit(3);
741 }
742 }
743
a6288483
N
744 buf = malloc(raid_disks * chunk_size);
745
48327135 746 if (save == 1) {
e86c9dd6
NB
747 int rv = save_stripes(fds, offsets,
748 raid_disks, chunk_size, level, layout,
749 1, &storefd,
a6288483 750 start, length, buf);
e86c9dd6 751 if (rv != 0) {
48327135
NB
752 fprintf(stderr,
753 "test_stripe: save_stripes returned %d\n", rv);
754 exit(1);
755 }
756 } else if (save == 2) {
757 int rv = test_stripes(fds, offsets,
758 raid_disks, chunk_size, level, layout,
759 start, length);
760 if (rv != 0) {
761 fprintf(stderr,
762 "test_stripe: test_stripes returned %d\n", rv);
e86c9dd6
NB
763 exit(1);
764 }
765 } else {
766 int rv = restore_stripes(fds, offsets,
767 raid_disks, chunk_size, level, layout,
353632d9 768 storefd, 0ULL,
e86c9dd6
NB
769 start, length);
770 if (rv != 0) {
48327135
NB
771 fprintf(stderr,
772 "test_stripe: restore_stripes returned %d\n",
773 rv);
e86c9dd6
NB
774 exit(1);
775 }
776 }
777 exit(0);
778}
779
780#endif /* MAIN */