[PATCH v7] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
From: Tetsuo Handa
Date: Fri Oct 16 2026 - 12:26:54 EST
Commit b849dd84b6cc ("bdev: Reduce time holding bd_mutex in sync in
blkdev_close()") introduced a sync_blockdev() call immediately before
holding disk->open_mutex. But this check was racy and best-effort basis.
If more than one thread concurrently called bdev_release() and saw
atomic_read(&bdev->bd_openers) > 1, the thread which actually hit
the atomic_dec_and_test(&bdev->bd_openers) condition will call
sync_blockdev() from blkdev_flush_mapping() (which could take several
minutes) with disk->open_mutex held.
Kill this race window and make sync_blockdev() always be called before
holding disk->open_mutex, by using a counter that tracks how many threads
are calling sync_blockdev() from bdev_release() without disk->open_mutex
held.
Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
---
block/bdev.c | 18 +++++++++++++++---
include/linux/blk_types.h | 1 +
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/block/bdev.c b/block/bdev.c
index cd8323083740..cc9f66fd9d6b 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1150,6 +1150,17 @@ static inline void bd_yield_claim(struct file *bdev_file)
bd_end_claim(bdev, holder);
}
+static inline void bd_early_sync(struct gendisk *disk, struct block_device *bdev)
+{
+ if (atomic_read(&bdev->bd_openers) == 1 + bdev->bd_closers) {
+ bdev->bd_closers++;
+ mutex_unlock(&disk->open_mutex);
+ sync_blockdev(bdev);
+ mutex_lock(&disk->open_mutex);
+ bdev->bd_closers--;
+ }
+}
+
void bdev_release(struct file *bdev_file)
{
struct block_device *bdev = file_bdev(bdev_file);
@@ -1167,10 +1178,11 @@ void bdev_release(struct file *bdev_file)
* of the world and we want to avoid long (could be several minute)
* syncs while holding the mutex.
*/
- if (atomic_read(&bdev->bd_openers) == 1)
- sync_blockdev(bdev);
-
mutex_lock(&disk->open_mutex);
+ bd_early_sync(disk, bdev);
+ if (bdev_is_partition(bdev))
+ bd_early_sync(disk, bdev_whole(bdev));
+
bdev_yield_write_access(bdev_file);
if (holder)
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 98e21b4cbf32..04d378818c76 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -74,6 +74,7 @@ struct block_device {
#ifdef CONFIG_SECURITY
void *bd_security;
#endif
+ int bd_closers;
/*
* keep this out-of-line as it's both big and not needed in the fast
* path
--
2.56.0