summaryrefslogtreecommitdiff
path: root/drivers/md/md.c
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.com>2016-06-02 16:19:53 +1000
committerShaohua Li <shli@fb.com>2016-06-13 11:54:22 -0700
commitd787be4092e27728cb4c012bee9762098ef3c662 (patch)
treeb8de57ed842d3c01f6fdd4f6ee5be6408763a993 /drivers/md/md.c
parentf5b67ae86ee317db20c0e10d54f16a0bbbd3207d (diff)
md: reduce the number of synchronize_rcu() calls when multiple devices fail.
Every time a device is removed with ->hot_remove_disk() a synchronize_rcu() call is made which can delay several milliseconds in some case. If lots of devices fail at once - as could happen with a large RAID10 where one set of devices are removed all at once - these delays can add up to be very inconcenient. As failure is not reversible we can check for that first, setting a separate flag if it is found, and then all synchronize_rcu() once for all the flagged devices. Then ->hot_remove_disk() function can skip the synchronize_rcu() step if the flag is set. fix build error(Shaohua) Signed-off-by: NeilBrown <neilb@suse.com> Signed-off-by: Shaohua Li <shli@fb.com>
Diffstat (limited to 'drivers/md/md.c')
-rw-r--r--drivers/md/md.c29
1 files changed, 26 insertions, 3 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 0793754eeffd..2ed547f5c3b6 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -8180,15 +8180,34 @@ static int remove_and_add_spares(struct mddev *mddev,
struct md_rdev *rdev;
int spares = 0;
int removed = 0;
+ bool remove_some = false;
- rdev_for_each(rdev, mddev)
+ rdev_for_each(rdev, mddev) {
+ if ((this == NULL || rdev == this) &&
+ rdev->raid_disk >= 0 &&
+ !test_bit(Blocked, &rdev->flags) &&
+ test_bit(Faulty, &rdev->flags) &&
+ atomic_read(&rdev->nr_pending)==0) {
+ /* Faulty non-Blocked devices with nr_pending == 0
+ * never get nr_pending incremented,
+ * never get Faulty cleared, and never get Blocked set.
+ * So we can synchronize_rcu now rather than once per device
+ */
+ remove_some = true;
+ set_bit(RemoveSynchronized, &rdev->flags);
+ }
+ }
+
+ if (remove_some)
+ synchronize_rcu();
+ rdev_for_each(rdev, mddev) {
if ((this == NULL || rdev == this) &&
rdev->raid_disk >= 0 &&
!test_bit(Blocked, &rdev->flags) &&
- (test_bit(Faulty, &rdev->flags) ||
+ ((test_bit(RemoveSynchronized, &rdev->flags) ||
(!test_bit(In_sync, &rdev->flags) &&
!test_bit(Journal, &rdev->flags))) &&
- atomic_read(&rdev->nr_pending)==0) {
+ atomic_read(&rdev->nr_pending)==0)) {
if (mddev->pers->hot_remove_disk(
mddev, rdev) == 0) {
sysfs_unlink_rdev(mddev, rdev);
@@ -8196,6 +8215,10 @@ static int remove_and_add_spares(struct mddev *mddev,
removed++;
}
}
+ if (remove_some && test_bit(RemoveSynchronized, &rdev->flags))
+ clear_bit(RemoveSynchronized, &rdev->flags);
+ }
+
if (removed && mddev->kobj.sd)
sysfs_notify(&mddev->kobj, NULL, "degraded");