mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
@ 2026-10-14 12:02 Tetsuo Handa
  2026-10-14 13:07 ` [PATCH v2] " Tetsuo Handa
  0 siblings, 1 reply; 8+ messages in thread
From: Tetsuo Handa @ 2026-10-14 12:02 UTC (permalink / raw)
  To: LKML

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 process concurrently called bdev_release() and saw
atomic_read(&bdev->bd_openers) > 1, the process 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 marker that checks whether lockless
sync_blockdev() has raced.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 block/bdev.c              | 41 +++++++++++++++++++++++++++++++++++++--
 include/linux/blk_types.h |  1 +
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index cd8323083740..07e4cc0e5c2a 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1167,10 +1167,47 @@ 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)
+ retry:
+	mutex_lock(&disk->open_mutex);
+	if (bdev_is_partition(bdev) &&
+	    atomic_read(&bdev_whole(bdev)->bd_openers) == 1) {
+		/*
+		 * This "bdev" is a partition, and blkdev_put_whole() from
+		 * blkdev_put_part() will trigger blkdev_flush_mapping() on
+		 * the whole disk. Therefore, we need to sync the whole disk.
+		 */
+		bdev->bdev_syncer = current;
+		mutex_unlock(&disk->open_mutex);
+		sync_blockdev(bdev_whole(bdev));
+		mutex_lock(&disk->open_mutex);
+		/* Need to retry if we raced. */
+		if (bdev->bdev_syncer != current)
+			goto retry;
+	} else if (atomic_read(&bdev->bd_openers) == 1) {
+		/*
+		 * If "bdev" is a whole disk, blkdev_put_whole() will trigger
+		 * blkdev_flush_mapping() on this disk. Therefore, sync this
+		 * "bdev".
+		 *
+		 * If "bdev" is a partition, blkdev_put_part() will trigger
+		 * blkdev_flush_mapping() on this partition, but
+		 * blkdev_put_whole() from blkdev_put_part() won't trigger
+		 * blkdev_flush_mapping() on the whole disk because we chedked
+		 * bdev_whole(bdev)->bd_openers != 1. Therefore, we need to
+		 * sync only this "bdev".
+		 */
+		bdev->bdev_syncer = current;
+		mutex_unlock(&disk->open_mutex);
 		sync_blockdev(bdev);
+		mutex_lock(&disk->open_mutex);
+		/* Need to retry if we raced. */
+		if (bdev->bdev_syncer != current)
+			goto retry;
+	} else {
+		/* Mark sync_blockdev() being possibly raced. */
+		bdev->bdev_syncer = NULL;
+	}
 
-	mutex_lock(&disk->open_mutex);
 	bdev_yield_write_access(bdev_file);
 
 	if (holder)
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 98e21b4cbf32..d79bb4b6100f 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
+	struct task_struct	*bdev_syncer; /* Not refcounted. */
 	/*
 	 * keep this out-of-line as it's both big and not needed in the fast
 	 * path
-- 
2.56.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
  2026-10-14 12:02 [PATCH] block: avoid long sync_blockdev() under disk->open_mutex during parallel close Tetsuo Handa
@ 2026-10-14 13:07 ` Tetsuo Handa
  2026-10-14 14:12   ` [PATCH v3] " Tetsuo Handa
  0 siblings, 1 reply; 8+ messages in thread
From: Tetsuo Handa @ 2026-10-14 13:07 UTC (permalink / raw)
  To: LKML

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 process concurrently called bdev_release() and saw
atomic_read(&bdev->bd_openers) > 1, the process 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 marker that checks whether lockless
sync_blockdev() has raced.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
Changes in v2:
  - Fix errors mentioned by sashiko.

 block/bdev.c              | 43 +++++++++++++++++++++++++++++++++++++--
 include/linux/blk_types.h |  1 +
 2 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index cd8323083740..0b24c252206c 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1167,10 +1167,49 @@ 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)
+	mutex_lock(&disk->open_mutex);
+ retry:
+	if (bdev_is_partition(bdev) &&
+	    atomic_read(&bdev_whole(bdev)->bd_openers) == 1) {
+		/*
+		 * This "bdev" is a partition, and blkdev_put_whole() from
+		 * blkdev_put_part() will trigger blkdev_flush_mapping() on
+		 * the whole disk. Therefore, we need to sync both this
+		 * partition and the whole disk.
+		 */
+		bdev->bdev_syncer = current;
+		mutex_unlock(&disk->open_mutex);
 		sync_blockdev(bdev);
+		sync_blockdev(bdev_whole(bdev));
+		mutex_lock(&disk->open_mutex);
+		/* Need to retry if we raced. */
+		if (bdev->bdev_syncer != current)
+			goto retry;
+	} else if (atomic_read(&bdev->bd_openers) == 1) {
+		/*
+		 * If "bdev" is a whole disk, blkdev_put_whole() will trigger
+		 * blkdev_flush_mapping() on this disk. Therefore, sync this
+		 * "bdev".
+		 *
+		 * If "bdev" is a partition, blkdev_put_part() will trigger
+		 * blkdev_flush_mapping() on this partition, but
+		 * blkdev_put_whole() from blkdev_put_part() won't trigger
+		 * blkdev_flush_mapping() on the whole disk because we checked
+		 * bdev_whole(bdev)->bd_openers != 1. Therefore, we need to
+		 * sync only this "bdev".
+		 */
+		bdev->bdev_syncer = current;
+		mutex_unlock(&disk->open_mutex);
+		sync_blockdev(bdev);
+		mutex_lock(&disk->open_mutex);
+		/* Need to retry if we raced. */
+		if (bdev->bdev_syncer != current)
+			goto retry;
+	} else {
+		/* Mark sync_blockdev() being possibly raced. */
+		bdev->bdev_syncer = NULL;
+	}
 
-	mutex_lock(&disk->open_mutex);
 	bdev_yield_write_access(bdev_file);
 
 	if (holder)
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 98e21b4cbf32..d79bb4b6100f 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
+	struct task_struct	*bdev_syncer; /* Not refcounted. */
 	/*
 	 * keep this out-of-line as it's both big and not needed in the fast
 	 * path
-- 
2.56.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
  2026-10-14 13:07 ` [PATCH v2] " Tetsuo Handa
