Commit 55cc39f3 authored by NeilBrown's avatar NeilBrown Committed by Shaohua Li

md: close a race with setting mddev->in_sync

If ->in_sync is being set just as md_write_start() is being called,
it is possible that set_in_sync() won't see the elevated
->writes_pending, and md_write_start() won't see the set ->in_sync.

To close this race, re-test ->writes_pending after setting ->in_sync,
and add memory barriers to ensure the increment of ->writes_pending
will be seen by the time of this second test, or the new ->in_sync
will be seen by md_write_start().

Add a spinlock to array_state_show() to ensure this temporary
instability is never visible from userspace.
Signed-off-by: default avatarNeilBrown <neilb@suse.com>
Signed-off-by: default avatarShaohua Li <shli@fb.com>
parent 6497709b
...@@ -2258,6 +2258,10 @@ static bool set_in_sync(struct mddev *mddev) ...@@ -2258,6 +2258,10 @@ static bool set_in_sync(struct mddev *mddev)
if (atomic_read(&mddev->writes_pending) == 0) { if (atomic_read(&mddev->writes_pending) == 0) {
if (mddev->in_sync == 0) { if (mddev->in_sync == 0) {
mddev->in_sync = 1; mddev->in_sync = 1;
smp_mb();
if (atomic_read(&mddev->writes_pending))
/* lost a race with md_write_start() */
mddev->in_sync = 0;
set_bit(MD_SB_CHANGE_CLEAN, &mddev->sb_flags); set_bit(MD_SB_CHANGE_CLEAN, &mddev->sb_flags);
sysfs_notify_dirent_safe(mddev->sysfs_state); sysfs_notify_dirent_safe(mddev->sysfs_state);
} }
...@@ -4011,6 +4015,7 @@ array_state_show(struct mddev *mddev, char *page) ...@@ -4011,6 +4015,7 @@ array_state_show(struct mddev *mddev, char *page)
st = read_auto; st = read_auto;
break; break;
case 0: case 0:
spin_lock(&mddev->lock);
if (test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags)) if (test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags))
st = write_pending; st = write_pending;
else if (mddev->in_sync) else if (mddev->in_sync)
...@@ -4019,6 +4024,7 @@ array_state_show(struct mddev *mddev, char *page) ...@@ -4019,6 +4024,7 @@ array_state_show(struct mddev *mddev, char *page)
st = active_idle; st = active_idle;
else else
st = active; st = active;
spin_unlock(&mddev->lock);
} }
else { else {
if (list_empty(&mddev->disks) && if (list_empty(&mddev->disks) &&
...@@ -7885,6 +7891,7 @@ void md_write_start(struct mddev *mddev, struct bio *bi) ...@@ -7885,6 +7891,7 @@ void md_write_start(struct mddev *mddev, struct bio *bi)
did_change = 1; did_change = 1;
} }
atomic_inc(&mddev->writes_pending); atomic_inc(&mddev->writes_pending);
smp_mb(); /* Match smp_mb in set_in_sync() */
if (mddev->safemode == 1) if (mddev->safemode == 1)
mddev->safemode = 0; mddev->safemode = 0;
if (mddev->in_sync) { if (mddev->in_sync) {
......
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