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 --- 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);