Since __loop_clr_fd() is currently holding loop_validate_mutex and lo->lo_mutex, "avoid lo_mutex in ->release" part is incomplete. The intent of holding loop_validate_mutex (which causes disk->open_mutex => loop_validate_mutex => lo->lo_mutex => blk_mq_freeze_queue()/blk_mq_unfreeze_queue() chain) is to make loop_validate_file() traversal safe. Since ->release is called with disk->open_mutex held, and __loop_clr_fd() from lo_release() is called via ->release when disk_openers() == 0, we are guaranteed that "struct file" which will be passed to loop_validate_file() via fget() cannot be the loop device __loop_clr_fd(lo, true) will clear. Thus, there is no need to hold loop_validate_mutex and lo->lo_mutex from __loop_clr_fd() if release == true. diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 2506193a4fd1..d47f3d86dd55 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1136,25 +1136,26 @@ static void __loop_clr_fd(struct loop_device *lo, bool release) gfp_t gfp = lo->old_gfp_mask; /* - * Flush loop_configure() and loop_change_fd(). It is acceptable for - * loop_validate_file() to succeed, for actual clear operation has not - * started yet. - */ - mutex_lock(&loop_validate_mutex); - mutex_unlock(&loop_validate_mutex); - /* - * loop_validate_file() now fails because l->lo_state != Lo_bound - * became visible. + * Make sure that Lo_rundown state becomes visible to loop_configure() + * and loop_change_fd(). When called from ->release, we are guaranteed + * that the "struct file" which loop_configure()/loop_change_fd() found + * via fget() is not this loop device. */ + if (!release) { + mutex_lock(&loop_validate_mutex); + mutex_unlock(&loop_validate_mutex); + } /* - * Since this function is called upon "ioctl(LOOP_CLR_FD)" xor "close() - * after ioctl(LOOP_CLR_FD)", it is a sign of something going wrong if - * lo->lo_state has changed while waiting for lo->lo_mutex. + * It is a sign of something going wrong if lo->lo_state has changed + * while waiting for lo->lo_mutex. When called from ->release, we are + * guaranteed that the nobody is using this loop device. */ - mutex_lock(&lo->lo_mutex); - BUG_ON(lo->lo_state != Lo_rundown); - mutex_unlock(&lo->lo_mutex); + if (!release) { + mutex_lock(&lo->lo_mutex); + BUG_ON(lo->lo_state != Lo_rundown); + mutex_unlock(&lo->lo_mutex); + } if (test_bit(QUEUE_FLAG_WC, &lo->lo_queue->queue_flags)) blk_queue_write_cache(lo->lo_queue, false, false); But thinking again about release == false case, which I wrote "It is acceptable for loop_validate_file() to succeed, for actual clear operation has not started yet.", I came to feel why it is acceptable to succeed. Even if loop_validate_file() was safely traversed due to serialization via loop_validate_mutex, I/O requests after loop_configure()/loop_change_fd() completed will fail. Is this behavior what we want? If we don't want I/O requests after loop_configure()/loop_change_fd() completed fail due to __loop_clr_fd(), it is not acceptable for loop_validate_file() to succeed. We should hold loop_validate_mutex before setting Lo_rundown in order to make sure that loop_validate_file() will see Lo_rundown state. That is, something like below will be expected? diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 2506193a4fd1..a4ff94ca654f 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1135,27 +1135,6 @@ static void __loop_clr_fd(struct loop_device *lo, bool release) struct file *filp; gfp_t gfp = lo->old_gfp_mask; - /* - * Flush loop_configure() and loop_change_fd(). It is acceptable for - * loop_validate_file() to succeed, for actual clear operation has not - * started yet. - */ - mutex_lock(&loop_validate_mutex); - mutex_unlock(&loop_validate_mutex); - /* - * loop_validate_file() now fails because l->lo_state != Lo_bound - * became visible. - */ - - /* - * Since this function is called upon "ioctl(LOOP_CLR_FD)" xor "close() - * after ioctl(LOOP_CLR_FD)", it is a sign of something going wrong if - * lo->lo_state has changed while waiting for lo->lo_mutex. - */ - mutex_lock(&lo->lo_mutex); - BUG_ON(lo->lo_state != Lo_rundown); - mutex_unlock(&lo->lo_mutex); - if (test_bit(QUEUE_FLAG_WC, &lo->lo_queue->queue_flags)) blk_queue_write_cache(lo->lo_queue, false, false); @@ -1238,11 +1217,18 @@ static int loop_clr_fd(struct loop_device *lo) { int err; - err = mutex_lock_killable(&lo->lo_mutex); + /* + * Use global lock when setting Lo_rundown state in order to make sure + * that loop_validate_file() will fail if the "struct file" which + * loop_configure()/loop_change_fd() found via fget() was this loop + * device. The disk_openers(lo->lo_disk) > 1 test below guarantees that + * fget() did not return this loop device. + */ + err = loop_global_lock_killable(lo, true); if (err) return err; if (lo->lo_state != Lo_bound) { - mutex_unlock(&lo->lo_mutex); + loop_global_unlock(lo, true); return -ENXIO; } /* @@ -1257,11 +1243,11 @@ static int loop_clr_fd(struct loop_device *lo) */ if (disk_openers(lo->lo_disk) > 1) { lo->lo_flags |= LO_FLAGS_AUTOCLEAR; - mutex_unlock(&lo->lo_mutex); + loop_global_unlock(lo, true); return 0; } lo->lo_state = Lo_rundown; - mutex_unlock(&lo->lo_mutex); + loop_global_unlock(lo, true); __loop_clr_fd(lo, false); return 0;