Skip to content

Commit be4addb

Browse files
bmarzinsMikulas Patocka
authored andcommitted
dm: Fix deadlock when reloading a multipath table
Request-based devices (dm-multipath) queue I/O in blk-mq on noflush suspends. Any queued IO will make it impossible to freeze the queue. If a process attempts to update the queue limits while there is queued IO, it can be get stuck holding the limits lock, while unable to freeze the queue. If device-mapper then attempts to update the limits during a table swap, it will deadlock trying to grab the limits lock while making it impossible to flush the IO. Disallow updating the queue limits during a table swap, when updating an immutable request-based dm device (dm-multipath) during a noflush suspend. It is userspace's responsibility to make sure that the new table uses the same limits as the existing table if it asks for a noflush suspend. Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com> Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
1 parent 4929ba5 commit be4addb

3 files changed

Lines changed: 29 additions & 17 deletions

File tree

drivers/md/dm-table.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2043,6 +2043,10 @@ bool dm_table_supports_size_change(struct dm_table *t, sector_t old_size,
20432043
return true;
20442044
}
20452045

2046+
/*
2047+
* This function will be skipped by noflush reloads of immutable request
2048+
* based devices (dm-mpath).
2049+
*/
20462050
int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
20472051
struct queue_limits *limits)
20482052
{

drivers/md/dm-thin.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4383,11 +4383,8 @@ static void thin_postsuspend(struct dm_target *ti)
43834383
{
43844384
struct thin_c *tc = ti->private;
43854385

4386-
/*
4387-
* The dm_noflush_suspending flag has been cleared by now, so
4388-
* unfortunately we must always run this.
4389-
*/
4390-
noflush_work(tc, do_noflush_stop);
4386+
if (dm_noflush_suspending(ti))
4387+
noflush_work(tc, do_noflush_stop);
43914388
}
43924389

43934390
static int thin_preresume(struct dm_target *ti)

drivers/md/dm.c

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,7 +2439,6 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
24392439
{
24402440
struct dm_table *old_map;
24412441
sector_t size, old_size;
2442-
int ret;
24432442

24442443
lockdep_assert_held(&md->suspend_lock);
24452444

@@ -2454,11 +2453,13 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
24542453

24552454
set_capacity(md->disk, size);
24562455

2457-
ret = dm_table_set_restrictions(t, md->queue, limits);
2458-
if (ret) {
2459-
set_capacity(md->disk, old_size);
2460-
old_map = ERR_PTR(ret);
2461-
goto out;
2456+
if (limits) {
2457+
int ret = dm_table_set_restrictions(t, md->queue, limits);
2458+
if (ret) {
2459+
set_capacity(md->disk, old_size);
2460+
old_map = ERR_PTR(ret);
2461+
goto out;
2462+
}
24622463
}
24632464

24642465
/*
@@ -2836,6 +2837,7 @@ static void dm_wq_work(struct work_struct *work)
28362837

28372838
static void dm_queue_flush(struct mapped_device *md)
28382839
{
2840+
clear_bit(DMF_NOFLUSH_SUSPENDING, &md->flags);
28392841
clear_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags);
28402842
smp_mb__after_atomic();
28412843
queue_work(md->wq, &md->work);
@@ -2848,6 +2850,7 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
28482850
{
28492851
struct dm_table *live_map = NULL, *map = ERR_PTR(-EINVAL);
28502852
struct queue_limits limits;
2853+
bool update_limits = true;
28512854
int r;
28522855

28532856
mutex_lock(&md->suspend_lock);
@@ -2856,28 +2859,39 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
28562859
if (!dm_suspended_md(md))
28572860
goto out;
28582861

2862+
/*
2863+
* To avoid a potential deadlock locking the queue limits, disallow
2864+
* updating the queue limits during a table swap, when updating an
2865+
* immutable request-based dm device (dm-multipath) during a noflush
2866+
* suspend. It is userspace's responsibility to make sure that the new
2867+
* table uses the same limits as the existing table, if it asks for a
2868+
* noflush suspend.
2869+
*/
2870+
if (dm_request_based(md) && md->immutable_target &&
2871+
__noflush_suspending(md))
2872+
update_limits = false;
28592873
/*
28602874
* If the new table has no data devices, retain the existing limits.
28612875
* This helps multipath with queue_if_no_path if all paths disappear,
28622876
* then new I/O is queued based on these limits, and then some paths
28632877
* reappear.
28642878
*/
2865-
if (dm_table_has_no_data_devices(table)) {
2879+
else if (dm_table_has_no_data_devices(table)) {
28662880
live_map = dm_get_live_table_fast(md);
28672881
if (live_map)
28682882
limits = md->queue->limits;
28692883
dm_put_live_table_fast(md);
28702884
}
28712885

2872-
if (!live_map) {
2886+
if (update_limits && !live_map) {
28732887
r = dm_calculate_queue_limits(table, &limits);
28742888
if (r) {
28752889
map = ERR_PTR(r);
28762890
goto out;
28772891
}
28782892
}
28792893

2880-
map = __bind(md, table, &limits);
2894+
map = __bind(md, table, update_limits ? &limits : NULL);
28812895
dm_issue_global_event();
28822896

28832897
out:
@@ -2930,7 +2944,6 @@ static int __dm_suspend(struct mapped_device *md, struct dm_table *map,
29302944

29312945
/*
29322946
* DMF_NOFLUSH_SUSPENDING must be set before presuspend.
2933-
* This flag is cleared before dm_suspend returns.
29342947
*/
29352948
if (noflush)
29362949
set_bit(DMF_NOFLUSH_SUSPENDING, &md->flags);
@@ -2993,8 +3006,6 @@ static int __dm_suspend(struct mapped_device *md, struct dm_table *map,
29933006
if (!r)
29943007
set_bit(dmf_suspended_flag, &md->flags);
29953008

2996-
if (noflush)
2997-
clear_bit(DMF_NOFLUSH_SUSPENDING, &md->flags);
29983009
if (map)
29993010
synchronize_srcu(&md->io_barrier);
30003011

0 commit comments

Comments
 (0)