md/raid10: check In_sync flag in 'enough()'.
authorNeilBrown <neilb@suse.de>
Tue, 11 Jun 2013 05:08:03 +0000 (15:08 +1000)
committerNeilBrown <neilb@suse.de>
Thu, 13 Jun 2013 22:10:27 +0000 (08:10 +1000)
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>
drivers/md/raid10.c

index 5169ed2..aa8ba07 100644 (file)
@@ -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)