4.14-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Nate Dailey <nate.dai...@stratus.com>

commit f6eca2d43ed694ab8124dd24c88277f7eca93b7d upstream.

If freeze_array is attempted in the middle of close_sync/
wait_all_barriers, deadlock can occur.

freeze_array will wait for nr_pending and nr_queued to line up.
wait_all_barriers increments nr_pending for each barrier bucket, one
at a time, but doesn't actually issue IO that could be counted in
nr_queued. So freeze_array is blocked until wait_all_barriers
completes and allow_all_barriers runs. At the same time, when
_wait_barrier sees array_frozen == 1, it stops and waits for
freeze_array to complete.

Prevent the deadlock by making close_sync call _wait_barrier and
_allow_barrier for one bucket at a time, instead of deferring the
_allow_barrier calls until after all _wait_barriers are complete.

Signed-off-by: Nate Dailey <nate.dai...@stratus.com>
Fix: fd76863e37fe(RAID1: a new I/O barrier implementation to remove resync 
window)
Reviewed-by: Coly Li <col...@suse.de>
Signed-off-by: Shaohua Li <s...@fb.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 drivers/md/raid1.c |   24 ++++++------------------
 1 file changed, 6 insertions(+), 18 deletions(-)

--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -990,14 +990,6 @@ static void wait_barrier(struct r1conf *
        _wait_barrier(conf, idx);
 }
 
-static void wait_all_barriers(struct r1conf *conf)
-{
-       int idx;
-
-       for (idx = 0; idx < BARRIER_BUCKETS_NR; idx++)
-               _wait_barrier(conf, idx);
-}
-
 static void _allow_barrier(struct r1conf *conf, int idx)
 {
        atomic_dec(&conf->nr_pending[idx]);
@@ -1011,14 +1003,6 @@ static void allow_barrier(struct r1conf
        _allow_barrier(conf, idx);
 }
 
-static void allow_all_barriers(struct r1conf *conf)
-{
-       int idx;
-
-       for (idx = 0; idx < BARRIER_BUCKETS_NR; idx++)
-               _allow_barrier(conf, idx);
-}
-
 /* conf->resync_lock should be held */
 static int get_unqueued_pending(struct r1conf *conf)
 {
@@ -1654,8 +1638,12 @@ static void print_conf(struct r1conf *co
 
 static void close_sync(struct r1conf *conf)
 {
-       wait_all_barriers(conf);
-       allow_all_barriers(conf);
+       int idx;
+
+       for (idx = 0; idx < BARRIER_BUCKETS_NR; idx++) {
+               _wait_barrier(conf, idx);
+               _allow_barrier(conf, idx);
+       }
 
        mempool_destroy(conf->r1buf_pool);
        conf->r1buf_pool = NULL;


Reply via email to