Linux block layer
 help / color / mirror / Atom feed
* [PATCH 00/13] Improve the loop driver
@ 2026-08-20 19:57 Bart Van Assche
  2026-08-20 19:57 ` [PATCH 01/13] loop: Fix the code for recursion detection Bart Van Assche
                   ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Hi Jens,

This patch series fixes two bugs and enables lock context analysis in the loop
driver. While I am aware that the merge window is open, I'm posting this patch
series anyway such that reviewing of this patch series can start.

This series of patches is a reworked version of a subset of the series
"[PATCH v2 00/12] Enable lock context analysis in drivers/block/". In this
version Nilay's feedback has been addressed.

Thanks,

Bart.

Bart Van Assche (13):
  loop: Fix the code for recursion detection
  loop: Reorder checks in loop_validate_file()
  loop: Enable context analysis
  loop: Assign a unique lockdep key to each lo_mutex instance
  loop: Add more __must_hold() annotations
  loop: Protect all lo_backing_file accesses with lo->lo_mutex
  loop: Fix race conditions in loop_validate_file()
  loop: Remove memory barriers
  loop: Split loop_change_fd()
  loop: Split loop_configure()
  loop: Remove the "bool global" function argument
  loop: Modify the loop_process_work() calling convention
  loop: Add __guarded_by() annotations

 drivers/block/Makefile |   2 +
 drivers/block/loop.c   | 382 ++++++++++++++++++++++++-----------------
 2 files changed, 230 insertions(+), 154 deletions(-)


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

* [PATCH 01/13] loop: Fix the code for recursion detection
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 02/13] loop: Reorder checks in loop_validate_file() Bart Van Assche
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe
  Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche,
	Theodore Ts'o, stable

The purpose of loop_validate_file() is to prevent that the file descriptor
of an already bound loop device is changed into a file descriptor
associated with a file that depends on the loop device. This must be
prevented because otherwise infinite I/O loops could be triggered and
filesystems involved in this loop would become impossible to unmount.

Fix loop_validate_file() by comparing gendisk pointers instead of dev_t
values.

Cc: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
Fixes: d2ac838e4cd7 ("loop: add recursion validation to LOOP_CHANGE_FD")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 6f12976035b0..36deb08ee463 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -481,13 +481,26 @@ static void loop_update_dio_alignment(struct loop_device *lo)
 	lo->lo_dio_mem_align = SECTOR_SIZE - 1;
 }
 
-static inline int is_loop_device(struct file *file)
+/* Returns the block device that underpins a file.  */
+static inline struct block_device *loop_get_bdev(struct file *file)
 {
-	struct inode *i = file->f_mapping->host;
+	struct inode *inode = file->f_mapping->host;
+
+	if (S_ISBLK(inode->i_mode))
+		return I_BDEV(inode);
+	if (S_ISREG(inode->i_mode) && inode->i_sb)
+		return inode->i_sb->s_bdev;
+	return NULL;
+}
+
+static inline bool is_loop_device(struct file *file)
+{
+	struct block_device *bdev = loop_get_bdev(file);
 
-	return i && S_ISBLK(i->i_mode) && imajor(i) == LOOP_MAJOR;
+	return bdev && bdev->bd_disk->major == LOOP_MAJOR;
 }
 
+/* Returns 0 if and only if @file is not backed by loop device @bdev. */
 static int loop_validate_file(struct file *file, struct block_device *bdev)
 {
 	struct inode	*inode = file->f_mapping->host;
@@ -496,12 +509,13 @@ static int loop_validate_file(struct file *file, struct block_device *bdev)
 	/* Avoid recursion */
 	while (is_loop_device(f)) {
 		struct loop_device *l;
+		struct block_device *f_bdev = loop_get_bdev(f);
 
 		lockdep_assert_held(&loop_validate_mutex);
-		if (f->f_mapping->host->i_rdev == bdev->bd_dev)
+		if (f_bdev->bd_disk == bdev->bd_disk)
 			return -EBADF;
 
-		l = I_BDEV(f->f_mapping->host)->bd_disk->private_data;
+		l = f_bdev->bd_disk->private_data;
 		if (l->lo_state != Lo_bound)
 			return -EINVAL;
 		/* Order wrt setting lo->lo_backing_file in loop_configure(). */

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

* [PATCH 02/13] loop: Reorder checks in loop_validate_file()
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
  2026-08-20 19:57 ` [PATCH 01/13] loop: Fix the code for recursion detection Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 03/13] loop: Enable context analysis Bart Van Assche
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Checking inode attributes after a loop that modifies the file pointer
the inode has been derived from confuses some static analyzers. Hence
swap the loop and the inode attribute check.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 36deb08ee463..4315c36d4042 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -506,6 +506,9 @@ static int loop_validate_file(struct file *file, struct block_device *bdev)
 	struct inode	*inode = file->f_mapping->host;
 	struct file	*f = file;
 
+	if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
+		return -EINVAL;
+
 	/* Avoid recursion */
 	while (is_loop_device(f)) {
 		struct loop_device *l;
@@ -522,8 +525,6 @@ static int loop_validate_file(struct file *file, struct block_device *bdev)
 		rmb();
 		f = l->lo_backing_file;
 	}
-	if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
-		return -EINVAL;
 	return 0;
 }
 

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

* [PATCH 03/13] loop: Enable context analysis
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
  2026-08-20 19:57 ` [PATCH 01/13] loop: Fix the code for recursion detection Bart Van Assche
  2026-08-20 19:57 ` [PATCH 02/13] loop: Reorder checks in loop_validate_file() Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 04/13] loop: Assign a unique lockdep key to each lo_mutex instance Bart Van Assche
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Let the compiler verify __must_hold(), __guarded_by() etc. Suppress the
warnings reported for loop_global_{,un}_lock*() by adding a
__context_unsafe() annotation. Both __context_unsafe() annotations will be
removed by a later patch.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/Makefile | 2 ++
 drivers/block/loop.c   | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 2d8096eb8cdf..d4555949cc3c 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -6,6 +6,8 @@
 # Rewritten to use lists instead of if-statements.
 # 
 