@ 2026-10-14 14:12   ` Tetsuo Handa
  2026-10-14 16:21     ` [PATCH v4] " Tetsuo Handa
  0 siblings, 1 reply; 8+ messages in thread
From: Tetsuo Handa @ 2026-10-14 14:12 UTC (permalink / raw)
  To: LKML

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 process concurrently called bdev_release() and saw
atomic_read(&bdev->bd_openers) > 1, the process 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 marker that checks whether lockless
sync_blockdev() has raced.

Assisted-by: Gemini-Pro
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 block/bdev.c              | 35 +++++++++++++++++++++++++++++++++--
 include/linux/blk_types.h |  1 +
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index cd8323083740..c71dacd0c2b1 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1155,6 +1155,7 @@ void bdev_release(struct file *bdev_file)
 	struct block_device *bdev = file_bdev(bdev_file);
 	void *holder = bdev_file->private_data;
 	struct gendisk *disk = bdev->bd_disk;
+	struct block_device *whole;
 
 	/* We failed to open that block device. */
 	if (IS_ERR(holder))
@@ -1167,10 +1168,40 @@ 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)
+	mutex_lock(&disk->open_mutex);
+	whole = bdev_is_partition(bdev) ? bdev_whole(bdev) : NULL;
+ retry:
+	if ((whole && atomic_read(&whole->bd_openers) == 1) ||
+	    atomic_read(&bdev->bd_openers) == 1) {
+		bdev->bdev_syncer = current;
+		if (whole)
+			whole->bdev_syncer = current;
+		mutex_unlock(&disk->open_mutex);
 		sync_blockdev(bdev);
+		if (whole)
+			sync_blockdev(whole);
+		mutex_lock(&disk->open_mutex);
+		if (bdev->bdev_syncer != current ||
+		    (whole && whole->bdev_syncer != current))
+			goto retry;
+	} else {
+		/*
+		 * Mark sync_blockdev() being possibly raced.
+		 *
+		 * Theoretically, it is possible for a malicious thread to
+		 * trap a victim thread in an infinite "goto retry;" loop
+		 * by repeatedly calling open() and close(). In practice,
+		 * however, trapping a victim thread in such a loop is
+		 * extremely difficult. This is because the bd_opener count
+		 * remains elevated between open() and close() calls,
+		 * meaning the victim thread will observe bd_opener > 1
+		 * and skip synchronization within just a few retries.
+		 */
+		bdev->bdev_syncer = NULL;
+		if (whole)
+			whole->bdev_syncer = NULL;
+	}
 
