Skip to content

Commit 0bc85bd

Browse files
committed
dm cache: Fix ABBA deadlock between shrink_slab and dm_cache_metadata_abort
Bugzilla: https://bugzilla.redhat.com/2162536 Upstream Status: kernel/git/torvalds/linux.git commit 352b837 Author: Mike Snitzer <snitzer@kernel.org> Date: Wed Nov 30 13:26:32 2022 -0500 dm cache: Fix ABBA deadlock between shrink_slab and dm_cache_metadata_abort Same ABBA deadlock pattern fixed in commit 4b60f452ec51 ("dm thin: Fix ABBA deadlock between shrink_slab and dm_pool_abort_metadata") to DM-cache's metadata. Reported-by: Zhihao Cheng <chengzhihao1@huawei.com> Cc: stable@vger.kernel.org Fixes: 028ae9f ("dm cache: add fail io mode and needs_check flag") Signed-off-by: Mike Snitzer <snitzer@kernel.org> Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
1 parent 6f15a1c commit 0bc85bd

File tree

1 file changed

+47
-7
lines changed

1 file changed

+47
-7
lines changed

drivers/md/dm-cache-metadata.c

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -551,11 +551,13 @@ static int __create_persistent_data_objects(struct dm_cache_metadata *cmd,
551551
return r;
552552
}
553553

554-
static void __destroy_persistent_data_objects(struct dm_cache_metadata *cmd)
554+
static void __destroy_persistent_data_objects(struct dm_cache_metadata *cmd,
555+
bool destroy_bm)
555556
{
556557
dm_sm_destroy(cmd->metadata_sm);
557558
dm_tm_destroy(cmd->tm);
558-
dm_block_manager_destroy(cmd->bm);
559+
if (destroy_bm)
560+
dm_block_manager_destroy(cmd->bm);
559561
}
560562

561563
typedef unsigned long (*flags_mutator)(unsigned long);
@@ -826,7 +828,7 @@ static struct dm_cache_metadata *lookup_or_open(struct block_device *bdev,
826828
cmd2 = lookup(bdev);
827829
if (cmd2) {
828830
mutex_unlock(&table_lock);
829-
__destroy_persistent_data_objects(cmd);
831+
__destroy_persistent_data_objects(cmd, true);
830832
kfree(cmd);
831833
return cmd2;
832834
}
@@ -874,7 +876,7 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd)
874876
mutex_unlock(&table_lock);
875877

876878
if (!cmd->fail_io)
877-
__destroy_persistent_data_objects(cmd);
879+
__destroy_persistent_data_objects(cmd, true);
878880
kfree(cmd);
879881
}
880882
}
@@ -1807,14 +1809,52 @@ int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result)
18071809

18081810
int dm_cache_metadata_abort(struct dm_cache_metadata *cmd)
18091811
{
1810-
int r;
1812+
int r = -EINVAL;
1813+
struct dm_block_manager *old_bm = NULL, *new_bm = NULL;
1814+
1815+
/* fail_io is double-checked with cmd->root_lock held below */
1816+
if (unlikely(cmd->fail_io))
1817+
return r;
1818+
1819+
/*
1820+
* Replacement block manager (new_bm) is created and old_bm destroyed outside of
1821+
* cmd root_lock to avoid ABBA deadlock that would result (due to life-cycle of
1822+
* shrinker associated with the block manager's bufio client vs cmd root_lock).
1823+
* - must take shrinker_rwsem without holding cmd->root_lock
1824+
*/
1825+
new_bm = dm_block_manager_create(cmd->bdev, DM_CACHE_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
1826+
CACHE_MAX_CONCURRENT_LOCKS);
18111827

18121828
WRITE_LOCK(cmd);
1813-
__destroy_persistent_data_objects(cmd);
1814-
r = __create_persistent_data_objects(cmd, false);
1829+
if (cmd->fail_io) {
1830+
WRITE_UNLOCK(cmd);
1831+
goto out;
1832+
}
1833+
1834+
__destroy_persistent_data_objects(cmd, false);
1835+
old_bm = cmd->bm;
1836+
if (IS_ERR(new_bm)) {
1837+
DMERR("could not create block manager during abort");
1838+
cmd->bm = NULL;
1839+
r = PTR_ERR(new_bm);
1840+
goto out_unlock;
1841+
}
1842+
1843+
cmd->bm = new_bm;
1844+
r = __open_or_format_metadata(cmd, false);
1845+
if (r) {
1846+
cmd->bm = NULL;
1847+
goto out_unlock;
1848+
}
1849+
new_bm = NULL;
1850+
out_unlock:
18151851
if (r)
18161852
cmd->fail_io = true;
18171853
WRITE_UNLOCK(cmd);
1854+
dm_block_manager_destroy(old_bm);
1855+
out:
1856+
if (new_bm && !IS_ERR(new_bm))
1857+
dm_block_manager_destroy(new_bm);
18181858

18191859
return r;
18201860
}

0 commit comments

Comments
 (0)