+CONTEXT_ANALYSIS_loop.o := y
+
 # needed for trace events
 ccflags-y				+= -I$(src)
 
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 4315c36d4042..638d97293a79 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -108,6 +108,8 @@ static DEFINE_MUTEX(loop_validate_mutex);
  * loop_configure()/loop_change_fd()/__loop_clr_fd() calls.
  */
 static int loop_global_lock_killable(struct loop_device *lo, bool global)
+	__cond_acquires(0, &lo->lo_mutex)
+	__context_unsafe(conditional locking)
 {
 	int err;
 
@@ -129,6 +131,8 @@ static int loop_global_lock_killable(struct loop_device *lo, bool global)
  * @global: true if @lo was about to bind another "struct loop_device", false otherwise
  */
 static void loop_global_unlock(struct loop_device *lo, bool global)
+	__releases(&lo->lo_mutex)
+	__context_unsafe(conditional locking)
 {
 	mutex_unlock(&lo->lo_mutex);
 	if (global)

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

* [PATCH 04/13] loop: Assign a unique lockdep key to each lo_mutex instance
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (2 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 03/13] loop: Enable context analysis Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 05/13] loop: Add more __must_hold() annotations Bart Van Assche
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Prepare for nesting lo_mutex in loop_validate_file().

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 638d97293a79..5c7f72e01345 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -20,6 +20,7 @@
 #include <linux/suspend.h>
 #include <linux/freezer.h>
 #include <linux/mutex.h>
+#include <linux/lockdep.h>
 #include <linux/writeback.h>
 #include <linux/completion.h>
 #include <linux/highmem.h>
@@ -74,6 +75,7 @@ struct loop_device {
 	struct blk_mq_tag_set	tag_set;
 	struct gendisk		*lo_disk;
 	struct mutex		lo_mutex;
+	struct lock_class_key	lo_mutex_key;
 	bool			idr_visible;
 };
 
@@ -1801,6 +1803,7 @@ static void lo_free_disk(struct gendisk *disk)
 	loop_free_idle_workers(lo, true);
 	timer_shutdown_sync(&lo->timer);
 	mutex_destroy(&lo->lo_mutex);
+	lockdep_unregister_key(&lo->lo_mutex_key);
 	kfree(lo);
 }
 
@@ -2114,7 +2117,8 @@ static int loop_add(int i)
 	 */
 	if (!part_shift)
 		set_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
-	mutex_init(&lo->lo_mutex);
+	lockdep_register_key(&lo->lo_mutex_key);
+	mutex_init_with_key(&lo->lo_mutex, &lo->lo_mutex_key);
 	lo->lo_number		= i;
 	spin_lock_init(&lo->lo_lock);
 	spin_lock_init(&lo->lo_work_lock);

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

* [PATCH 05/13] loop: Add more __must_hold() annotations
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (3 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 04/13] loop: Assign a unique lockdep key to each lo_mutex instance Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 06/13] loop: Protect all lo_backing_file accesses with lo->lo_mutex Bart Van Assche
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Annotate all functions that are called with lo_mutex held with
__must_hold(&lo->lo_mutex). Add a 'lo' argument to loop_validate_file()
such that a __must_hold(&lo->lo_mutex) annotation can be added to this
function too.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 5c7f72e01345..aa40a6ed7f35 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -145,6 +145,7 @@ static int max_part;
 static int part_shift;
 
 static loff_t lo_calculate_size(struct loop_device *lo, struct file *file)
