From: NeilBrown <neilb@suse.de>
Subject: md/raid10: expedite the removal of lots of devices.
Patch-mainline: no
References: bnc#768084

Currently each call to raid10_remove_disk() calls synchronize_rcu 
which can cause a delay of a few milliseconds.  For 100 devices this is
a few hundreds of milliseconds.  For 1000 it is a few seconds.

So hoist the 'synchronize_rcu()' up out of the loop.  This requires us
to call both before and after, but 2 isn't much worse than 1, but is
lots better than 1000.

Signed-off-by: Neil Brown <neilb@suse.de>

---
 drivers/md/md.c     |   27 ++++++++++++++++++++-------
 drivers/md/md.h     |    5 +++++
 drivers/md/raid10.c |    3 ++-
 3 files changed, 27 insertions(+), 8 deletions(-)

--- linux-3.0-SLE11-SP2-BTMU.orig/drivers/md/md.c
+++ linux-3.0-SLE11-SP2-BTMU/drivers/md/md.c
@@ -7506,6 +7506,7 @@ static int remove_and_add_spares(mddev_t
 {
 	mdk_rdev_t *rdev;
 	int spares = 0;
+	int remove_some = 0;
 
 	mddev->curr_resync_completed = 0;
 
@@ -7515,14 +7516,26 @@ static int remove_and_add_spares(mddev_t
 		    (test_bit(Faulty, &rdev->flags) ||
 		     ! test_bit(In_sync, &rdev->flags)) &&
 		    atomic_read(&rdev->nr_pending)==0) {
-			if (mddev->pers->hot_remove_disk(
-				    mddev, rdev->raid_disk)==0) {
-				char nm[20];
-				sprintf(nm,"rd%d", rdev->raid_disk);
-				sysfs_remove_link(&mddev->kobj, nm);
-				rdev->raid_disk = -1;
-			}
+			remove_some = 1;
+			set_bit(RemoveSynchronised, &rdev->flags);
 		}
+	if (remove_some) {
+		synchronize_rcu();
+		/* Now we know that no-one will take a new reference */
+		list_for_each_entry(rdev, &mddev->disks, same_set)
+			if (test_bit(RemoveSynchronised, &rdev->flags)) {
+				if (mddev->pers->hot_remove_disk(
+					    mddev, rdev->raid_disk)==0) {
+					char nm[20];
+					sprintf(nm,"rd%d", rdev->raid_disk);
+					sysfs_remove_link(&mddev->kobj, nm);
+					rdev->raid_disk = -1;
+				}
+				clear_bit(RemoveSynchronised, &rdev->flags);
+			}
+		synchronize_rcu();
+		/* Now any temp reference that was taken is released */
+	}
 
 	if (mddev->degraded && !mddev->recovery_disabled) {
 		list_for_each_entry(rdev, &mddev->disks, same_set) {
--- linux-3.0-SLE11-SP2-BTMU.orig/drivers/md/md.h
+++ linux-3.0-SLE11-SP2-BTMU/drivers/md/md.h
@@ -95,6 +95,11 @@ struct mdk_rdev_s
 #define Timeout		11		/* Device fault due to timeout.
 					 * 'Faulty' is required to be set.
 					 */
+#define	RemoveSynchronised 12		/* synchronize_rcu was called after
+					 * This device was known to be faulty,
+					 * so it is save to remove without
+					 * another call.
+					 */
 	wait_queue_head_t blocked_wait;
 
 	int desc_nr;			/* descriptor index in the superblock */
--- linux-3.0-SLE11-SP2-BTMU.orig/drivers/md/raid10.c
+++ linux-3.0-SLE11-SP2-BTMU/drivers/md/raid10.c
@@ -1401,7 +1401,8 @@ static int raid10_remove_disk(mddev_t *m
 			goto abort;
 		}
 		p->rdev = NULL;
-		synchronize_rcu();
+		if (!test_bit(RemoveSynchronised, &rdev->flags))
+			synchronize_rcu();
 		if (atomic_read(&rdev->nr_pending)) {
 			/* lost the race, try later */
 			err = -EBUSY;
