raid1: prevent freeze_array/wait_all_barriers deadlock
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.dailey@stratus.com> Fix: fd76863e37fe(RAID1: a new I/O barrier implementation to remove resync window) Reviewed-by: Coly Li <colyli@suse.de> Cc: stable@vger.kernel.org (v4.11) Signed-off-by: Shaohua Li <shli@fb.com>
This commit is contained in:
@@ -989,14 +989,6 @@ static void wait_barrier(struct r1conf *conf, sector_t sector_nr)
|
|||||||
_wait_barrier(conf, idx);
|
_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)
|
static void _allow_barrier(struct r1conf *conf, int idx)
|
||||||
{
|
{
|
||||||
atomic_dec(&conf->nr_pending[idx]);
|
atomic_dec(&conf->nr_pending[idx]);
|
||||||
@@ -1010,14 +1002,6 @@ static void allow_barrier(struct r1conf *conf, sector_t sector_nr)
|
|||||||
_allow_barrier(conf, idx);
|
_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 */
|
/* conf->resync_lock should be held */
|
||||||
static int get_unqueued_pending(struct r1conf *conf)
|
static int get_unqueued_pending(struct r1conf *conf)
|
||||||
{
|
{
|
||||||
@@ -1645,8 +1629,12 @@ static void print_conf(struct r1conf *conf)
|
|||||||
|
|
||||||
static void close_sync(struct r1conf *conf)
|
static void close_sync(struct r1conf *conf)
|
||||||
{
|
{
|
||||||
wait_all_barriers(conf);
|
int idx;
|
||||||
allow_all_barriers(conf);
|
|
||||||
|
for (idx = 0; idx < BARRIER_BUCKETS_NR; idx++) {
|
||||||
|
_wait_barrier(conf, idx);
|
||||||
|
_allow_barrier(conf, idx);
|
||||||
|
}
|
||||||
|
|
||||||
mempool_destroy(conf->r1buf_pool);
|
mempool_destroy(conf->r1buf_pool);
|
||||||
conf->r1buf_pool = NULL;
|
conf->r1buf_pool = NULL;
|
||||||
|
Reference in New Issue
Block a user