+	__must_hold(&lo->lo_mutex)
 {
 	loff_t loopsize;
 	int ret;
@@ -186,6 +187,7 @@ static loff_t lo_calculate_size(struct loop_device *lo, struct file *file)
  * the backing device.
  */
 static bool lo_can_use_dio(struct loop_device *lo)
+	__must_hold(&lo->lo_mutex)
 {
 	if (!(lo->lo_backing_file->f_mode & FMODE_CAN_ODIRECT))
 		return false;
@@ -205,6 +207,7 @@ static bool lo_can_use_dio(struct loop_device *lo)
  * not the originally passed in one.
  */
 static inline void loop_update_dio(struct loop_device *lo)
+	__must_hold(&lo->lo_mutex)
 {
 	lockdep_assert_held(&lo->lo_mutex);
 	WARN_ON_ONCE(lo->lo_state == Lo_bound &&
@@ -223,6 +226,7 @@ static inline void loop_update_dio(struct loop_device *lo)
  * a sector_t, eg using loop_validate_size()
  */
 static void loop_set_size(struct loop_device *lo, loff_t size)
+	__must_hold(&lo->lo_mutex)
 {
 	if (!set_capacity_and_notify(lo->lo_disk, size))
 		kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
@@ -455,6 +459,7 @@ static void loop_reread_partitions(struct loop_device *lo)
 }
 
 static void loop_update_dio_alignment(struct loop_device *lo)
+	__must_hold(&lo->lo_mutex)
 {
 	struct file *file = lo->lo_backing_file;
 	struct block_device *sb_bdev = file->f_mapping->host->i_sb->s_bdev;
@@ -507,7 +512,9 @@ static inline bool is_loop_device(struct file *file)
 }
 
 /* Returns 0 if and only if @file is not backed by loop device @bdev. */
-static int loop_validate_file(struct file *file, struct block_device *bdev)
+static int loop_validate_file(struct loop_device *lo, struct file *file,
+			      struct block_device *bdev)
+	__must_hold(&lo->lo_mutex)
 {
 	struct inode	*inode = file->f_mapping->host;
 	struct file	*f = file;
@@ -535,6 +542,7 @@ static int loop_validate_file(struct file *file, struct block_device *bdev)
 }
 
 static void loop_assign_backing_file(struct loop_device *lo, struct file *file)
+	__must_hold(&lo->lo_mutex)
 {
 	lo->lo_backing_file = file;
 	lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping);
@@ -599,7 +607,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
 	if (!(lo->lo_flags & LO_FLAGS_READ_ONLY))
 		goto out_err;
 
-	error = loop_validate_file(file, bdev);
+	error = loop_validate_file(lo, file, bdev);
 	if (error)
 		goto out_err;
 
@@ -770,6 +778,7 @@ static void loop_sysfs_exit(struct loop_device *lo)
 
 static void loop_get_discard_config(struct loop_device *lo,
 				    u32 *granularity, u32 *max_discard_sectors)
+	__must_hold(&lo->lo_mutex)
 {
 	struct file *file = lo->lo_backing_file;
 	struct inode *inode = file->f_mapping->host;
@@ -936,6 +945,7 @@ static void loop_free_idle_workers_timer(struct timer_list *timer)
 static int
 loop_set_status_from_info(struct loop_device *lo,
 			  const struct loop_info64 *info)
+	__must_hold(&lo->lo_mutex)
 {
 	if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE)
 		return -EINVAL;
@@ -974,6 +984,7 @@ static unsigned int loop_default_blocksize(struct loop_device *lo)
 }
 
 static void loop_set_dma_limit(struct loop_device *lo, struct queue_limits *lim)
+	__must_hold(&lo->lo_mutex)
 {
 	/*
 	 * Direct I/O forwards the user pages to the backing file unchanged, so
@@ -988,6 +999,7 @@ static void loop_set_dma_limit(struct loop_device *lo, struct queue_limits *lim)
 
 static void loop_update_limits(struct loop_device *lo, struct queue_limits *lim,
 		unsigned int bsize)
+	__must_hold(&lo->lo_mutex)
 {
 	struct file *file = lo->lo_backing_file;
 	struct inode *inode = file->f_mapping->host;
@@ -1064,7 +1076,7 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
 	if (lo->lo_state != Lo_unbound)
 		goto out_unlock;
 
-	error = loop_validate_file(file, bdev);
+	error = loop_validate_file(lo, file, bdev);
 	if (error)
 		goto out_unlock;
 
@@ -1443,6 +1455,7 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {
 }
 
 static int loop_set_capacity(struct loop_device *lo)
+	__must_hold(&lo->lo_mutex)
 {
 	loff_t size;
 
@@ -1456,6 +1469,7 @@ static int loop_set_capacity(struct loop_device *lo)
 }
 
 static int loop_set_dio(struct loop_device *lo, unsigned long arg)
+	__must_hold(&lo->lo_mutex)
 {
 	bool use_dio = !!arg;
 	unsigned int memflags;

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

* [PATCH 06/13] loop: Protect all lo_backing_file accesses with lo->lo_mutex
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (4 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 05/13] loop: Add more __must_hold() annotations Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 07/13] loop: Fix race conditions in loop_validate_file() Bart Van Assche
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Protect all lo_backing_file accesses from the control path with
lo->lo_mutex. Use READ_ONCE() to read lo_backing_file from the data path.
Serialization of I/O path lo_backing_file reads and control path
lo_backing_file changes happens by freezing the request queue.

Remove lo_lock because it is no longer used.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 45 ++++++++++++++++++++++++++------------------
 1 file changed, 27 insertions(+), 18 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index aa40a6ed7f35..c5f026520836 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -53,14 +53,13 @@ struct loop_device {
 	int		lo_flags;
 	char		lo_file_name[LO_NAME_SIZE];
 
-	struct file	*lo_backing_file;
+	struct file	*lo_backing_file __guarded_by(&lo_mutex);
 	unsigned int	lo_min_dio_size;
 	unsigned int	lo_dio_mem_align;
 	struct block_device *lo_device;
 
 	gfp_t		old_gfp_mask;
 
-	spinlock_t		lo_lock;
 	int			lo_state;
 	spinlock_t              lo_work_lock;
 	struct workqueue_struct *workqueue;
@@ -261,7 +260,7 @@ static int lo_fallocate(struct loop_device *lo, struct request *rq, loff_t pos,
 	 * We use fallocate to manipulate the space mappings used by the image
 	 * a.k.a. discard/zerorange.
 	 */
-	struct file *file = lo->lo_backing_file;
+	struct file *file = context_unsafe(READ_ONCE(lo->lo_backing_file));
 	int ret;
 
 	mode |= FALLOC_FL_KEEP_SIZE;
@@ -285,7 +284,7 @@ static int lo_fallocate(struct loop_device *lo, struct request *rq, loff_t pos,
 
 static int lo_req_flush(struct loop_device *lo, struct request *rq)
 {
-	int ret = vfs_fsync(lo->lo_backing_file, 0);
+	int ret = vfs_fsync(context_unsafe(READ_ONCE(lo->lo_backing_file)), 0);
 	if (unlikely(ret && ret != -EINVAL))
 		ret = -EIO;
 
@@ -354,7 +353,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
 	struct iov_iter iter;
 	struct req_iterator rq_iter;
 	struct request *rq = blk_mq_rq_from_pdu(cmd);
-	struct file *file = lo->lo_backing_file;
+	struct file *file = context_unsafe(READ_ONCE(lo->lo_backing_file));
 	unsigned int nr_bvec;
 	int ret;
 
@@ -511,6 +510,19 @@ static inline bool is_loop_device(struct file *file)
 	return bdev && bdev->bd_disk->major == LOOP_MAJOR;
 }
 
+static struct file *loop_get_backing_file(struct loop_device *lo)
+	__must_hold(&lo->lo_mutex)
+{
+	if (lo->lo_state != Lo_bound)
+		return NULL;
+	/*
+	 * Order wrt setting lo->lo_backing_file in
+	 * loop_configure().
+	 */
+	rmb();
+	return lo->lo_backing_file;
+}
+
 /* Returns 0 if and only if @file is not backed by loop device @bdev. */
 static int loop_validate_file(struct loop_device *lo, struct file *file,
 			      struct block_device *bdev)
@@ -532,11 +544,10 @@ static int loop_validate_file(struct loop_device *lo, struct file *file,
 			return -EBADF;
 
 		l = f_bdev->bd_disk->private_data;
-		if (l->lo_state != Lo_bound)
+		scoped_guard(mutex, &l->lo_mutex)
+			f = loop_get_backing_file(l);
+		if (!f)
 			return -EINVAL;
-		/* Order wrt setting lo->lo_backing_file in loop_configure(). */
-		rmb();
-		f = l->lo_backing_file;
 	}
 	return 0;
 }
@@ -693,10 +704,9 @@ static ssize_t loop_attr_backing_file_show(struct loop_device *lo, char *buf)
 	ssize_t ret;
 	char *p = NULL;
 
-	spin_lock_irq(&lo->lo_lock);
-	if (lo->lo_backing_file)
-		p = file_path(lo->lo_backing_file, buf, PAGE_SIZE - 1);
-	spin_unlock_irq(&lo->lo_lock);
+	scoped_guard(mutex, &lo->lo_mutex)
+		if (lo->lo_backing_file)
+			p = file_path(lo->lo_backing_file, buf, PAGE_SIZE - 1);
 
 	if (IS_ERR_OR_NULL(p))
 		ret = PTR_ERR(p);
@@ -1174,10 +1184,10 @@ static void __loop_clr_fd(struct loop_device *lo)
 	gfp_t gfp = lo->old_gfp_mask;
 	int err;
 
-	spin_lock_irq(&lo->lo_lock);
-	filp = lo->lo_backing_file;
-	lo->lo_backing_file = NULL;
-	spin_unlock_irq(&lo->lo_lock);
+	scoped_guard(mutex, &lo->lo_mutex) {
+		filp = lo->lo_backing_file;
+		lo->lo_backing_file = NULL;
+	}
 
 	lo->lo_device = NULL;
 	lo->lo_offset = 0;
@@ -2134,7 +2144,6 @@ static int loop_add(int i)
 	lockdep_register_key(&lo->lo_mutex_key);
 	mutex_init_with_key(&lo->lo_mutex, &lo->lo_mutex_key);
 	lo->lo_number		= i;
-	spin_lock_init(&lo->lo_lock);
 	spin_lock_init(&lo->lo_work_lock);
 	INIT_WORK(&lo->rootcg_work, loop_rootcg_workfn);
 	INIT_LIST_HEAD(&lo->rootcg_cmd_list);

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

* [PATCH 07/13] loop: Fix race conditions in loop_validate_file()
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (5 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 06/13] loop: Protect all lo_backing_file accesses with lo->lo_mutex Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 08/13] loop: Remove memory barriers Bart Van Assche
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Fix race conditions in loop_validate_file() by adding reference counting
to the file chain traversal.

Ensure the file reference is kept alive during all dereferences by
calling get_file() before the loop and deferring fput() until after we
have locked the target device's lo_mutex and confirmed it is in the
Lo_bound state.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index c5f026520836..8b633ea6e72f 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -520,7 +520,7 @@ static struct file *loop_get_backing_file(struct loop_device *lo)
 	 * loop_configure().
 	 */
 	rmb();
-	return lo->lo_backing_file;
+	return get_file(lo->lo_backing_file);
 }
 
 /* Returns 0 if and only if @file is not backed by loop device @bdev. */
@@ -534,21 +534,27 @@ static int loop_validate_file(struct loop_device *lo, struct file *file,
 	if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
 		return -EINVAL;
 
+	get_file(f);
 	/* Avoid recursion */
 	while (is_loop_device(f)) {
 		struct loop_device *l;
+		struct file *prev_f = f;
 		struct block_device *f_bdev = loop_get_bdev(f);
 
 		lockdep_assert_held(&loop_validate_mutex);
-		if (f_bdev->bd_disk == bdev->bd_disk)
+		if (f_bdev->bd_disk == bdev->bd_disk) {
+			fput(f);
 			return -EBADF;
+		}
 
 		l = f_bdev->bd_disk->private_data;
 		scoped_guard(mutex, &l->lo_mutex)
 			f = loop_get_backing_file(l);
+		fput(prev_f);
 		if (!f)
 			return -EINVAL;
 	}
+	fput(f);
 	return 0;
 }
 

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

* [PATCH 08/13] loop: Remove memory barriers
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (6 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 07/13] loop: Fix race conditions in loop_validate_file() Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 09/13] loop: Split loop_change_fd() Bart Van Assche
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Now that all lo_state and lo_backing_file accesses from the control path
are serialized by lo_mutex, it is no longer necessary to use memory
barriers to order the lo_state and lo_backing_file accesses. Hence,
remove these memory barriers. While several lockless accesses of these
two member variables remain in the I/O path, these are serialized with
backing file changes by freezing the request queue. See also
loop_change_fd().

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 8b633ea6e72f..93b589bc6e3d 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -515,11 +515,6 @@ static struct file *loop_get_backing_file(struct loop_device *lo)
 {
 	if (lo->lo_state != Lo_bound)
 		return NULL;
-	/*
-	 * Order wrt setting lo->lo_backing_file in
-	 * loop_configure().
-	 */
-	rmb();
 	return get_file(lo->lo_backing_file);
 }
 
@@ -1149,9 +1144,6 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
 	size = lo_calculate_size(lo, file);
 	loop_set_size(lo, size);
 
-	/* Order wrt reading lo_state in loop_validate_file(). */
-	wmb();
-
 	WRITE_ONCE(lo->lo_state, Lo_bound);
 	if (part_shift)
 		lo->lo_flags |= LO_FLAGS_PARTSCAN;

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

* [PATCH 09/13] loop: Split loop_change_fd()
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (7 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 08/13] loop: Remove memory barriers Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 10/13] loop: Split loop_configure() Bart Van Assche
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Prepare for adding a second call of __loop_change_fd().

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 86 ++++++++++++++++++++++++--------------------
 1 file changed, 47 insertions(+), 39 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 93b589bc6e3d..5ba4cf435103 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -576,6 +576,50 @@ static int loop_check_backing_file(struct file *file)
 	return 0;
 }
 
