This is the wrong way to approach it. Instead of making things ever more complex try to make it simpler. In this case, ensure that the destroy_workqueue is not held with pointless locks. Given that the loop device already is known to not have a reference and marked as in the rundown state there shouldn't be anything that is required to be protected by lo_mutex. So something like this untested patch should probably do the work: diff --git a/drivers/block/loop.c b/drivers/block/loop.c index fa1c298a8cfb..c734dc768316 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1347,16 +1347,15 @@ static int __loop_clr_fd(struct loop_device *lo, bool release) * became visible. */ - mutex_lock(&lo->lo_mutex); if (WARN_ON_ONCE(lo->lo_state != Lo_rundown)) { err = -ENXIO; - goto out_unlock; + goto out; } filp = lo->lo_backing_file; if (filp == NULL) { err = -EINVAL; - goto out_unlock; + goto out; } if (test_bit(QUEUE_FLAG_WC, &lo->lo_queue->queue_flags)) @@ -1366,6 +1365,8 @@ static int __loop_clr_fd(struct loop_device *lo, bool release) blk_mq_freeze_queue(lo->lo_queue); destroy_workqueue(lo->workqueue); + + mutex_lock(&lo->lo_mutex); spin_lock_irq(&lo->lo_work_lock); list_for_each_entry_safe(worker, pos, &lo->idle_worker_list, idle_list) { @@ -1413,8 +1414,8 @@ static int __loop_clr_fd(struct loop_device *lo, bool release) partscan = lo->lo_flags & LO_FLAGS_PARTSCAN && bdev; lo_number = lo->lo_number; disk_force_media_change(lo->lo_disk, DISK_EVENT_MEDIA_CHANGE); -out_unlock: mutex_unlock(&lo->lo_mutex); + if (partscan) { /* * open_mutex has been held already in release path, so don't @@ -1435,7 +1436,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release) /* Device is gone, no point in returning error */ err = 0; } - +out: /* * lo->lo_state is set to Lo_unbound here after above partscan has * finished.