From: NeilBrown Subject: Disable recovery when degraded RAID1 array appears to be faulty. Patch-mainline: no References: bnc#447835 If a raid1 has only one working drive and it has a sector which gives an error on read, then an attempt to recover onto a spare will fail, but as the single remaining drive is not removed from the array, the recovery will be immediately re-attempted, resulting in an infinite recovery loop. So detect this situation and don't retry recovery once an error on the lone remaining drive is detected. Allow recovery to be retried once every time a spare is added in case the problem wasn't actually a media error. Signed-off-by: Neil Brown --- drivers/md/md.c | 11 ++++++----- drivers/md/raid1.c | 8 ++++++-- include/linux/raid/md_k.h | 5 +++++ 3 files changed, 17 insertions(+), 7 deletions(-) --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3071,7 +3071,7 @@ action_store(mddev_t *mddev, const char set_bit(MD_RECOVERY_INTR, &mddev->recovery); md_unregister_thread(mddev->sync_thread); mddev->sync_thread = NULL; - mddev->recovery = 0; + mddev->recovery &= ~65535; } } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) @@ -4528,7 +4528,7 @@ static int set_bitmap_file(mddev_t *mdde if (mddev->pers) { if (!mddev->pers->quiesce) return -EBUSY; - if (mddev->recovery || mddev->sync_thread) + if ((mddev->recovery & 65535) || mddev->sync_thread) return -EBUSY; /* we should be able to change the bitmap.. */ } @@ -4783,7 +4783,7 @@ static int update_array_info(mddev_t *md if ((state ^ info->state) & (1<pers->quiesce == NULL) return -EINVAL; - if (mddev->recovery || mddev->sync_thread) + if ((mddev->recovery & 65535) || mddev->sync_thread) return -EBUSY; if (info->state & (1<degraded && ! mddev->ro) { + if (mddev->degraded && ! mddev->ro && + !test_bit(MD_RECOVERY_DISABLED, &mddev->recovery)) { rdev_for_each(rdev, rtmp, mddev) { if (rdev->raid_disk >= 0 && !test_bit(In_sync, &rdev->flags) && @@ -6200,7 +6201,7 @@ void md_check_recovery(mddev_t *mddev) rdev_for_each(rdev, rtmp, mddev) rdev->saved_raid_disk = -1; - mddev->recovery = 0; + mddev->recovery &= ~65535; /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); sysfs_notify(&mddev->kobj, NULL, "sync_action"); --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1012,12 +1012,16 @@ static void error(mddev_t *mddev, mdk_rd * else mark the drive as failed */ if (test_bit(In_sync, &rdev->flags) - && (conf->raid_disks - mddev->degraded) == 1) + && (conf->raid_disks - mddev->degraded) == 1) { /* * Don't fail the drive, act as though we were just a - * normal single drive + * normal single drive. + * Disable any future recovery attempts as they will + * likely hit an error on this device. */ + set_bit(MD_RECOVERY_DISABLED, &mddev->recovery); return; + } if (test_and_clear_bit(In_sync, &rdev->flags)) { unsigned long flags; spin_lock_irqsave(&conf->device_lock, flags); --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -200,6 +200,8 @@ struct mddev_s * RESHAPE: A reshape is happening * * If neither SYNC or RESHAPE are set, then it is a recovery. + * + * DISABLED: read error on degraded array makes recovery impossible. */ #define MD_RECOVERY_RUNNING 0 #define MD_RECOVERY_SYNC 1 @@ -212,6 +214,9 @@ struct mddev_s #define MD_RECOVERY_RESHAPE 8 #define MD_RECOVERY_FROZEN 9 + +#define MD_RECOVERY_DISABLED 16 + unsigned long recovery; int in_sync; /* know to not need resync */