]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - restripe.c
Merge branch 'master' in devel-3.0
[thirdparty/mdadm.git] / restripe.c
index b7b36368130f9326688425eb365808955496ad12..509b45087bed453b6f76aa23e4a2d4639a6d1405 100644 (file)
 
 static int geo_map(int block, unsigned long long stripe, int raid_disks, int level, int layout)
 {
-       /* On the given stripe, find which disk in the array with have
+       /* On the given stripe, find which disk in the array will have
         * block numbered 'block'.
+        * '-1' means the parity block.
+        * '-2' means the Q syndrome.
         */
        int pd;
 
@@ -71,6 +73,7 @@ static int geo_map(int block, unsigned long long stripe, int raid_disks, int lev
        case 600 + ALGORITHM_LEFT_ASYMMETRIC:
                pd = raid_disks - 1 - (stripe % raid_disks);
                if (block == -1) return pd;
+               if (block == -2) return (pd+1) % raid_disks;
                if (pd == raid_disks - 1)
                        return block+1;
                if (block >= pd)
@@ -80,6 +83,7 @@ static int geo_map(int block, unsigned long long stripe, int raid_disks, int lev
        case 600 + ALGORITHM_RIGHT_ASYMMETRIC:
                pd = stripe % raid_disks;
                if (block == -1) return pd;
+               if (block == -2) return (pd+1) % raid_disks;
                if (pd == raid_disks - 1)
                        return block+1;
                if (block >= pd)
@@ -89,11 +93,13 @@ static int geo_map(int block, unsigned long long stripe, int raid_disks, int lev
        case 600 + ALGORITHM_LEFT_SYMMETRIC:
                pd = raid_disks - 1 - (stripe % raid_disks);
                if (block == -1) return pd;
+               if (block == -2) return (pd+1) % raid_disks;
                return (pd + 2 + block) % raid_disks;
 
        case 600 + ALGORITHM_RIGHT_SYMMETRIC:
                pd = stripe % raid_disks;
                if (block == -1) return pd;
+               if (block == -2) return (pd+1) % raid_disks;
                return (pd + 2 + block) % raid_disks;
        }
        return -1;
@@ -112,10 +118,30 @@ static void xor_blocks(char *target, char **sources, int disks, int size)
        }
 }
 
+static void qsyndrome(char *p, char *q, char **sources, int disks, int size)
+{
+       int d, z;
+       char wq0, wp0, wd0, w10, w20;
+       for ( d = 0; d < size; d++) {
+               wq0 = wp0 = sources[disks-1][d];
+               for ( z = disks-2 ; z >= 0 ; z-- ) {
+                       wd0 = sources[z][d];
+                       wp0 ^= wd0;
+                       w20 = (wq0&0x80) ? 0xff : 0x00;
+                       w10 = (wq0 << 1) & 0xff;
+                       w20 &= 0x1d;
+                       w10 ^= w20;
+                       wq0 = w10 ^ wd0;
+               }
+               p[d] = wp0;
+               q[d] = wq0;
+       }
+}
+
 /* Save data:
  * We are given:
  *  A list of 'fds' of the active disks.  For now we require all to be present.
- *  A geomtry: raid_disks, chunk_size, level, layout
+ *  A geometry: raid_disks, chunk_size, level, layout
  *  A list of 'fds' for mirrored targets.  They are already seeked to
  *    right (Write) location
  *  A start and length
@@ -126,7 +152,8 @@ int save_stripes(int *source, unsigned long long *offsets,
                 int nwrites, int *dest,
                 unsigned long long start, unsigned long long length)
 {
-       char buf[8192];
+       char abuf[8192+512];
+       char *buf = (char*)(((unsigned long)abuf+511)&~511UL);
        int cpos = start % chunk_size; /* where in chunk we are up to */
        int len;
        int data_disks = raid_disks - (level == 0 ? 0 : level <=5 ? 1 : 2);
@@ -136,7 +163,7 @@ int save_stripes(int *source, unsigned long long *offsets,
                unsigned long long offset;
                int i;
                len = chunk_size - cpos;
-               if (len > sizeof(buf)) len = sizeof(buf);
+               if (len > 8192) len = 8192;
                if (len > length) len = length;
                /* len bytes to be moved from one device */
 
@@ -193,6 +220,7 @@ int restore_stripes(int *dest, unsigned long long *offsets,
        while (length > 0) {
                int len = data_disks * chunk_size;
                unsigned long long offset;
+               int disk, qdisk;
                if (length < len)
                        return -3;
                for (i=0; i < data_disks; i++) {
@@ -207,11 +235,22 @@ int restore_stripes(int *dest, unsigned long long *offsets,
                }
                /* We have the data, now do the parity */
                offset = (start/chunk_size/data_disks) * chunk_size;
-               if (level >= 4) {
-                       int disk = geo_map(-1, start/chunk_size/data_disks,
+               switch (level) {
+               case 4:
+               case 5:
+                       disk = geo_map(-1, start/chunk_size/data_disks,
                                           raid_disks, level, layout);
                        xor_blocks(stripes[disk], blocks, data_disks, chunk_size);
-                       /* FIXME need to do raid6 Q as well */
+                       break;
+               case 6:
+                       disk = geo_map(-1, start/chunk_size/data_disks,
+                                      raid_disks, level, layout);
+                       qdisk = geo_map(-2, start/chunk_size/data_disks,
+                                      raid_disks, level, layout);
+
+                       qsyndrome(stripes[disk], stripes[qdisk], blocks,
+                                 data_disks, chunk_size);
+                       break;
                }
                for (i=0; i < raid_disks ; i++)
                        if (dest[i] >= 0) {
@@ -228,6 +267,58 @@ int restore_stripes(int *dest, unsigned long long *offsets,
 
 #ifdef MAIN
 
+int test_stripes(int *source, unsigned long long *offsets,
+                int raid_disks, int chunk_size, int level, int layout,
+                unsigned long long start, unsigned long long length)
+{
+       /* ready the data and p (and q) blocks, and check we got them right */
+       char *stripe_buf = malloc(raid_disks * chunk_size);
+       char **stripes = malloc(raid_disks * sizeof(char*));
+       char **blocks = malloc(raid_disks * sizeof(char*));
+       char *p = malloc(chunk_size);
+       char *q = malloc(chunk_size);
+
+       int i;
+       int data_disks = raid_disks - (level == 5 ? 1: 2);
+       for ( i = 0 ; i < raid_disks ; i++)
+               stripes[i] = stripe_buf + i * chunk_size;
+
+       while (length > 0) {
+               int disk;
+
+               for (i = 0 ; i < raid_disks ; i++) {
+                       lseek64(source[i], offsets[i]+start, 0);
+                       read(source[i], stripes[i], chunk_size);
+               }
+               for (i = 0 ; i < data_disks ; i++) {
+                       int disk = geo_map(i, start/chunk_size, raid_disks,
+                                          level, layout);
+                       blocks[i] = stripes[disk];
+                       printf("%d->%d\n", i, disk);
+               }
+               switch(level) {
+               case 6:
+                       qsyndrome(p, q, blocks, data_disks, chunk_size);
+                       disk = geo_map(-1, start/chunk_size, raid_disks,
+                                      level, layout);
+                       if (memcmp(p, stripes[disk], chunk_size) != 0) {
+                               printf("P(%d) wrong at %llu\n", disk,
+                                      start / chunk_size);
+                       }
+                       disk = geo_map(-2, start/chunk_size, raid_disks,
+                                      level, layout);
+                       if (memcmp(q, stripes[disk], chunk_size) != 0) {
+                               printf("Q(%d) wrong at %llu\n", disk,
+                                      start / chunk_size);
+                       }
+                       break;
+               }
+               length -= chunk_size;
+               start += chunk_size;
+       }
+       return 0;
+}
+
 unsigned long long getnum(char *str, char **err)
 {
        char *e;
@@ -262,6 +353,8 @@ main(int argc, char *argv[])
                save = 1;
        else if (strcmp(argv[1], "restore") == 0)
                save = 0;
+       else if (strcmp(argv[1], "test") == 0)
+               save = 2;
        else {
                fprintf(stderr, "test_stripe: must give 'save' or 'restore'.\n");
                exit(2);
@@ -302,13 +395,23 @@ main(int argc, char *argv[])
                }
        }
 
-       if (save) {
+       if (save == 1) {
                int rv = save_stripes(fds, offsets,
                                      raid_disks, chunk_size, level, layout,
                                      1, &storefd,
                                      start, length);
                if (rv != 0) {
-                       fprintf(stderr, "test_stripe: save_stripes returned %d\n", rv);
+                       fprintf(stderr,
+                               "test_stripe: save_stripes returned %d\n", rv);
+                       exit(1);
+               }
+       } else if (save == 2) {
+               int rv = test_stripes(fds, offsets,
+                                     raid_disks, chunk_size, level, layout,
+                                     start, length);
+               if (rv != 0) {
+                       fprintf(stderr,
+                               "test_stripe: test_stripes returned %d\n", rv);
                        exit(1);
                }
        } else {
@@ -317,7 +420,9 @@ main(int argc, char *argv[])
                                         storefd, 0ULL,
                                         start, length);
                if (rv != 0) {
-                       fprintf(stderr, "test_stripe: restore_stripes returned %d\n", rv);
+                       fprintf(stderr,
+                               "test_stripe: restore_stripes returned %d\n",
+                               rv);
                        exit(1);
                }
        }