Re: [PATCH v2 02/12] loop: Remove the "bool global" function argument
Nilay Shroff <[email protected]> Mon, 3 Aug 2026 18:35:37 +0530
| Newsgroups | org.kernel.vger.linux-block |
|---|---|
| Message-ID | <[email protected]> |
On 7/31/26 1:28 AM, Bart Van Assche wrote: > Move the code that is protected by a mutex from loop_change_fd() into > __loop_change_fd(). Move the code that is protected by a mutex from > loop_configure() into __loop_configure(). 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. This patch prepares for adding lock context > annotations. > > Cc: Haris Iqbal <[email protected]> > Signed-off-by: Bart Van Assche <[email protected]> > --- > drivers/block/loop.c | 249 ++++++++++++++++++++++++------------------- > 1 file changed, 138 insertions(+), 111 deletions(-) > > diff --git a/drivers/block/loop.c b/drivers/block/loop.c > index 1faecef33009..a71fe763c933 100644 > --- a/drivers/block/loop.c > +++ b/drivers/block/loop.c > @@ -98,25 +98,22 @@ 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) > { > 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; > } > @@ -125,13 +122,11 @@ 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) > { > mutex_unlock(&lo->lo_mutex); > - if (global) > - mutex_unlock(&loop_validate_mutex); > + mutex_unlock(&loop_validate_mutex); > } > > static int max_part; > @@ -523,6 +518,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(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 > @@ -536,7 +575,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; > @@ -554,46 +592,21 @@ 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 (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; > - 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(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; > - loop_global_unlock(lo, is_loop); > > /* > * Flush loop_validate_file() before fput(), for l->lo_backing_file > @@ -618,8 +631,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); > @@ -974,61 +985,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(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) || > @@ -1039,10 +1018,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 */ > @@ -1059,7 +1036,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 > @@ -1080,14 +1057,66 @@ 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); > > - loop_global_unlock(lo, is_loop); > + 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; > + } > + > + 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) > loop_reread_partitions(lo); > > @@ -1096,8 +1125,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); > @@ -1194,11 +1221,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; > } > /* > @@ -1210,7 +1237,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; > } Overall this change looks good to me. But I see, lo->lo_lock is used to protect lo->lo_backing_file. so shall we annotate the lo->lo_backing_file using __guarded_by(&lo_lock)? Thanks, --Nilay