From 1df0978eca28d2883ac746ce2e1046f26ce47133 Mon Sep 17 00:00:00 2001 From: Li Lingfeng Date: Fri, 7 Jul 2023 14:51:13 +0800 Subject: [PATCH 1/4] Revert "dm: make sure dm_table is binded before queue request" hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7FI78 -------------------------------- This reverts commit 90d1a836f017faf27c24265773171997485075ce. It's not proper to just abort IO when the map is not ready. So revert this and requeue IO to keep consistent with the community. Signed-off-by: Li Lingfeng (cherry picked from commit bc4c0996c59752ff9747204836e0be289ddf1ea0) --- drivers/md/dm-rq.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c index 8980f129b31f..0fe032147c09 100644 --- a/drivers/md/dm-rq.c +++ b/drivers/md/dm-rq.c @@ -500,15 +500,8 @@ static blk_status_t dm_mq_queue_rq(struct blk_mq_hw_ctx *hctx, if (unlikely(!ti)) { int srcu_idx; - struct dm_table *map; - - map = dm_get_live_table(md, &srcu_idx); - if (!map) { - DMERR_LIMIT("%s: mapping table unavailable, erroring io", - dm_device_name(md)); - dm_put_live_table(md, srcu_idx); - return BLK_STS_IOERR; - } + struct dm_table *map = dm_get_live_table(md, &srcu_idx); + ti = dm_table_find_target(map, 0); dm_put_live_table(md, srcu_idx); } -- Gitee From d328b14769fbf4241d7ac9c54122cb06239233af Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Fri, 7 Jul 2023 14:51:14 +0800 Subject: [PATCH 2/4] dm: requeue IO if mapping table not yet available mainline inclusion from mainline-v5.18-rc1 commit fa247089de9936a46e290d4724cb5f0b845600f5 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7FI78 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v6.4-rc7&id=fa247089de9936a46e290d4724cb5f0b845600f5 ---------------------------------------- Update both bio-based and request-based DM to requeue IO if the mapping table not available. This race of IO being submitted before the DM device ready is so narrow, yet possible for initial table load given that the DM device's request_queue is created prior, that it best to requeue IO to handle this unlikely case. Reported-by: Zhang Yi Signed-off-by: Mike Snitzer Signed-off-by: Li Lingfeng (cherry picked from commit e50475d3ba17d941f9692bdab477c3cacc0f42f5) --- drivers/md/dm-rq.c | 7 ++++++- drivers/md/dm.c | 11 +++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c index 0fe032147c09..31b6cc71ee96 100644 --- a/drivers/md/dm-rq.c +++ b/drivers/md/dm-rq.c @@ -500,8 +500,13 @@ static blk_status_t dm_mq_queue_rq(struct blk_mq_hw_ctx *hctx, if (unlikely(!ti)) { int srcu_idx; - struct dm_table *map = dm_get_live_table(md, &srcu_idx); + struct dm_table *map; + map = dm_get_live_table(md, &srcu_idx); + if (unlikely(!map)) { + dm_put_live_table(md, srcu_idx); + return BLK_STS_RESOURCE; + } ti = dm_table_find_target(map, 0); dm_put_live_table(md, srcu_idx); } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 1cb2a84f2403..3649ee4d9000 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1684,15 +1684,10 @@ static blk_qc_t dm_submit_bio(struct bio *bio) struct dm_table *map; map = dm_get_live_table(md, &srcu_idx); - if (unlikely(!map)) { - DMERR_LIMIT("%s: mapping table unavailable, erroring io", - dm_device_name(md)); - bio_io_error(bio); - goto out; - } - /* If suspended, queue this IO for later */ - if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags))) { + /* If suspended, or map not yet available, queue this IO for later */ + if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) || + unlikely(!map)) { if (bio->bi_opf & REQ_NOWAIT) bio_wouldblock_error(bio); else if (bio->bi_opf & REQ_RAHEAD) -- Gitee From 22030e13ecdc8ced6e9e05facb1de841fd522634 Mon Sep 17 00:00:00 2001 From: Li Lingfeng Date: Fri, 7 Jul 2023 14:51:15 +0800 Subject: [PATCH 3/4] dm: don't lock fs when the map is NULL in process of resume mainline inclusion from mainline-v6.4-rc1 commit 38d11da522aacaa05898c734a1cec86f1e611129 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7FI78 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v6.4&id=38d11da522aacaa05898c734a1cec86f1e611129 ---------------------------------------- Commit fa247089de99 ("dm: requeue IO if mapping table not yet available") added a detection of whether the mapping table is available in the IO submission process. If the mapping table is unavailable, it returns BLK_STS_RESOURCE and requeues the IO. This can lead to the following deadlock problem: dm create mount ioctl(DM_DEV_CREATE_CMD) ioctl(DM_TABLE_LOAD_CMD) do_mount vfs_get_tree ext4_get_tree get_tree_bdev sget_fc alloc_super // got &s->s_umount down_write_nested(&s->s_umount, ...); ext4_fill_super ext4_load_super ext4_read_bh submit_bio // submit and wait io end ioctl(DM_DEV_SUSPEND_CMD) dev_suspend do_resume dm_suspend __dm_suspend lock_fs freeze_bdev get_active_super grab_super // wait for &s->s_umount down_write(&s->s_umount); dm_swap_table __bind // set md->map(can't get here) IO will be continuously requeued while holding the lock since mapping table is NULL. At the same time, mapping table won't be set since the lock is not available. Like request-based DM, bio-based DM also has the same problem. It's not proper to just abort IO if the mapping table not available. So clear DM_SKIP_LOCKFS_FLAG when the mapping table is NULL, this allows the DM table to be loaded and the IO submitted upon resume. Fixes: fa247089de99 ("dm: requeue IO if mapping table not yet available") Cc: stable@vger.kernel.org Signed-off-by: Li Lingfeng Signed-off-by: Mike Snitzer Conflicts: drivers/md/dm-ioctl.c Signed-off-by: Li Lingfeng (cherry picked from commit 14dd9b4da76fc3a61867f4ffcf31642333a27f3b) --- drivers/md/dm-ioctl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 73b7a4e4d9db..6f18d6fdabba 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1059,9 +1059,13 @@ static int do_resume(struct dm_ioctl *param) /* Do we need to load a new map ? */ if (new_map) { + int srcu_idx; + /* Suspend if it isn't already suspended */ - if (param->flags & DM_SKIP_LOCKFS_FLAG) + old_map = dm_get_live_table(md, &srcu_idx); + if (param->flags & DM_SKIP_LOCKFS_FLAG || !old_map) suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; + dm_put_live_table(md, srcu_idx); if (param->flags & DM_NOFLUSH_FLAG) suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; if (!dm_suspended_md(md)) -- Gitee From d464494a87eb7f261b25598a126ff3fd8c3a2d2e Mon Sep 17 00:00:00 2001 From: Li Lingfeng Date: Fri, 7 Jul 2023 14:51:16 +0800 Subject: [PATCH 4/4] dm: don't lock fs when the map is NULL during suspend or resume mainline inclusion from mainline-v6.4-rc8 commit 2760904d895279f87196f0fa9ec570c79fe6a2e4 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7FI78 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v6.4-rc7&id=2760904d895279f87196f0fa9ec570c79fe6a2e4 ---------------------------------------- As described in commit 38d11da522aa ("dm: don't lock fs when the map is NULL in process of resume"), a deadlock may be triggered between do_resume() and do_mount(). This commit preserves the fix from commit 38d11da522aa but moves it to where it also serves to fix a similar deadlock between do_suspend() and do_mount(). It does so, if the active map is NULL, by clearing DM_SUSPEND_LOCKFS_FLAG in dm_suspend() which is called by both do_suspend() and do_resume(). Fixes: 38d11da522aa ("dm: don't lock fs when the map is NULL in process of resume") Signed-off-by: Li Lingfeng Signed-off-by: Mike Snitzer Conflicts: drivers/md/dm-ioctl.c Signed-off-by: Li Lingfeng (cherry picked from commit 0ba9dcd9fae43a2538d056e2571f693119a4016a) --- drivers/md/dm-ioctl.c | 6 +----- drivers/md/dm.c | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 6f18d6fdabba..73b7a4e4d9db 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1059,13 +1059,9 @@ static int do_resume(struct dm_ioctl *param) /* Do we need to load a new map ? */ if (new_map) { - int srcu_idx; - /* Suspend if it isn't already suspended */ - old_map = dm_get_live_table(md, &srcu_idx); - if (param->flags & DM_SKIP_LOCKFS_FLAG || !old_map) + if (param->flags & DM_SKIP_LOCKFS_FLAG) suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; - dm_put_live_table(md, srcu_idx); if (param->flags & DM_NOFLUSH_FLAG) suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; if (!dm_suspended_md(md)) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 3649ee4d9000..3a49fbed974a 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2602,6 +2602,10 @@ int dm_suspend(struct mapped_device *md, unsigned suspend_flags) } map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock)); + if (!map) { + /* avoid deadlock with fs/namespace.c:do_mount() */ + suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; + } r = __dm_suspend(md, map, suspend_flags, TASK_INTERRUPTIBLE, DMF_SUSPENDED); if (r) -- Gitee