-	mutex_lock(&disk->open_mutex);
 	bdev_yield_write_access(bdev_file);
 
 	if (holder)
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 98e21b4cbf32..d79bb4b6100f 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
+	struct task_struct	*bdev_syncer; /* Not refcounted. */
 	/*
 	 * keep this out-of-line as it's both big and not needed in the fast
 	 * path
-- 
2.56.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
  2026-10-14 14:12   ` [PATCH v3] " Tetsuo Handa
@ 2026-10-14 16:21     ` Tetsuo Handa
  2026-10-15 10:25       ` [PATCH v5] " Tetsuo Handa
  0 siblings, 1 reply; 8+ messages in thread
From: Tetsuo Handa @ 2026-10-14 16:21 UTC (permalink / raw)
  To: LKML

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 process concurrently called bdev_release() and saw
atomic_read(&bdev->bd_openers) > 1, the process 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 marker that checks whether lockless
sync_blockdev() has raced.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 block/bdev.c              | 37 ++++++++++++++++++++++++++++++++++---
 include/linux/blk_types.h |  1 +
 2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index cd8323083740..57f0f57da8c1 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1167,10 +1167,41 @@ 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);
+ retry:
+	if (bdev_is_partition(bdev)) {
+		if (atomic_read(&bdev->bd_openers) == 1) {
+			bdev->bdev_syncer = current;
+			mutex_unlock(&disk->open_mutex);
+			sync_blockdev(bdev);
+			/*
+			 * Always sync the whole disk despite we need to sync
+			 * only when we're the last one, for we can't serialize
+			 * concurrent closing of multiple partitions on the
+			 * same disk using open_mutex.
+			 */
+			sync_blockdev(bdev_whole(bdev));
+			mutex_lock(&disk->open_mutex);
+			if (bdev->bdev_syncer != current)
+				goto retry;
+		} else {
+			/* Mark sync_blockdev() being possibly raced. */
+			bdev->bdev_syncer = NULL;
+		}
+	} else {
+		if (atomic_read(&bdev->bd_openers) == 1) {
+			bdev->bdev_syncer = current;
+			mutex_unlock(&disk->open_mutex);
+			sync_blockdev(bdev);
+			mutex_lock(&disk->open_mutex);
+			if (bdev->bdev_syncer != current)
+				goto retry;
+		} else {
+			/* Mark sync_blockdev() being possibly raced. */
+			bdev->bdev_syncer = NULL;
+		}
+	}
+
 	bdev_yield_write_access(bdev_file);
 
 	if (holder)
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 98e21b4cbf32..d79bb4b6100f 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
+	struct task_struct	*bdev_syncer; /* Not refcounted. */
 	/*
 	 * keep this out-of-line as it's both big and not needed in the fast
 	 * path
-- 
2.56.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v5] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
  2026-10-14 16:21     ` [PATCH v4] " Tetsuo Handa
@ 2026-10-15 10:25       ` Tetsuo Handa
  2026-10-15 11:18         ` [PATCH v6] " Tetsuo Handa
  0 siblings, 1 reply; 8+ messages in thread
From: Tetsuo Handa @ 2026-10-15 10:25 UTC (permalink / raw)
  To: LKML

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@I-love.SAKURA.ne.jp>
---
 block/bdev.c              | 17 +++++++++++++++--
 include/linux/blk_types.h |  1 +
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index cd8323083740..309c49842ddc 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1167,10 +1167,23 @@ 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)
+	mutex_lock(&disk->open_mutex);
+	if (atomic_read(&bdev->bd_openers) == 1 + bdev->bd_closers) {
+		bdev->bd_closers++;
+		mutex_unlock(&disk->open_mutex);
 		sync_blockdev(bdev);
+		/*
+		 * Always sync the whole disk when closing a partition despite
+		 * we need to sync the whole disk only when we're also closing
+		 * the whole disk, for we can't serialize concurrent closing of
+		 * partitions using disk->open_mutex.
+		 */
+		if (bdev_is_partition(bdev))
+			sync_blockdev(bdev_whole(bdev));
+		mutex_lock(&disk->open_mutex);
+		bdev->bd_closers--;
+	}
 
