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 | olio-linux-3.10-3a9f28a5117e00a868dd8b4395f9a707ae56764b.tar.xz olio-linux-3.10-3a9f28a5117e00a868dd8b4395f9a707ae56764b.zip  | |
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 e6957151233..039e3af7292 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,  |