Commit 423f04d6 authored by NeilBrown's avatar NeilBrown

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: 34cab6f4 ("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: 76073054 ("md/raid1: clean up read_balance.")
Cc: stable@vger.kernel.org (v3.0+)
Signed-off-by: default avatarNeilBrown <neilb@suse.com>
parent 74d33293
...@@ -1476,6 +1476,7 @@ static void error(struct mddev *mddev, struct md_rdev *rdev) ...@@ -1476,6 +1476,7 @@ static void error(struct mddev *mddev, struct md_rdev *rdev)
{ {
char b[BDEVNAME_SIZE]; char b[BDEVNAME_SIZE];
struct r1conf *conf = mddev->private; struct r1conf *conf = mddev->private;
unsigned long flags;
/* /*
* If it is not operational, then we have already marked it as dead * 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,14 +1496,13 @@ static void error(struct mddev *mddev, struct md_rdev *rdev)
return; return;
} }
set_bit(Blocked, &rdev->flags); set_bit(Blocked, &rdev->flags);
spin_lock_irqsave(&conf->device_lock, flags);
if (test_and_clear_bit(In_sync, &rdev->flags)) { if (test_and_clear_bit(In_sync, &rdev->flags)) {
unsigned long flags;
spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded++; mddev->degraded++;
set_bit(Faulty, &rdev->flags); set_bit(Faulty, &rdev->flags);
spin_unlock_irqrestore(&conf->device_lock, flags);
} else } else
set_bit(Faulty, &rdev->flags); set_bit(Faulty, &rdev->flags);
spin_unlock_irqrestore(&conf->device_lock, flags);
/* /*
* if recovery is running, make sure it aborts. * if recovery is running, make sure it aborts.
*/ */
...@@ -1568,7 +1568,10 @@ static int raid1_spare_active(struct mddev *mddev) ...@@ -1568,7 +1568,10 @@ static int raid1_spare_active(struct mddev *mddev)
* Find all failed disks within the RAID1 configuration * Find all failed disks within the RAID1 configuration
* and mark them readable. * and mark them readable.
* Called under mddev lock, so rcu protection not needed. * Called under mddev lock, so rcu protection not needed.
* device_lock used to avoid races with raid1_end_read_request
* which expects 'In_sync' flags and ->degraded to be consistent.
*/ */
spin_lock_irqsave(&conf->device_lock, flags);
for (i = 0; i < conf->raid_disks; i++) { for (i = 0; i < conf->raid_disks; i++) {
struct md_rdev *rdev = conf->mirrors[i].rdev; struct md_rdev *rdev = conf->mirrors[i].rdev;
struct md_rdev *repl = conf->mirrors[conf->raid_disks + i].rdev; struct md_rdev *repl = conf->mirrors[conf->raid_disks + i].rdev;
...@@ -1599,7 +1602,6 @@ static int raid1_spare_active(struct mddev *mddev) ...@@ -1599,7 +1602,6 @@ static int raid1_spare_active(struct mddev *mddev)
sysfs_notify_dirent_safe(rdev->sysfs_state); sysfs_notify_dirent_safe(rdev->sysfs_state);
} }
} }
spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded -= count; mddev->degraded -= count;
spin_unlock_irqrestore(&conf->device_lock, flags); spin_unlock_irqrestore(&conf->device_lock, flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment