]> www.infradead.org Git - users/hch/block.git/commitdiff
loop: avoid holding loop_ctl_mutex over add_disk loop-locking
authorChristoph Hellwig <hch@lst.de>
Tue, 17 Aug 2021 13:37:30 +0000 (15:37 +0200)
committerChristoph Hellwig <hch@lst.de>
Thu, 26 Aug 2021 09:55:17 +0000 (11:55 +0200)
To avoid complex lock ordering issues loop_ctl_mutex should not
be held over add_disk.  Add a new Lo_new state for a loop device
that has just been created but which is not live yet.

Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/block/loop.c
drivers/block/loop.h

index fe249662fa1bebe9df0280e75bdeffce21aebff7..cb071364abbbdc46d8af31cb1cd38f94b66149ea 100644 (file)
@@ -2328,7 +2328,11 @@ static int loop_add(int i)
        lo = kzalloc(sizeof(*lo), GFP_KERNEL);
        if (!lo)
                goto out;
-       lo->lo_state = Lo_unbound;
+       lo->lo_state = Lo_new;
+       atomic_set(&lo->lo_refcnt, 0);
+       mutex_init(&lo->lo_mutex);
+       spin_lock_init(&lo->lo_lock);
+       spin_lock_init(&lo->lo_work_lock);
 
        err = mutex_lock_killable(&loop_ctl_mutex);
        if (err)
@@ -2342,9 +2346,11 @@ static int loop_add(int i)
        } else {
                err = idr_alloc(&loop_index_idr, lo, 0, 0, GFP_KERNEL);
        }
-       if (err < 0)
-               goto out_unlock;
        i = err;
+       lo->lo_number = i;
+       mutex_unlock(&loop_ctl_mutex);
+       if (err < 0)
+               goto out_free_dev;
 
        err = -ENOMEM;
        lo->tag_set.ops = &loop_mq_ops;
@@ -2398,11 +2404,6 @@ static int loop_add(int i)
        if (!part_shift)
                disk->flags |= GENHD_FL_NO_PART_SCAN;
        disk->flags |= GENHD_FL_EXT_DEVT;
-       atomic_set(&lo->lo_refcnt, 0);
-       mutex_init(&lo->lo_mutex);
-       lo->lo_number           = i;
-       spin_lock_init(&lo->lo_lock);
-       spin_lock_init(&lo->lo_work_lock);
        disk->major             = LOOP_MAJOR;
        disk->first_minor       = i << part_shift;
        disk->minors            = 1 << part_shift;
@@ -2413,14 +2414,18 @@ static int loop_add(int i)
        disk->event_flags       = DISK_EVENT_FLAG_UEVENT;
        sprintf(disk->disk_name, "loop%d", i);
        add_disk(disk);
-       mutex_unlock(&loop_ctl_mutex);
+
+       mutex_lock(&lo->lo_mutex);
+       lo->lo_state = Lo_unbound;
+       mutex_unlock(&lo->lo_mutex);
+
        return i;
 
 out_cleanup_tags:
        blk_mq_free_tag_set(&lo->tag_set);
 out_free_idr:
+       mutex_lock(&loop_ctl_mutex);
        idr_remove(&loop_index_idr, i);
-out_unlock:
        mutex_unlock(&loop_ctl_mutex);
 out_free_dev:
        kfree(lo);
index d14ce6bdc014cc4bb801e582ec05063aab794274..608a20c23c6412ddfd7bdba61cbd050f968937e1 100644 (file)
@@ -24,6 +24,7 @@ enum {
        Lo_bound,
        Lo_rundown,
        Lo_deleting,
+       Lo_new,
 };
 
 struct loop_func_table;