summaryrefslogtreecommitdiffstats
path: root/restripe.c
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2009-10-12 16:57:22 +1100
committerNeilBrown <neilb@suse.de>2009-10-12 16:57:22 +1100
commitcc50ccdc2960d9feb322002f6ebfd8fba950b79f (patch)
treec6d3f6db0b3d831e949ca11cbdc50549f4b51121 /restripe.c
parent487e48afabe36ff71c8e2a177880daaf4a24d089 (diff)
downloadmdadm-cc50ccdc2960d9feb322002f6ebfd8fba950b79f.tar.gz
mdadm-cc50ccdc2960d9feb322002f6ebfd8fba950b79f.tar.xz
mdadm-cc50ccdc2960d9feb322002f6ebfd8fba950b79f.zip
restripe : various fixed for RAID6 2-failure recovery.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'restripe.c')
-rw-r--r--restripe.c52
1 files changed, 40 insertions, 12 deletions
diff --git a/restripe.c b/restripe.c
index e5ecd10..d346920 100644
--- a/restripe.c
+++ b/restripe.c
@@ -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,60 @@ 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'
*/
- for (i = 0; i < data_disks; i++)
- bufs[i] = (uint8_t*)buf + chunk_size * ((qdisk+1+i) % raid_disks);
+ 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
+ * snum is syndrome disk for which 0 is immediately after Q
+ */
+ snum = (raid_disks + dnum - qdisk - 1) % raid_disks;
+ bufs[snum] = (uint8_t*)buf + chunk_size * i;
+ }
- fdisk[0] = (qdisk + 1 + fdisk[0]) % raid_disks;
- fdisk[1] = (qdisk + 1 + fdisk[1]) % raid_disks;
+ fdisk[0] = (raid_disks + fdisk[0] - qdisk - 1) % raid_disks;
+ fdisk[1] = (raid_disks + fdisk[1] - qdisk - 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++)