Skip to content

Commit fd14641

Browse files
jankarabrauner
authored andcommitted
fs: Avoid grabbing sb->s_umount under bdev->bd_holder_lock
The implementation of bdev holder operations such as fs_bdev_mark_dead() and fs_bdev_sync() grab sb->s_umount semaphore under bdev->bd_holder_lock. This is problematic because it leads to disk->open_mutex -> sb->s_umount lock ordering which is counterintuitive (usually we grab higher level (e.g. filesystem) locks first and lower level (e.g. block layer) locks later) and indeed makes lockdep complain about possible locking cycles whenever we open a block device while holding sb->s_umount semaphore. Implement a function bdev_super_lock_shared() which safely transitions from holding bdev->bd_holder_lock to holding sb->s_umount on alive superblock without introducing the problematic lock dependency. We use this function fs_bdev_sync() and fs_bdev_mark_dead(). Signed-off-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20231018152924.3858-1-jack@suse.cz Link: https://lore.kernel.org/r/20231017184823.1383356-1-hch@lst.de Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 6306ff3 commit fd14641

3 files changed

Lines changed: 38 additions & 22 deletions

File tree

block/bdev.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,9 +1012,10 @@ void bdev_mark_dead(struct block_device *bdev, bool surprise)
10121012
mutex_lock(&bdev->bd_holder_lock);
10131013
if (bdev->bd_holder_ops && bdev->bd_holder_ops->mark_dead)
10141014
bdev->bd_holder_ops->mark_dead(bdev, surprise);
1015-
else
1015+
else {
1016+
mutex_unlock(&bdev->bd_holder_lock);
10161017
sync_blockdev(bdev);
1017-
mutex_unlock(&bdev->bd_holder_lock);
1018+
}
10181019

10191020
invalidate_bdev(bdev);
10201021
}

block/ioctl.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,10 @@ static int blkdev_flushbuf(struct block_device *bdev, unsigned cmd,
370370
mutex_lock(&bdev->bd_holder_lock);
371371
if (bdev->bd_holder_ops && bdev->bd_holder_ops->sync)
372372
bdev->bd_holder_ops->sync(bdev);
373-
else
373+
else {
374+
mutex_unlock(&bdev->bd_holder_lock);
374375
sync_blockdev(bdev);
375-
mutex_unlock(&bdev->bd_holder_lock);
376+
}
376377

377378
invalidate_bdev(bdev);
378379
return 0;

fs/super.c

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,32 +1419,47 @@ EXPORT_SYMBOL(sget_dev);
14191419

14201420
#ifdef CONFIG_BLOCK
14211421
/*
1422-
* Lock a super block that the callers holds a reference to.
1422+
* Lock the superblock that is holder of the bdev. Returns the superblock
1423+
* pointer if we successfully locked the superblock and it is alive. Otherwise
1424+
* we return NULL and just unlock bdev->bd_holder_lock.
14231425
*
1424-
* The caller needs to ensure that the super_block isn't being freed while
1425-
* calling this function, e.g. by holding a lock over the call to this function
1426-
* and the place that clears the pointer to the superblock used by this function
1427-
* before freeing the superblock.
1426+
* The function must be called with bdev->bd_holder_lock and releases it.
14281427
*/
1429-
static bool super_lock_shared_active(struct super_block *sb)
1428+
static struct super_block *bdev_super_lock_shared(struct block_device *bdev)
1429+
__releases(&bdev->bd_holder_lock)
14301430
{
1431-
bool born = super_lock_shared(sb);
1431+
struct super_block *sb = bdev->bd_holder;
1432+
bool born;
1433+
1434+
lockdep_assert_held(&bdev->bd_holder_lock);
1435+
lockdep_assert_not_held(&sb->s_umount);
1436+
1437+
/* Make sure sb doesn't go away from under us */
1438+
spin_lock(&sb_lock);
1439+
sb->s_count++;
1440+
spin_unlock(&sb_lock);
1441+
mutex_unlock(&bdev->bd_holder_lock);
14321442

1443+
born = super_lock_shared(sb);
14331444
if (!born || !sb->s_root || !(sb->s_flags & SB_ACTIVE)) {
14341445
super_unlock_shared(sb);
1435-
return false;
1446+
put_super(sb);
1447+
return NULL;
14361448
}
1437-
return true;
1449+
/*
1450+
* The superblock is active and we hold s_umount, we can drop our
1451+
* temporary reference now.
1452+
*/
1453+
put_super(sb);
1454+
return sb;
14381455
}
14391456

14401457
static void fs_bdev_mark_dead(struct block_device *bdev, bool surprise)
14411458
{
1442-
struct super_block *sb = bdev->bd_holder;
1443-
1444-
/* bd_holder_lock ensures that the sb isn't freed */
1445-
lockdep_assert_held(&bdev->bd_holder_lock);
1459+
struct super_block *sb;
14461460

1447-
if (!super_lock_shared_active(sb))
1461+
sb = bdev_super_lock_shared(bdev);
1462+
if (!sb)
14481463
return;
14491464

14501465
if (!surprise)
@@ -1459,11 +1474,10 @@ static void fs_bdev_mark_dead(struct block_device *bdev, bool surprise)
14591474

14601475
static void fs_bdev_sync(struct block_device *bdev)
14611476
{
1462-
struct super_block *sb = bdev->bd_holder;
1463-
1464-
lockdep_assert_held(&bdev->bd_holder_lock);
1477+
struct super_block *sb;
14651478

1466-
if (!super_lock_shared_active(sb))
1479+
sb = bdev_super_lock_shared(bdev);
1480+
if (!sb)
14671481
return;
14681482
sync_filesystem(sb);
14691483
super_unlock_shared(sb);

0 commit comments

Comments
 (0)