diff --git a/drivers/block/loop.c b/drivers/block/loop.c index d58d68f3c7cd..c8a06bad1581 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1891,7 +1891,7 @@ static int lo_open(struct block_device *bdev, fmode_t mode) if (err) return err; lo = bdev->bd_disk->private_data; - if (!lo) { + if (!lo || lo->wait_removal) { mutex_unlock(&loop_ctl_mutex); return -ENXIO; } @@ -2188,7 +2188,10 @@ static int loop_add(struct loop_device **l, int i) static void loop_remove(struct loop_device *lo) { + lo->wait_removal = true; + mutex_unlock(&loop_ctl_mutex); del_gendisk(lo->lo_disk); + mutex_lock(&loop_ctl_mutex); blk_cleanup_queue(lo->lo_queue); blk_mq_free_tag_set(&lo->tag_set); put_disk(lo->lo_disk); @@ -2201,7 +2204,7 @@ static int find_free_cb(int id, void *ptr, void *data) struct loop_device *lo = ptr; struct loop_device **l = data; - if (lo->lo_state == Lo_unbound) { + if (lo->lo_state == Lo_unbound && !lo->wait_removal) { *l = lo; return 1; } @@ -2254,6 +2257,13 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd, struct loop_device *lo; int ret; + if (0) { +unlock_and_retry: + mutex_unlock(&loop_ctl_mutex); + if (schedule_timeout_killable(HZ / 10)) + return -EINTR; + } + debug_check_no_locks_held(); ret = mutex_lock_killable(&loop_ctl_mutex); if (ret) return ret; @@ -2263,6 +2273,8 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd, case LOOP_CTL_ADD: ret = loop_lookup(&lo, parm); if (ret >= 0) { + if (lo->wait_removal) + goto unlock_and_retry; ret = -EEXIST; break; } @@ -2272,6 +2284,8 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd, ret = loop_lookup(&lo, parm); if (ret < 0) break; + if (lo->wait_removal) + goto unlock_and_retry; ret = mutex_lock_killable(&lo->lo_mutex); if (ret) break; @@ -2286,9 +2300,10 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd, break; } lo->lo_disk->private_data = NULL; + parm = lo->lo_number; mutex_unlock(&lo->lo_mutex); - idr_remove(&loop_index_idr, lo->lo_number); loop_remove(lo); + idr_remove(&loop_index_idr, parm); break; case LOOP_CTL_GET_FREE: ret = loop_lookup(&lo, -1); diff --git a/drivers/block/loop.h b/drivers/block/loop.h index a3c04f310672..6272700ae8c1 100644 --- a/drivers/block/loop.h +++ b/drivers/block/loop.h @@ -58,6 +58,7 @@ struct loop_device { struct task_struct *worker_task; bool use_dio; bool sysfs_inited; + bool wait_removal; struct request_queue *lo_queue; struct blk_mq_tag_set tag_set;