]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - restripe.c
Add missing 'continue' in Grow_restart.
[thirdparty/mdadm.git] / restripe.c
index 38b44122066cd9c62a66d04b50157f3ff0d89557..f673206102f0aea0c2b449d6ceb067a4230f1e5d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * mdadm - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2006 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2006-2009 Neil Brown <neilb@suse.de>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
@@ -425,7 +425,7 @@ int save_stripes(int *source, unsigned long long *offsets,
                                       raid_disks, level, layout);
                        if (dnum < 0) abort();
                        if (source[dnum] < 0 ||
-                           lseek64(source[dnum], offsets[disk]+offset, 0) < 0 ||
+                           lseek64(source[dnum], offsets[dnum]+offset, 0) < 0 ||
                            read(source[dnum], buf+disk * chunk_size, chunk_size)
                            != chunk_size)
                                if (failed <= 2) {
@@ -465,32 +465,71 @@ int save_stripes(int *source, unsigned long long *offsets,
                                 * 'p' and 'q' get to be all zero
                                 */
                                for (i = 0; i < raid_disks; i++)
-                                       if (i == disk || i == qdisk)
-                                               bufs[i] = zero;
-                                       else
-                                               bufs[i] = (uint8_t*)buf+i*chunk_size;
+                                       bufs[i] = zero;
+                               for (i = 0; i < data_disks; i++) {
+                                       int dnum = geo_map(i,
+                                                          start/chunk_size/data_disks,
+                                                          raid_disks, level, layout);
+                                       int snum;
+                                       /* i is the logical block number, so is index to 'buf'.
+                                        * dnum is physical disk number
+                                        * and thus the syndrome number.
+                                        */
+                                       snum = dnum;
+                                       bufs[snum] = (uint8_t*)buf + chunk_size * i;
+                               }
                                syndrome_disks = raid_disks;
                        } else {
                                /* for md, q is over 'data_disks' blocks,
                                 * starting immediately after 'q'
+                                * Note that for the '_6' variety, the p block
+                                * makes a hole that we need to be careful of.
                                 */
-                               for (i = 0; i < data_disks; i++)
-                                       bufs[i] = (uint8_t*)buf + chunk_size * ((qdisk+1+i) % raid_disks);
+                               int j;
+                               int snum = 0;
+                               for (j = 0; j < raid_disks; j++) {
+                                       int dnum = (qdisk + 1 + j) % raid_disks;
+                                       if (dnum == disk || dnum == qdisk)
+                                               continue;
+                                       for (i = 0; i < data_disks; i++)
+                                               if (geo_map(i,
+                                                           start/chunk_size/data_disks,
+                                                           raid_disks, level, layout) == dnum)
+                                                       break;
+                                       /* i is the logical block number, so is index to 'buf'.
+                                        * dnum is physical disk number
+                                        * snum is syndrome disk for which 0 is immediately after Q
+                                        */
+                                       bufs[snum] = (uint8_t*)buf + chunk_size * i;
+
+                                       if (fblock[0] == i)
+                                               fdisk[0] = snum;
+                                       if (fblock[1] == i)
+                                               fdisk[1] = snum;
+                                       snum++;
+                               }
 
-                               fdisk[0] = (qdisk + 1 + fdisk[0]) * raid_disks;
-                               fdisk[1] = (qdisk + 1 + fdisk[1]) * raid_disks;
                                syndrome_disks = data_disks;
                        }
-                       bufs[syndrome_disks] = (uint8_t*)buf + chunk_size * disk;
-                       bufs[syndrome_disks+1] = (uint8_t*)buf + chunk_size * qdisk;
+
+                       /* Place P and Q blocks at end of bufs */
+                       bufs[syndrome_disks] = (uint8_t*)buf + chunk_size * data_disks;
+                       bufs[syndrome_disks+1] = (uint8_t*)buf + chunk_size * (data_disks+1);
+
                        if (fblock[1] == data_disks)
                                /* One data failed, and parity failed */
                                raid6_datap_recov(syndrome_disks+2, chunk_size,
                                                  fdisk[0], bufs);
-                       else 
+                       else {
+                               if (fdisk[0] > fdisk[1]) {
+                                       int t = fdisk[0];
+                                       fdisk[0] = fdisk[1];
+                                       fdisk[1] = t;
+                               }
                                /* Two data blocks failed, P,Q OK */
                                raid6_2data_recov(syndrome_disks+2, chunk_size,
                                                  fdisk[0], fdisk[1], bufs);
+                       }
                }
 
                for (i=0; i<nwrites; i++)
@@ -519,13 +558,14 @@ int restore_stripes(int *dest, unsigned long long *offsets,
                    int source, unsigned long long read_offset,
                    unsigned long long start, unsigned long long length)
 {
-       char *stripe_buf = malloc(raid_disks * chunk_size);
+       char *stripe_buf;
        char **stripes = malloc(raid_disks * sizeof(char*));
        char **blocks = malloc(raid_disks * sizeof(char*));
        int i;
 
        int data_disks = raid_disks - (level == 0 ? 0 : level <= 5 ? 1 : 2);
 
+       posix_memalign((void**)&stripe_buf, 4096, raid_disks * chunk_size);
        if (zero == NULL) {
                zero = malloc(chunk_size);
                if (zero)
@@ -644,7 +684,7 @@ int test_stripes(int *source, unsigned long long *offsets,
                }
                switch(level) {
                case 6:
-                       qsyndrome(p, q, blocks, data_disks, chunk_size);
+                       qsyndrome(p, q, (uint8_t**)blocks, data_disks, chunk_size);
                        disk = geo_map(-1, start/chunk_size, raid_disks,
                                       level, layout);
                        if (memcmp(p, stripes[disk], chunk_size) != 0) {