diff options
author | NeilBrown <neilb@suse.de> | 2011-07-28 11:33:42 +1000 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2011-07-28 11:33:42 +1000 |
commit | 3a9f28a5117e00a868dd8b4395f9a707ae56764b (patch) | |
tree | 36fe0fc7a7ccfc0da03dea546286b7bdef581246 /drivers/md/raid1.c | |
parent | d8f05d2995d467a91db1af01637e6ffd94660ca8 (diff) | |
download | op-kernel-dev-3a9f28a5117e00a868dd8b4395f9a707ae56764b.zip op-kernel-dev-3a9f28a5117e00a868dd8b4395f9a707ae56764b.tar.gz |
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/raid1.c')
-rw-r--r-- | drivers/md/raid1.c | 41 |
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, |