+static int __loop_change_fd(struct loop_device *lo, struct block_device *bdev,
+			    struct file *file, struct file **old_file,
+			    bool *partscan)
+	__must_hold(&lo->lo_mutex)
+{
+	unsigned int memflags;
+	int error;
+
+	if (lo->lo_state != Lo_bound)
+		return -ENXIO;
+
+	/* the loop device has to be read-only */
+	if (!(lo->lo_flags & LO_FLAGS_READ_ONLY))
+		return -EINVAL;
+
+	error = loop_validate_file(lo, file, bdev);
+	if (error)
+		return error;
+
+	*old_file = lo->lo_backing_file;
+
+	/* size of the new backing store needs to be the same */
+	if (lo_calculate_size(lo, file) != lo_calculate_size(lo, *old_file))
+		return -EINVAL;
+
+	/*
+	 * We might switch to direct I/O mode for the loop device, write back
+	 * all dirty data the page cache now that so that the individual I/O
+	 * operations don't have to do that.
+	 */
+	vfs_fsync(file, 0);
+
+	/* and ... switch */
+	disk_force_media_change(lo->lo_disk);
+	memflags = blk_mq_freeze_queue(lo->lo_queue);
+	mapping_set_gfp_mask((*old_file)->f_mapping, lo->old_gfp_mask);
+	loop_assign_backing_file(lo, file);
+	loop_update_dio(lo);
+	blk_mq_unfreeze_queue(lo->lo_queue, memflags);
+	*partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
+
+	return 0;
+}
+
 /*
  * loop_change_fd switched the backing store of a loopback device to
  * a new file. This is useful for operating system installers to free up
@@ -589,7 +633,6 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
 {
 	struct file *file = fget(arg);
 	struct file *old_file;
-	unsigned int memflags;
 	int error;
 	bool partscan;
 	bool is_loop;
@@ -610,43 +653,10 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
 	error = loop_global_lock_killable(lo, is_loop);
 	if (error)
 		goto out_putf;
-	error = -ENXIO;
-	if (lo->lo_state != Lo_bound)
-		goto out_err;
-
-	/* the loop device has to be read-only */
-	error = -EINVAL;
-	if (!(lo->lo_flags & LO_FLAGS_READ_ONLY))
-		goto out_err;
-
-	error = loop_validate_file(lo, file, bdev);
-	if (error)
-		goto out_err;
-
-	old_file = lo->lo_backing_file;
-
-	error = -EINVAL;
-
-	/* size of the new backing store needs to be the same */
-	if (lo_calculate_size(lo, file) != lo_calculate_size(lo, old_file))
-		goto out_err;
-
-	/*
-	 * We might switch to direct I/O mode for the loop device, write back
-	 * all dirty data the page cache now that so that the individual I/O
-	 * operations don't have to do that.
-	 */
-	vfs_fsync(file, 0);
-
-	/* and ... switch */
-	disk_force_media_change(lo->lo_disk);
-	memflags = blk_mq_freeze_queue(lo->lo_queue);
-	mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
-	loop_assign_backing_file(lo, file);
-	loop_update_dio(lo);
-	blk_mq_unfreeze_queue(lo->lo_queue, memflags);
-	partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
+	error = __loop_change_fd(lo, bdev, file, &old_file, &partscan);
 	loop_global_unlock(lo, is_loop);
