From: NeilBrown <neilb@cse.unsw.edu.au>

Two problems are fixed here.
1/ if the array is known to require a resync (parity update),
  but there are too many failed devices,  the resync cannot complete
  but will be retried indefinitely.
2/ if the array has too many failed drives to be usable and a spare is
  available, reconstruction will be attempted, but cannot work.  This
  also is retried indefinitely.

Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/md/md.c        |   12 ++++++------
 25-akpm/drivers/md/raid5.c     |   13 +++++++++++++
 25-akpm/drivers/md/raid6main.c |   12 ++++++++++++
 3 files changed, 31 insertions(+), 6 deletions(-)

diff -puN drivers/md/md.c~md-make-raid5-and-raid6-robust-against-failure-during-recovery drivers/md/md.c
--- 25/drivers/md/md.c~md-make-raid5-and-raid6-robust-against-failure-during-recovery	2005-02-17 18:00:51.000000000 -0800
+++ 25-akpm/drivers/md/md.c	2005-02-17 18:00:51.000000000 -0800
@@ -3655,18 +3655,18 @@ void md_check_recovery(mddev_t *mddev)
 
 		/* no recovery is running.
 		 * remove any failed drives, then
-		 * add spares if possible
+		 * add spares if possible.
+		 * Spare are also removed and re-added, to allow
+		 * the personality to fail the re-add.
 		 */
-		ITERATE_RDEV(mddev,rdev,rtmp) {
+		ITERATE_RDEV(mddev,rdev,rtmp)
 			if (rdev->raid_disk >= 0 &&
-			    rdev->faulty &&
+			    (rdev->faulty || ! rdev->in_sync) &&
 			    atomic_read(&rdev->nr_pending)==0) {
 				if (mddev->pers->hot_remove_disk(mddev, rdev->raid_disk)==0)
 					rdev->raid_disk = -1;
 			}
-			if (!rdev->faulty && rdev->raid_disk >= 0 && !rdev->in_sync)
-				spares++;
-		}
+
 		if (mddev->degraded) {
 			ITERATE_RDEV(mddev,rdev,rtmp)
 				if (rdev->raid_disk < 0
diff -puN drivers/md/raid5.c~md-make-raid5-and-raid6-robust-against-failure-during-recovery drivers/md/raid5.c
--- 25/drivers/md/raid5.c~md-make-raid5-and-raid6-robust-against-failure-during-recovery	2005-02-17 18:00:51.000000000 -0800
+++ 25-akpm/drivers/md/raid5.c	2005-02-17 18:00:51.000000000 -0800
@@ -1493,6 +1493,15 @@ static int sync_request (mddev_t *mddev,
 		unplug_slaves(mddev);
 		return 0;
 	}
+	/* if there is 1 or more failed drives and we are trying
+	 * to resync, then assert that we are finished, because there is
+	 * nothing we can do.
+	 */
+	if (mddev->degraded >= 1 && test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
+		int rv = (mddev->size << 1) - sector_nr;
+		md_done_sync(mddev, rv, 1);
+		return rv;
+	}
 
 	x = sector_nr;
 	chunk_offset = sector_div(x, sectors_per_chunk);
@@ -1884,6 +1893,10 @@ static int raid5_add_disk(mddev_t *mddev
 	int disk;
 	struct disk_info *p;
 
+	if (mddev->degraded > 1)
+		/* no point adding a device */
+		return 0;
+
 	/*
 	 * find the disk ...
 	 */
diff -puN drivers/md/raid6main.c~md-make-raid5-and-raid6-robust-against-failure-during-recovery drivers/md/raid6main.c
--- 25/drivers/md/raid6main.c~md-make-raid5-and-raid6-robust-against-failure-during-recovery	2005-02-17 18:00:51.000000000 -0800
+++ 25-akpm/drivers/md/raid6main.c	2005-02-17 18:00:51.000000000 -0800
@@ -1652,6 +1652,15 @@ static int sync_request (mddev_t *mddev,
 		unplug_slaves(mddev);
 		return 0;
 	}
+	/* if there are 2 or more failed drives and we are trying
+	 * to resync, then assert that we are finished, because there is
+	 * nothing we can do.
+	 */
+	if (mddev->degraded >= 2 && test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
+		int rv = (mddev->size << 1) - sector_nr;
+		md_done_sync(mddev, rv, 1);
+		return rv;
+	}
 
 	x = sector_nr;
 	chunk_offset = sector_div(x, sectors_per_chunk);
@@ -2050,6 +2059,9 @@ static int raid6_add_disk(mddev_t *mddev
 	int disk;
 	struct disk_info *p;
 
+	if (mddev->degraded > 2)
+		/* no point adding a device */
+		return 0;
 	/*
 	 * find the disk ...
 	 */
_