aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/raid1.c
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.com>2015-07-26 21:48:52 -0400
committerNeilBrown <neilb@suse.com>2015-08-02 22:29:42 -0400
commit423f04d63cf421ea436bcc5be02543d549ce4b28 (patch)
tree0d98d3b3de62c7689fbe58a6229c7d760d998c55 /drivers/md/raid1.c
parent74d33293e467df61de1b1d8b2fbe29e550dec33b (diff)
md/raid1: extend spinlock to protect raid1_end_read_request against inconsistencies
raid1_end_read_request() assumes that the In_sync bits are consistent with the ->degaded count. raid1_spare_active updates the In_sync bit before the ->degraded count and so exposes an inconsistency, as does error() So extend the spinlock in raid1_spare_active() and error() to hide those inconsistencies. This should probably be part of Commit: 34cab6f42003 ("md/raid1: fix test for 'was read error from last working device'.") as it addresses the same issue. It fixes the same bug and should go to -stable for same reasons. Fixes: 76073054c95b ("md/raid1: clean up read_balance.") Cc: stable@vger.kernel.org (v3.0+) Signed-off-by: NeilBrown <neilb@suse.com>
Diffstat (limited to 'drivers/md/raid1.c')
-rw-r--r--drivers/md/raid1.c10
1 files changed, 6 insertions, 4 deletions
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 94f5b55069e0..967a4ed73929 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1476,6 +1476,7 @@ static void error(struct mddev *mddev, struct md_rdev *rdev)
1476{ 1476{
1477 char b[BDEVNAME_SIZE]; 1477 char b[BDEVNAME_SIZE];
1478 struct r1conf *conf = mddev->private; 1478 struct r1conf *conf = mddev->private;
1479 unsigned long flags;
1479 1480
1480 /* 1481 /*
1481 * If it is not operational, then we have already marked it as dead 1482 * If it is not operational, then we have already marked it as dead
@@ -1495,14 +1496,13 @@ static void error(struct mddev *mddev, struct md_rdev *rdev)
1495 return; 1496 return;
1496 } 1497 }
1497 set_bit(Blocked, &rdev->flags); 1498 set_bit(Blocked, &rdev->flags);
1499 spin_lock_irqsave(&conf->device_lock, flags);
1498 if (test_and_clear_bit(In_sync, &rdev->flags)) { 1500 if (test_and_clear_bit(In_sync, &rdev->flags)) {
1499 unsigned long flags;
1500 spin_lock_irqsave(&conf->device_lock, flags);
1501 mddev->degraded++; 1501 mddev->degraded++;
1502 set_bit(Faulty, &rdev->flags); 1502 set_bit(Faulty, &rdev->flags);
1503 spin_unlock_irqrestore(&conf->device_lock, flags);
1504 } else 1503 } else
1505 set_bit(Faulty, &rdev->flags); 1504 set_bit(Faulty, &rdev->flags);
1505 spin_unlock_irqrestore(&conf->device_lock, flags);
1506 /* 1506 /*
1507 * if recovery is running, make sure it aborts. 1507 * if recovery is running, make sure it aborts.
1508 */ 1508 */
@@ -1568,7 +1568,10 @@ static int raid1_spare_active(struct mddev *mddev)
1568 * Find all failed disks within the RAID1 configuration 1568 * Find all failed disks within the RAID1 configuration
1569 * and mark them readable. 1569 * and mark them readable.
1570 * Called under mddev lock, so rcu protection not needed. 1570 * Called under mddev lock, so rcu protection not needed.
1571 * device_lock used to avoid races with raid1_end_read_request
1572 * which expects 'In_sync' flags and ->degraded to be consistent.
1571 */ 1573 */
1574 spin_lock_irqsave(&conf->device_lock, flags);
1572 for (i = 0; i < conf->raid_disks; i++) { 1575 for (i = 0; i < conf->raid_disks; i++) {
1573 struct md_rdev *rdev = conf->mirrors[i].rdev; 1576 struct md_rdev *rdev = conf->mirrors[i].rdev;
1574 struct md_rdev *repl = conf->mirrors[conf->raid_disks + i].rdev; 1577 struct md_rdev *repl = conf->mirrors[conf->raid_disks + i].rdev;
@@ -1599,7 +1602,6 @@ static int raid1_spare_active(struct mddev *mddev)
1599 sysfs_notify_dirent_safe(rdev->sysfs_state); 1602 sysfs_notify_dirent_safe(rdev->sysfs_state);
1600 } 1603 }
1601 } 1604 }
1602 spin_lock_irqsave(&conf->device_lock, flags);
1603 mddev->degraded -= count; 1605 mddev->degraded -= count;
1604 spin_unlock_irqrestore(&conf->device_lock, flags); 1606 spin_unlock_irqrestore(&conf->device_lock, flags);
1605 1607