+	if (error)
+		goto out_putf;
 
 	/*
 	 * Flush loop_validate_file() before fput(), for l->lo_backing_file
@@ -671,8 +681,6 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
 	kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
 	return error;
 
-out_err:
-	loop_global_unlock(lo, is_loop);
 out_putf:
 	fput(file);
 	dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0);

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

* [PATCH 10/13] loop: Split loop_configure()
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (8 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 09/13] loop: Split loop_change_fd() Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 11/13] loop: Remove the "bool global" function argument Bart Van Assche
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Prepare for adding a second __loop_configure() call.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 110 +++++++++++++++++++++++--------------------
 1 file changed, 58 insertions(+), 52 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 5ba4cf435103..cf65d88d73c2 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1052,61 +1052,29 @@ static void loop_update_limits(struct loop_device *lo, struct queue_limits *lim,
 		lim->discard_granularity = 0;
 }
 
-static int loop_configure(struct loop_device *lo, blk_mode_t mode,
-			  struct block_device *bdev,
-			  const struct loop_config *config)
+static int __loop_configure(struct loop_device *lo, blk_mode_t mode,
+			    struct block_device *bdev,
+			    const struct loop_config *config, struct file *file,
+			    bool *partscan)
+	__must_hold(&lo->lo_mutex)
 {
-	struct file *file = fget(config->fd);
 	struct queue_limits lim;
-	int error;
 	loff_t size;
-	bool partscan;
-	bool is_loop;
-
-	if (!file)
-		return -EBADF;
-
-	error = loop_check_backing_file(file);
-	if (error) {
-		fput(file);
-		return error;
-	}
-
-	is_loop = is_loop_device(file);
-
-	/* This is safe, since we have a reference from open(). */
-	__module_get(THIS_MODULE);
-
-	/*
-	 * If we don't hold exclusive handle for the device, upgrade to it
-	 * here to avoid changing device under exclusive owner.
-	 */
-	if (!(mode & BLK_OPEN_EXCL)) {
-		error = bd_prepare_to_claim(bdev, loop_configure, NULL);
-		if (error)
-			goto out_putf;
-	}
-
-	error = loop_global_lock_killable(lo, is_loop);
-	if (error)
-		goto out_bdev;
+	int error;
 
