aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2011-07-28 11:33:42 +1000
committerNeilBrown <neilb@suse.de>2011-07-28 11:33:42 +1000
commit3a9f28a5117e00a868dd8b4395f9a707ae56764b (patch)
tree36fe0fc7a7ccfc0da03dea546286b7bdef581246 /drivers/md
parentd8f05d2995d467a91db1af01637e6ffd94660ca8 (diff)
downloadkernel_goldelico_gta04-3a9f28a5117e00a868dd8b4395f9a707ae56764b.zip
kernel_goldelico_gta04-3a9f28a5117e00a868dd8b4395f9a707ae56764b.tar.gz
kernel_goldelico_gta04-3a9f28a5117e00a868dd8b4395f9a707ae56764b.tar.bz2
md/raid1: improve handling of read failure during recovery.
If we cannot read a block from anywhere during recovery, there is now a better approach than just giving up. We can record a bad block on each device and keep going - being careful not to clear the bad block when a write succeeds as it might - it will be a write of incorrect data. We have now reached the state where - for raid1 - we only call md_error if md_set_badblocks has failed. Signed-off-by: NeilBrown <neilb@suse.de> Reviewed-by: Namhyung Kim <namhyung@gmail.com>
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/raid1.c41
1 files changed, 34 insertions, 7 deletions
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index e695715..039e3af 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1392,7 +1392,12 @@ static void end_sync_write(struct bio *bio, int error)
} else if (is_badblock(conf->mirrors[mirror].rdev,
r1_bio->sector,
r1_bio->sectors,
- &first_bad, &bad_sectors))
+ &first_bad, &bad_sectors) &&
+ !is_badblock(conf->mirrors[r1_bio->read_disk].rdev,
+ r1_bio->sector,
+ r1_bio->sectors,
+ &first_bad, &bad_sectors)
+ )
set_bit(R1BIO_MadeGood, &r1_bio->state);
update_head_pos(mirror, r1_bio);
@@ -1473,16 +1478,36 @@ static int fix_sync_read_error(r1bio_t *r1_bio)
if (!success) {
char b[BDEVNAME_SIZE];
- /* Cannot read from anywhere, array is toast */
- md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev);
+ int abort = 0;
+ /* Cannot read from anywhere, this block is lost.
+ * Record a bad block on each device. If that doesn't
+ * work just disable and interrupt the recovery.
+ * Don't fail devices as that won't really help.
+ */
printk(KERN_ALERT "md/raid1:%s: %s: unrecoverable I/O read error"
" for block %llu\n",
mdname(mddev),
bdevname(bio->bi_bdev, b),
(unsigned long long)r1_bio->sector);
- md_done_sync(mddev, r1_bio->sectors, 0);
- put_buf(r1_bio);
- return 0;
+ for (d = 0; d < conf->raid_disks; d++) {
+ rdev = conf->mirrors[d].rdev;
+ if (!rdev || test_bit(Faulty, &rdev->flags))
+ continue;
+ if (!rdev_set_badblocks(rdev, sect, s, 0))
+ abort = 1;
+ }
+ if (abort) {
+ mddev->recovery_disabled = 1;
+ set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+ md_done_sync(mddev, r1_bio->sectors, 0);
+ put_buf(r1_bio);
+ return 0;
+ }
+ /* Try next page */
+ sectors -= s;
+ sect += s;
+ idx++;
+ continue;
}
start = d;
@@ -1879,7 +1904,9 @@ static void raid1d(mddev_t *mddev)
if (bio->bi_end_io == NULL)
continue;
if (test_bit(BIO_UPTODATE,
- &bio->bi_flags)) {
+ &bio->bi_flags) &&
+ test_bit(R1BIO_MadeGood,
+ &r1_bio->state)) {
rdev_clear_badblocks(
rdev,
r1_bio->sector,