diff options
author | NeilBrown <neilb@suse.de> | 2013-06-11 09:08:03 +0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2013-06-14 02:10:27 +0400 |
commit | 725d6e579f06360744fc101b1af2f82d8aa282f1 (patch) | |
tree | c46f6b92ac05727d1201f6f63276dfeb6ddd4096 | |
parent | 635f6416a2e983adac8ccf90bffbeed0c1a76454 (diff) | |
download | linux-725d6e579f06360744fc101b1af2f82d8aa282f1.tar.xz |
md/raid10: check In_sync flag in 'enough()'.
It isn't really enough to check that the rdev is present, we need to
also be sure that the device is still In_sync.
Doing this requires using rcu_dereference to access the rdev, and
holding the rcu_read_lock() to ensure the rdev doesn't disappear while
we look at it.
Signed-off-by: NeilBrown <neilb@suse.de>
-rw-r--r-- | drivers/md/raid10.c | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 5169ed2a9156..aa8ba0760cac 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1633,6 +1633,7 @@ static void status(struct seq_file *seq, struct mddev *mddev) static int _enough(struct r10conf *conf, int previous, int ignore) { int first = 0; + int has_enough = 0; int disks, ncopies; if (previous) { disks = conf->prev.raid_disks; @@ -1642,21 +1643,27 @@ static int _enough(struct r10conf *conf, int previous, int ignore) ncopies = conf->geo.near_copies; } + rcu_read_lock(); do { int n = conf->copies; int cnt = 0; int this = first; while (n--) { - if (conf->mirrors[this].rdev && - this != ignore) + struct md_rdev *rdev; + if (this != ignore && + (rdev = rcu_dereference(conf->mirrors[this].rdev)) && + test_bit(In_sync, &rdev->flags)) cnt++; this = (this+1) % disks; } if (cnt == 0) - return 0; + goto out; first = (first + ncopies) % disks; } while (first != 0); - return 1; + has_enough = 1; +out: + rcu_read_unlock(); + return has_enough; } static int enough(struct r10conf *conf, int ignore) |