-	error = -EBUSY;
 	if (lo->lo_state != Lo_unbound)
-		goto out_unlock;
+		return -EBUSY;
 
 	error = loop_validate_file(lo, file, bdev);
 	if (error)
-		goto out_unlock;
+		return error;
 
-	if ((config->info.lo_flags & ~LOOP_CONFIGURE_SETTABLE_FLAGS) != 0) {
-		error = -EINVAL;
-		goto out_unlock;
-	}
+	if ((config->info.lo_flags & ~LOOP_CONFIGURE_SETTABLE_FLAGS) != 0)
+		return -EINVAL;
 
 	error = loop_set_status_from_info(lo, &config->info);
 	if (error)
-		goto out_unlock;
+		return error;
 	lo->lo_flags = config->info.lo_flags;
 
 	if (!(file->f_mode & FMODE_WRITE) || !(mode & BLK_OPEN_WRITE) ||
@@ -1117,10 +1085,8 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
 		lo->workqueue = alloc_workqueue("loop%d",
 						WQ_UNBOUND | WQ_FREEZABLE,
 						0, lo->lo_number);
-		if (!lo->workqueue) {
-			error = -ENOMEM;
-			goto out_unlock;
-		}
+		if (!lo->workqueue)
+			return -ENOMEM;
 	}
 
 	/* suppress uevents while reconfiguring the device */
@@ -1137,7 +1103,7 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
 	/* No need to freeze the queue as the device isn't bound yet. */
 	error = queue_limits_commit_update(lo->lo_queue, &lim);
 	if (error)
-		goto out_unlock;
+		return error;
 
 	/*
 	 * We might switch to direct I/O mode for the loop device, write back
@@ -1155,14 +1121,56 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
 	WRITE_ONCE(lo->lo_state, Lo_bound);
 	if (part_shift)
 		lo->lo_flags |= LO_FLAGS_PARTSCAN;
-	partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
-	if (partscan)
+	*partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
+	if (*partscan)
 		clear_bit(GD_SUPPRESS_PART_SCAN, &lo->lo_disk->state);
 
 	dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0);
 	kobject_uevent(&disk_to_dev(lo->lo_disk)->kobj, KOBJ_CHANGE);
 
+	return 0;
+}
+
+static int loop_configure(struct loop_device *lo, blk_mode_t mode,
+			  struct block_device *bdev,
+			  const struct loop_config *config)
+{
+	struct file *file = fget(config->fd);
+	int error;
+	bool partscan;
+	bool is_loop;
+
+	if (!file)
+		return -EBADF;
+
+	error = loop_check_backing_file(file);
+	if (error) {
+		fput(file);
+		return error;
+	}
+
+	is_loop = is_loop_device(file);
+
+	/* This is safe, since we have a reference from open(). */
+	__module_get(THIS_MODULE);
+
+	/*
+	 * If we don't hold exclusive handle for the device, upgrade to it
+	 * here to avoid changing device under exclusive owner.
+	 */
+	if (!(mode & BLK_OPEN_EXCL)) {
+		error = bd_prepare_to_claim(bdev, loop_configure, NULL);
+		if (error)
+			goto out_putf;
+	}
+
+	error = loop_global_lock_killable(lo, is_loop);
+	if (error)
+		goto out_bdev;
+	error = __loop_configure(lo, mode, bdev, config, file, &partscan);
 	loop_global_unlock(lo, is_loop);
+	if (error)
+		goto out_bdev;
 	if (partscan)
 		loop_reread_partitions(lo);
 
@@ -1171,8 +1179,6 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
 
 	return 0;
 
-out_unlock:
-	loop_global_unlock(lo, is_loop);
 out_bdev:
 	if (!(mode & BLK_OPEN_EXCL))
 		bd_abort_claiming(bdev, loop_configure);

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