-	mutex_lock(&disk->open_mutex);
 	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


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v6] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
  2026-10-15 10:25       ` [PATCH v5] " Tetsuo Handa
@ 2026-10-15 11:18         ` Tetsuo Handa
  2026-10-16 15:40           ` [PATCH v7] " Tetsuo Handa
  0 siblings, 1 reply; 8+ messages in thread
From: Tetsuo Handa @ 2026-10-15 11:18 UTC (permalink / raw)
  To: LKML

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@I-love.SAKURA.ne.jp>
---
 block/bdev.c              | 24 ++++++++++++++++++++++--
 include/linux/blk_types.h |  1 +
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index cd8323083740..cc16f0d7d496 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1167,10 +1167,30 @@ 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)
+	mutex_lock(&disk->open_mutex);
+	if (atomic_read(&bdev->bd_openers) == 1 + bdev->bd_closers) {
+		struct block_device *whole = bdev_is_partition(bdev) ?
+			bdev_whole(bdev) : NULL;
+
+		bdev->bd_closers++;
+		if (whole)
+			whole->bd_closers++;
+		mutex_unlock(&disk->open_mutex);
 		sync_blockdev(bdev);
+		/*
+		 * Always sync the whole disk when closing a partition despite
+		 * we need to sync the whole disk only when we're also closing
+		 * the whole disk, for we can't serialize concurrent closing of
+		 * partitions using disk->open_mutex.
+		 */
+		if (whole)
+			sync_blockdev(whole);
+		mutex_lock(&disk->open_mutex);
+		bdev->bd_closers--;
+		if (whole)
+			whole->bd_closers--;
+	}
 
-	mutex_lock(&disk->open_mutex);
 	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


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v7] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
  2026-10-15 11:18         ` [PATCH v6] " Tetsuo Handa
@ 2026-10-16 15:40           ` Tetsuo Handa
  2026-10-19  2:29             ` kernel test robot
  0 siblings, 1 reply; 8+ messages in thread
From: Tetsuo Handa @ 2026-10-16 15:40 UTC (permalink / raw)
  To: LKML

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@I-love.SAKURA.ne.jp>
---
 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

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v7] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
  2026-10-16 15:40           ` [PATCH v7] " Tetsuo Handa
@ 2026-10-19  2:29             ` kernel test robot
  0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2026-10-19  2:29 UTC (permalink / raw)
  To: Tetsuo Handa, LKML; +Cc: llvm, oe-kbuild-all

Hi Tetsuo,

kernel test robot noticed the following build warnings:

[auto build test WARNING on axboe/for-next]
[also build test WARNING on linus/master v7.3 next-20261016]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Tetsuo-Handa/block-avoid-long-sync_blockdev-under-disk-open_mutex-during-parallel-close/20261017-004014
base:   https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git for-next
patch link:    https://lore.kernel.org/r/6efed2d0-fe66-4d29-876b-fd3b74abaa70%40I-love.SAKURA.ne.jp
patch subject: [PATCH v7] block: avoid long sync_blockdev() under disk->open_mutex during parallel close
config: um-x86_64_defconfig (https://download.01.org/0day-ci/archive/20261019/202610191027.uw2yfQhY-lkp@intel.com/config)
compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project 6719b9e31d6a93099612c474eec76d9f41d0275a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261019/202610191027.uw2yfQhY-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202610191027.uw2yfQhY-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> block/bdev.c:1157:3: warning: releasing mutex 'disk->open_mutex' that was not held [-Wthread-safety-analysis]
    1157 |                 mutex_unlock(&disk->open_mutex);
         |                 ^
>> block/bdev.c:1162:1: warning: mutex 'disk->open_mutex' is not held on every path through here [-Wthread-safety-analysis]
    1162 | }
         | ^
   block/bdev.c:1159:3: note: mutex acquired here
    1159 |                 mutex_lock(&disk->open_mutex);
         |                 ^
   2 warnings generated.


vim +1157 block/bdev.c

  1152	
  1153	static inline void bd_early_sync(struct gendisk *disk, struct block_device *bdev)
  1154	{
  1155		if (atomic_read(&bdev->bd_openers) == 1 + bdev->bd_closers) {
  1156			bdev->bd_closers++;
> 1157			mutex_unlock(&disk->open_mutex);
  1158			sync_blockdev(bdev);
  1159			mutex_lock(&disk->open_mutex);
  1160			bdev->bd_closers--;
  1161		}
> 1162	}
  1163	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-19  2:30 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-14 12:02 [PATCH] block: avoid long sync_blockdev() under disk->open_mutex during parallel close Tetsuo Handa
2026-10-14 13:07 ` [PATCH v2] " Tetsuo Handa
2026-10-14 14:12   ` [PATCH v3] " Tetsuo Handa
2026-10-14 16:21     ` [PATCH v4] " Tetsuo Handa
2026-10-15 10:25       ` [PATCH v5] " Tetsuo Handa
2026-10-15 11:18         ` [PATCH v6] " Tetsuo Handa
2026-10-16 15:40           ` [PATCH v7] " Tetsuo Handa
2026-10-19  2:29             ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®