* [PATCH 11/13] loop: Remove the "bool global" function argument
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (9 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 10/13] loop: Split loop_configure() Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 12/13] loop: Modify the loop_process_work() calling convention Bart Van Assche
  2026-08-20 19:57 ` [PATCH 13/13] loop: Add __guarded_by() annotations Bart Van Assche
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Keep the behavior in loop_global_lock_killable() for the global == true
case. Expand loop_global_lock_killable(lo, false) calls into a
mutex_lock_killable() and a mutex_unlock() call.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 71 ++++++++++++++++++++++++++------------------
 1 file changed, 42 insertions(+), 29 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index cf65d88d73c2..8fde5f2d3473 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -100,27 +100,24 @@ static DEFINE_MUTEX(loop_validate_mutex);
  * loop_global_lock_killable() - take locks for safe loop_validate_file() test
  *
  * @lo: struct loop_device
- * @global: true if @lo is about to bind another "struct loop_device", false otherwise
  *
  * Returns 0 on success, -EINTR otherwise.
  *
- * Since loop_validate_file() traverses on other "struct loop_device" if
- * is_loop_device() is true, we need a global lock for serializing concurrent
+ * Since loop_validate_file() traverses on other "struct loop_device", we need a
+ * global lock for serializing concurrent
  * loop_configure()/loop_change_fd()/__loop_clr_fd() calls.
  */
-static int loop_global_lock_killable(struct loop_device *lo, bool global)
+static int loop_global_lock_killable(struct loop_device *lo)
+	__cond_acquires(0, &loop_validate_mutex)
 	__cond_acquires(0, &lo->lo_mutex)
-	__context_unsafe(conditional locking)
 {
 	int err;
 
-	if (global) {
-		err = mutex_lock_killable(&loop_validate_mutex);
-		if (err)
-			return err;
-	}
+	err = mutex_lock_killable(&loop_validate_mutex);
+	if (err)
+		return err;
 	err = mutex_lock_killable(&lo->lo_mutex);
-	if (err && global)
+	if (err)
 		mutex_unlock(&loop_validate_mutex);
 	return err;
 }
@@ -129,15 +126,13 @@ static int loop_global_lock_killable(struct loop_device *lo, bool global)
  * loop_global_unlock() - release locks taken by loop_global_lock_killable()
  *
  * @lo: struct loop_device
- * @global: true if @lo was about to bind another "struct loop_device", false otherwise
  */
-static void loop_global_unlock(struct loop_device *lo, bool global)
+static void loop_global_unlock(struct loop_device *lo)
 	__releases(&lo->lo_mutex)
-	__context_unsafe(conditional locking)
+	__releases(&loop_validate_mutex)
 {
 	mutex_unlock(&lo->lo_mutex);
-	if (global)
-		mutex_unlock(&loop_validate_mutex);
+	mutex_unlock(&loop_validate_mutex);
 }
 
 static int max_part;
@@ -650,11 +645,19 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
 	dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 1);
 
 	is_loop = is_loop_device(file);
-	error = loop_global_lock_killable(lo, is_loop);
-	if (error)
-		goto out_putf;
-	error = __loop_change_fd(lo, bdev, file, &old_file, &partscan);
-	loop_global_unlock(lo, is_loop);
+	if (is_loop) {
+		error = loop_global_lock_killable(lo);
+		if (error)
+			goto out_putf;
+		error = __loop_change_fd(lo, bdev, file, &old_file, &partscan);
+		loop_global_unlock(lo);
+	} else {
+		error = mutex_lock_killable(&lo->lo_mutex);
+		if (error)
+			goto out_putf;
+		error = __loop_change_fd(lo, bdev, file, &old_file, &partscan);
+		mutex_unlock(&lo->lo_mutex);
+	}
 	if (error)
 		goto out_putf;
 
@@ -1164,11 +1167,21 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
 			goto out_putf;
 	}
 
-	error = loop_global_lock_killable(lo, is_loop);
-	if (error)
-		goto out_bdev;
-	error = __loop_configure(lo, mode, bdev, config, file, &partscan);
-	loop_global_unlock(lo, is_loop);
+	if (is_loop) {
+		error = loop_global_lock_killable(lo);
+		if (error)
+			goto out_bdev;
+		error = __loop_configure(lo, mode, bdev, config, file,
+					 &partscan);
+		loop_global_unlock(lo);
+	} else {
+		error = mutex_lock_killable(&lo->lo_mutex);
+		if (error)
+			goto out_bdev;
+		error = __loop_configure(lo, mode, bdev, config, file,
+					 &partscan);
+		mutex_unlock(&lo->lo_mutex);
+	}
 	if (error)
 		goto out_bdev;
 	if (partscan)
@@ -1275,11 +1288,11 @@ static int loop_clr_fd(struct loop_device *lo)
 	 * which loop_configure()/loop_change_fd() found via fget() was this
 	 * loop device.
 	 */
-	err = loop_global_lock_killable(lo, true);
+	err = loop_global_lock_killable(lo);
 	if (err)
 		return err;
 	if (lo->lo_state != Lo_bound) {
-		loop_global_unlock(lo, true);
+		loop_global_unlock(lo);
 		return -ENXIO;
 	}
 	/*
@@ -1291,7 +1304,7 @@ static int loop_clr_fd(struct loop_device *lo)
 	lo->lo_flags |= LO_FLAGS_AUTOCLEAR;
 	if (disk_openers(lo->lo_disk) == 1)
 		WRITE_ONCE(lo->lo_state, Lo_rundown);
-	loop_global_unlock(lo, true);
+	loop_global_unlock(lo);
 
 	return 0;
 }

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

* [PATCH 12/13] loop: Modify the loop_process_work() calling convention
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (10 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 11/13] loop: Remove the "bool global" function argument Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  2026-08-20 19:57 ` [PATCH 13/13] loop: Add __guarded_by() annotations Bart Van Assche
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Prepare for annotating the rootcg_cmd_list member with __guarded_by().

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 8fde5f2d3473..637212af70d2 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -2037,13 +2037,15 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
 }
 
 static void loop_process_work(struct loop_worker *worker,
-			struct list_head *cmd_list, struct loop_device *lo)
+			      struct loop_device *lo, bool rootcg)
 {
 	int orig_flags = current->flags;
+	struct list_head *cmd_list;
 	struct loop_cmd *cmd;
 
 	current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
 	spin_lock_irq(&lo->lo_work_lock);
+	cmd_list = rootcg ? &lo->rootcg_cmd_list : &worker->cmd_list;
 	while (!list_empty(cmd_list)) {
 		cmd = container_of(
 			cmd_list->next, struct loop_cmd, list_entry);
@@ -2074,14 +2076,14 @@ static void loop_workfn(struct work_struct *work)
 {
 	struct loop_worker *worker =
 		container_of(work, struct loop_worker, work);
-	loop_process_work(worker, &worker->cmd_list, worker->lo);
+	loop_process_work(worker, worker->lo, false);
 }
 
 static void loop_rootcg_workfn(struct work_struct *work)
 {
 	struct loop_device *lo =
 		container_of(work, struct loop_device, rootcg_work);
-	loop_process_work(NULL, &lo->rootcg_cmd_list, lo);
+	loop_process_work(NULL, lo, true);
 }
 
 static const struct blk_mq_ops loop_mq_ops = {

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

* [PATCH 13/13] loop: Add __guarded_by() annotations
  2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
                   ` (11 preceding siblings ...)
  2026-08-20 19:57 ` [PATCH 12/13] loop: Modify the loop_process_work() calling convention Bart Van Assche
@ 2026-08-20 19:57 ` Bart Van Assche
  12 siblings, 0 replies; 14+ messages in thread
From: Bart Van Assche @ 2026-08-20 19:57 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-block, Christoph Hellwig, Nilay Shroff, Bart Van Assche

Document which synchronization object protects which variable. Suppress
complaints about accesses without locking in initialization and cleanup
functions with context_unsafe() and __assume_ctx_lock().

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/block/loop.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 637212af70d2..8a6082d887eb 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -60,13 +60,13 @@ struct loop_device {
 
 	gfp_t		old_gfp_mask;
 
-	int			lo_state;
+	int			lo_state __guarded_by(&lo_mutex);
 	spinlock_t              lo_work_lock;
 	struct workqueue_struct *workqueue;
 	struct work_struct      rootcg_work;
-	struct list_head        rootcg_cmd_list;
-	struct list_head        idle_worker_list;
-	struct rb_root          worker_tree;
+	struct list_head        rootcg_cmd_list __guarded_by(&lo_work_lock);
+	struct list_head        idle_worker_list __guarded_by(&lo_work_lock);
+	struct rb_root          worker_tree __guarded_by(&lo_work_lock);
 	struct timer_list       timer;
 	bool			sysfs_inited;
 
@@ -92,8 +92,8 @@ struct loop_cmd {
 #define LOOP_IDLE_WORKER_TIMEOUT (60 * HZ)
 #define LOOP_DEFAULT_HW_Q_DEPTH 128
 
-static DEFINE_IDR(loop_index_idr);
 static DEFINE_MUTEX(loop_ctl_mutex);
+static __guarded_by(&loop_ctl_mutex) DEFINE_IDR(loop_index_idr);
 static DEFINE_MUTEX(loop_validate_mutex);
 
 /**
@@ -2107,10 +2107,10 @@ static int loop_add(int i)
 	lo = kzalloc_obj(*lo);
 	if (!lo)
 		goto out;
-	lo->worker_tree = RB_ROOT;
-	INIT_LIST_HEAD(&lo->idle_worker_list);
+	context_unsafe(lo->worker_tree = RB_ROOT);
+	context_unsafe(INIT_LIST_HEAD(&lo->idle_worker_list));
 	timer_setup(&lo->timer, loop_free_idle_workers_timer, TIMER_DEFERRABLE);
-	WRITE_ONCE(lo->lo_state, Lo_unbound);
+	context_unsafe(WRITE_ONCE(lo->lo_state, Lo_unbound));
 
 	err = mutex_lock_killable(&loop_ctl_mutex);
 	if (err)
@@ -2173,7 +2173,7 @@ static int loop_add(int i)
 	lo->lo_number		= i;
 	spin_lock_init(&lo->lo_work_lock);
 	INIT_WORK(&lo->rootcg_work, loop_rootcg_workfn);
-	INIT_LIST_HEAD(&lo->rootcg_cmd_list);
+	context_unsafe(INIT_LIST_HEAD(&lo->rootcg_cmd_list));
 	disk->major		= LOOP_MAJOR;
 	disk->first_minor	= i << part_shift;
 	disk->minors		= 1 << part_shift;
@@ -2405,6 +2405,7 @@ static void __exit loop_exit(void)
 	 * module unloading is requested). If this is not a clean unloading,
 	 * we have no means to avoid kernel crash.
 	 */
+	__assume_ctx_lock(&loop_ctl_mutex);
 	idr_for_each_entry(&loop_index_idr, lo, id)
 		loop_remove(lo);
 

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

end of thread, other threads:[~2026-08-20 19:58 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-20 19:57 [PATCH 00/13] Improve the loop driver Bart Van Assche
2026-08-20 19:57 ` [PATCH 01/13] loop: Fix the code for recursion detection Bart Van Assche
2026-08-20 19:57 ` [PATCH 02/13] loop: Reorder checks in loop_validate_file() Bart Van Assche
2026-08-20 19:57 ` [PATCH 03/13] loop: Enable context analysis Bart Van Assche
2026-08-20 19:57 ` [PATCH 04/13] loop: Assign a unique lockdep key to each lo_mutex instance Bart Van Assche
2026-08-20 19:57 ` [PATCH 05/13] loop: Add more __must_hold() annotations Bart Van Assche
2026-08-20 19:57 ` [PATCH 06/13] loop: Protect all lo_backing_file accesses with lo->lo_mutex Bart Van Assche
2026-08-20 19:57 ` [PATCH 07/13] loop: Fix race conditions in loop_validate_file() Bart Van Assche
2026-08-20 19:57 ` [PATCH 08/13] loop: Remove memory barriers Bart Van Assche
2026-08-20 19:57 ` [PATCH 09/13] loop: Split loop_change_fd() Bart Van Assche
2026-08-20 19:57 ` [PATCH 10/13] loop: Split loop_configure() Bart Van Assche
2026-08-20 19:57 ` [PATCH 11/13] loop: Remove the "bool global" function argument Bart Van Assche
2026-08-20 19:57 ` [PATCH 12/13] loop: Modify the loop_process_work() calling convention Bart Van Assche
2026-08-20 19:57 ` [PATCH 13/13] loop: Add __guarded_by() annotations Bart Van Assche

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