All of lore.kernel.org
 help / color / mirror / Atom feed
* [syzbot] possible deadlock in __loop_clr_fd (3)
@ 2021-11-10 17:00 syzbot
  2021-11-10 22:10 ` Tetsuo Handa
  0 siblings, 1 reply; 10+ messages in thread
From: syzbot @ 2021-11-10 17:00 UTC (permalink / raw)
  To: axboe, linux-block, linux-kernel, syzkaller-bugs

Hello,

syzbot found the following issue on:

HEAD commit:    cb690f5238d7 Merge tag 'for-5.16/drivers-2021-11-09' of gi..
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=1611368ab00000
kernel config:  https://syzkaller.appspot.com/x/.config?x=9d7259f0deb293aa
dashboard link: https://syzkaller.appspot.com/bug?extid=63614029dfb79abd4383
compiler:       gcc (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2

Unfortunately, I don't have any reproducer for this issue yet.

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+63614029dfb79abd4383@syzkaller.appspotmail.com

======================================================
WARNING: possible circular locking dependency detected
5.15.0-syzkaller #0 Not tainted
------------------------------------------------------
syz-executor.1/23454 is trying to acquire lock:
ffff8880222c2938 ((wq_completion)loop1){+.+.}-{0:0}, at: flush_workqueue+0xe1/0x15b0 kernel/workqueue.c:2815

but task is already holding lock:
ffff88801a7e9360 (&lo->lo_mutex){+.+.}-{3:3}, at: __loop_clr_fd+0x7a/0x1070 drivers/block/loop.c:1106

which lock already depends on the new lock.


the existing dependency chain (in reverse order) is:

-> #7 (&lo->lo_mutex){+.+.}-{3:3}:
       __mutex_lock_common kernel/locking/mutex.c:607 [inline]
       __mutex_lock+0x12f/0x12f0 kernel/locking/mutex.c:740
       lo_open+0x75/0x120 drivers/block/loop.c:1733
       blkdev_get_whole+0x99/0x2d0 block/bdev.c:671
       blkdev_get_by_dev.part.0+0x354/0xb50 block/bdev.c:826
       blkdev_get_by_dev+0x6b/0x80 block/bdev.c:859
       blkdev_open+0x154/0x2e0 block/fops.c:501
       do_dentry_open+0x4c8/0x1250 fs/open.c:822
       do_open fs/namei.c:3426 [inline]
       path_openat+0x1cad/0x2750 fs/namei.c:3559
       do_filp_open+0x1aa/0x400 fs/namei.c:3586
       do_sys_openat2+0x16d/0x4d0 fs/open.c:1212
       do_sys_open fs/open.c:1228 [inline]
       __do_sys_open fs/open.c:1236 [inline]
       __se_sys_open fs/open.c:1232 [inline]
       __x64_sys_open+0x119/0x1c0 fs/open.c:1232
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x44/0xae

-> #6 (&disk->open_mutex){+.+.}-{3:3}:
       __mutex_lock_common kernel/locking/mutex.c:607 [inline]
       __mutex_lock+0x12f/0x12f0 kernel/locking/mutex.c:740
       blkdev_get_by_dev.part.0+0x24e/0xb50 block/bdev.c:819
       blkdev_get_by_dev+0x6b/0x80 block/bdev.c:859
       swsusp_check+0x97/0x2f0 kernel/power/swap.c:1520
       software_resume.part.0+0x102/0x1f0 kernel/power/hibernate.c:979
       software_resume kernel/power/hibernate.c:86 [inline]
       resume_store+0x161/0x190 kernel/power/hibernate.c:1181
       kobj_attr_store+0x50/0x80 lib/kobject.c:856
       sysfs_kf_write+0x110/0x160 fs/sysfs/file.c:136
       kernfs_fop_write_iter+0x342/0x500 fs/kernfs/file.c:296
       call_write_iter include/linux/fs.h:2162 [inline]
       new_sync_write+0x429/0x660 fs/read_write.c:503
       vfs_write+0x7cd/0xae0 fs/read_write.c:590
       ksys_write+0x12d/0x250 fs/read_write.c:643
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x44/0xae

-> #5 (system_transition_mutex/1){+.+.}-{3:3}:
       __mutex_lock_common kernel/locking/mutex.c:607 [inline]
       __mutex_lock+0x12f/0x12f0 kernel/locking/mutex.c:740
       software_resume.part.0+0x19/0x1f0 kernel/power/hibernate.c:934
       software_resume kernel/power/hibernate.c:86 [inline]
       resume_store+0x161/0x190 kernel/power/hibernate.c:1181
       kobj_attr_store+0x50/0x80 lib/kobject.c:856
       sysfs_kf_write+0x110/0x160 fs/sysfs/file.c:136
       kernfs_fop_write_iter+0x342/0x500 fs/kernfs/file.c:296
       call_write_iter include/linux/fs.h:2162 [inline]
       new_sync_write+0x429/0x660 fs/read_write.c:503
       vfs_write+0x7cd/0xae0 fs/read_write.c:590
       ksys_write+0x12d/0x250 fs/read_write.c:643
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x44/0xae

-> #4 (&of->mutex){+.+.}-{3:3}:
       __mutex_lock_common kernel/locking/mutex.c:607 [inline]
       __mutex_lock+0x12f/0x12f0 kernel/locking/mutex.c:740
       kernfs_fop_write_iter+0x287/0x500 fs/kernfs/file.c:287
       call_write_iter include/linux/fs.h:2162 [inline]
       do_iter_readv_writev+0x472/0x750 fs/read_write.c:725
       do_iter_write+0x188/0x710 fs/read_write.c:851
       vfs_iter_write+0x70/0xa0 fs/read_write.c:892
       iter_file_splice_write+0x723/0xc70 fs/splice.c:689
       do_splice_from fs/splice.c:767 [inline]
       do_splice+0xb7e/0x1960 fs/splice.c:1079
       __do_splice+0x134/0x250 fs/splice.c:1144
       __do_sys_splice fs/splice.c:1350 [inline]
       __se_sys_splice fs/splice.c:1332 [inline]
       __x64_sys_splice+0x198/0x250 fs/splice.c:1332
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x44/0xae

-> #3 (&pipe->mutex/1){+.+.}-{3:3}:
       __mutex_lock_common kernel/locking/mutex.c:607 [inline]
       __mutex_lock+0x12f/0x12f0 kernel/locking/mutex.c:740
       pipe_lock_nested fs/pipe.c:81 [inline]
       pipe_lock+0x5a/0x70 fs/pipe.c:89
       iter_file_splice_write+0x183/0xc70 fs/splice.c:635
       do_splice_from fs/splice.c:767 [inline]
       do_splice+0xb7e/0x1960 fs/splice.c:1079
       __do_splice+0x134/0x250 fs/splice.c:1144
       __do_sys_splice fs/splice.c:1350 [inline]
       __se_sys_splice fs/splice.c:1332 [inline]
       __x64_sys_splice+0x198/0x250 fs/splice.c:1332
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x44/0xae

-> #2 (sb_writers#3){.+.+}-{0:0}:
       percpu_down_read include/linux/percpu-rwsem.h:51 [inline]
       __sb_start_write include/linux/fs.h:1810 [inline]
       sb_start_write include/linux/fs.h:1880 [inline]
       file_start_write include/linux/fs.h:3009 [inline]
       lo_write_bvec drivers/block/loop.c:242 [inline]
       lo_write_simple drivers/block/loop.c:265 [inline]
       do_req_filebacked drivers/block/loop.c:494 [inline]
       loop_handle_cmd drivers/block/loop.c:1857 [inline]
       loop_process_work+0x1499/0x1db0 drivers/block/loop.c:1897
       process_one_work+0x9b2/0x1690 kernel/workqueue.c:2298
       worker_thread+0x658/0x11f0 kernel/workqueue.c:2445
       kthread+0x405/0x4f0 kernel/kthread.c:327
       ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:295

-> #1 ((work_completion)(&lo->rootcg_work)){+.+.}-{0:0}:
       process_one_work+0x921/0x1690 kernel/workqueue.c:2274
       worker_thread+0x658/0x11f0 kernel/workqueue.c:2445
       kthread+0x405/0x4f0 kernel/kthread.c:327
       ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:295

-> #0 ((wq_completion)loop1){+.+.}-{0:0}:
       check_prev_add kernel/locking/lockdep.c:3063 [inline]
       check_prevs_add kernel/locking/lockdep.c:3186 [inline]
       validate_chain kernel/locking/lockdep.c:3801 [inline]
       __lock_acquire+0x2a07/0x54a0 kernel/locking/lockdep.c:5027
       lock_acquire kernel/locking/lockdep.c:5637 [inline]
       lock_acquire+0x1ab/0x510 kernel/locking/lockdep.c:5602
       flush_workqueue+0x110/0x15b0 kernel/workqueue.c:2818
       drain_workqueue+0x1a5/0x3c0 kernel/workqueue.c:2983
       destroy_workqueue+0x71/0x800 kernel/workqueue.c:4420
       __loop_clr_fd+0x1de/0x1070 drivers/block/loop.c:1124
       loop_clr_fd drivers/block/loop.c:1237 [inline]
       lo_ioctl+0x398/0x17c0 drivers/block/loop.c:1562
       blkdev_ioctl+0x37a/0x800 block/ioctl.c:597
       vfs_ioctl fs/ioctl.c:51 [inline]
       __do_sys_ioctl fs/ioctl.c:874 [inline]
       __se_sys_ioctl fs/ioctl.c:860 [inline]
       __x64_sys_ioctl+0x193/0x200 fs/ioctl.c:860
       do_syscall_x64 arch/x86/entry/common.c:50 [inline]
       do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
       entry_SYSCALL_64_after_hwframe+0x44/0xae

other info that might help us debug this:

Chain exists of:
  (wq_completion)loop1 --> &disk->open_mutex --> &lo->lo_mutex

 Possible unsafe locking scenario:

       CPU0                    CPU1
       ----                    ----
  lock(&lo->lo_mutex);
                               lock(&disk->open_mutex);
                               lock(&lo->lo_mutex);
  lock((wq_completion)loop1);

 *** DEADLOCK ***

1 lock held by syz-executor.1/23454:
 #0: ffff88801a7e9360 (&lo->lo_mutex){+.+.}-{3:3}, at: __loop_clr_fd+0x7a/0x1070 drivers/block/loop.c:1106

stack backtrace:
CPU: 1 PID: 23454 Comm: syz-executor.1 Not tainted 5.15.0-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:88 [inline]
 dump_stack_lvl+0xcd/0x134 lib/dump_stack.c:106
 check_noncircular+0x25f/0x2e0 kernel/locking/lockdep.c:2143
 check_prev_add kernel/locking/lockdep.c:3063 [inline]
 check_prevs_add kernel/locking/lockdep.c:3186 [inline]
 validate_chain kernel/locking/lockdep.c:3801 [inline]
 __lock_acquire+0x2a07/0x54a0 kernel/locking/lockdep.c:5027
 lock_acquire kernel/locking/lockdep.c:5637 [inline]
 lock_acquire+0x1ab/0x510 kernel/locking/lockdep.c:5602
 flush_workqueue+0x110/0x15b0 kernel/workqueue.c:2818
 drain_workqueue+0x1a5/0x3c0 kernel/workqueue.c:2983
 destroy_workqueue+0x71/0x800 kernel/workqueue.c:4420
 __loop_clr_fd+0x1de/0x1070 drivers/block/loop.c:1124
 loop_clr_fd drivers/block/loop.c:1237 [inline]
 lo_ioctl+0x398/0x17c0 drivers/block/loop.c:1562
 blkdev_ioctl+0x37a/0x800 block/ioctl.c:597
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:874 [inline]
 __se_sys_ioctl fs/ioctl.c:860 [inline]
 __x64_sys_ioctl+0x193/0x200 fs/ioctl.c:860
 do_syscall_x64 arch/x86/entry/common.c:50 [inline]
 do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
 entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7f77a49078a7
Code: 3c 1c 48 f7 d8 49 39 c4 72 b8 e8 04 54 02 00 85 c0 78 bd 48 83 c4 08 4c 89 e0 5b 41 5c c3 0f 1f 44 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 bc ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f77a4f4ed78 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 00007f77a4f4ee00 RCX: 00007f77a49078a7
RDX: 0000000000000000 RSI: 0000000000004c01 RDI: 0000000000000003
RBP: 0000000000000003 R08: 0000000000000000 R09: 00007f77a4f4ec10
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000032
R13: 0000000000000000 R14: 0000000000000014 R15: 00007f77a4f4ee40
 </TASK>


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [syzbot] possible deadlock in __loop_clr_fd (3)
  2021-11-10 17:00 [syzbot] possible deadlock in __loop_clr_fd (3) syzbot
@ 2021-11-10 22:10 ` Tetsuo Handa
  2021-11-11  0:55   ` Dan Schatzberg
  0 siblings, 1 reply; 10+ messages in thread
From: Tetsuo Handa @ 2021-11-10 22:10 UTC (permalink / raw)
  To: Dan Schatzberg, Ming Lei; +Cc: axboe, linux-block

Hello.

Commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker")
is calling destroy_workqueue() with lo->lo_mutex held, and syzbot is
reporting circular locking dependency

  &disk->open_mutex => &lo->lo_mutex => (wq_completion)loop$M =>
  (work_completion)(&lo->rootcg_work) => sb_writers#$N => &p->lock =>
  &of->mutex => system_transition_mutex/1 => &disk->open_mutex

. Can you somehow call destroy_workqueue() without holding a lock (e.g.
breaking &lo->lo_mutex => (wq_completion)loop$M dependency chain by
making sure that below change is safe) ?

@@ -1365,7 +1365,9 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
        /* freeze request queue during the transition */
        blk_mq_freeze_queue(lo->lo_queue);

+       mutex_unlock(&lo->lo_mutex);
        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) {

On 2021/11/11 2:00, syzbot wrote:
> Hello,
> 
> syzbot found the following issue on:
> 
> HEAD commit:    cb690f5238d7 Merge tag 'for-5.16/drivers-2021-11-09' of gi..
> git tree:       upstream
> console output: https://syzkaller.appspot.com/x/log.txt?x=1611368ab00000
> kernel config:  https://syzkaller.appspot.com/x/.config?x=9d7259f0deb293aa
> dashboard link: https://syzkaller.appspot.com/bug?extid=63614029dfb79abd4383
> compiler:       gcc (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [syzbot] possible deadlock in __loop_clr_fd (3)
  2021-11-10 22:10 ` Tetsuo Handa
@ 2021-11-11  0:55   ` Dan Schatzberg
  2021-11-11 15:14     ` Tetsuo Handa
  0 siblings, 1 reply; 10+ messages in thread
From: Dan Schatzberg @ 2021-11-11  0:55 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: Ming Lei, axboe, linux-block

On Thu, Nov 11, 2021 at 07:10:56AM +0900, Tetsuo Handa wrote:
> Hello.
> 
> Commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker")
> is calling destroy_workqueue() with lo->lo_mutex held, and syzbot is
> reporting circular locking dependency

The commit changed the logic from a separate kthread + queue to a
workqueue. So I don't believe this changed anything regarding this
circular locking dependency, it's just that the dependency is now
clear via workqueue whereas it wasn't with the kthread.

> 
>   &disk->open_mutex => &lo->lo_mutex => (wq_completion)loop$M =>
>   (work_completion)(&lo->rootcg_work) => sb_writers#$N => &p->lock =>
>   &of->mutex => system_transition_mutex/1 => &disk->open_mutex
> 
> . Can you somehow call destroy_workqueue() without holding a lock (e.g.
> breaking &lo->lo_mutex => (wq_completion)loop$M dependency chain by
> making sure that below change is safe) ?

It's really not clear to me - the lo_mutex protects a lot of entry
points (ioctls and others) and it's hard to tell if the observed state
will be sane if they can race in the middle of __loop_clr_fd.

> 
> @@ -1365,7 +1365,9 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
>         /* freeze request queue during the transition */
>         blk_mq_freeze_queue(lo->lo_queue);
> 
> +       mutex_unlock(&lo->lo_mutex);
>         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) {
> 
> On 2021/11/11 2:00, syzbot wrote:
> > Hello,
> > 
> > syzbot found the following issue on:
> > 
> > HEAD commit:    cb690f5238d7 Merge tag 'for-5.16/drivers-2021-11-09' of gi..
> > git tree:       upstream
> > console output: https://syzkaller.appspot.com/x/log.txt?x=1611368ab00000
> > kernel config:  https://syzkaller.appspot.com/x/.config?x=9d7259f0deb293aa
> > dashboard link: https://syzkaller.appspot.com/bug?extid=63614029dfb79abd4383
> > compiler:       gcc (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [syzbot] possible deadlock in __loop_clr_fd (3)
  2021-11-11  0:55   ` Dan Schatzberg
@ 2021-11-11 15:14     ` Tetsuo Handa
  2021-11-12  6:20       ` Christoph Hellwig
  0 siblings, 1 reply; 10+ messages in thread
From: Tetsuo Handa @ 2021-11-11 15:14 UTC (permalink / raw)
  To: Dan Schatzberg; +Cc: Ming Lei, axboe, linux-block, Christoph Hellwig

On 2021/11/11 9:55, Dan Schatzberg wrote:
>> . Can you somehow call destroy_workqueue() without holding a lock (e.g.
>> breaking &lo->lo_mutex => (wq_completion)loop$M dependency chain by
>> making sure that below change is safe) ?
> 
> It's really not clear to me - the lo_mutex protects a lot of entry
> points (ioctls and others) and it's hard to tell if the observed state
> will be sane if they can race in the middle of __loop_clr_fd.
> 

I'm not familiar with the block layer, but I think it is clear.
We check lo_state with lo_mutex held.
The loop functions fail if lo_state is not what each function expected.

Is blk_mq_freeze_queue() for waiting for "loop_queue_work() from loop_queue_rq()" to
complete and for blocking further loop_queue_rq() until blk_mq_unfreeze_queue() ?
If yes, we need to call blk_mq_freeze_queue() before destroy_workqueue().
But I think lo_state is helping us, like a patch shown below.



From e36f23c081e0b8ed08239f4e3fbc954b4d7d3feb Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Thu, 11 Nov 2021 23:55:43 +0900
Subject: [PATCH] loop: destroy workqueue without holding locks

syzbot is reporting circular locking problem at __loop_clr_fd() [1], for
commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker")
is calling destroy_workqueue() with lo->lo_mutex held.

Since all functions where lo->lo_state matters are already checking
lo->lo_state with lo->lo_mutex held (in order to avoid racing with e.g.
ioctl(LOOP_CTL_REMOVE)), and __loop_clr_fd() can be called from either
ioctl(LOOP_CLR_FD) or close(), lo->lo_state == Lo_rundown is considered
as an exclusive lock for __loop_clr_fd(). Therefore, it is safe to
temporarily drop lo->lo_mutex when calling destroy_workqueue().

Link: https://syzkaller.appspot.com/bug?extid=63614029dfb79abd4383 [1]
Reported-by: syzbot <syzbot+63614029dfb79abd4383@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 drivers/block/loop.c | 34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index a154cab6cd98..b98ec1c2d950 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1104,16 +1104,20 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	 */
 
 	mutex_lock(&lo->lo_mutex);
-	if (WARN_ON_ONCE(lo->lo_state != Lo_rundown)) {
-		err = -ENXIO;
-		goto out_unlock;
-	}
+	/*
+	 * 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.
+	 */
+	BUG_ON(lo->lo_state != Lo_rundown);
 
+	/*
+	 * Since ioctl(LOOP_CLR_FD) depends on lo->lo_state == Lo_bound, it is
+	 * a sign of something going wrong if lo->lo_backing_file was not
+	 * assigned by ioctl(LOOP_SET_FD) or ioctl(LOOP_CONFIGURE).
+	 */
 	filp = lo->lo_backing_file;
-	if (filp == NULL) {
-		err = -EINVAL;
-		goto out_unlock;
-	}
+	BUG_ON(!filp);
 
 	if (test_bit(QUEUE_FLAG_WC, &lo->lo_queue->queue_flags))
 		blk_queue_write_cache(lo->lo_queue, false, false);
@@ -1121,7 +1125,20 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	/* freeze request queue during the transition */
 	blk_mq_freeze_queue(lo->lo_queue);
 
+	/*
+	 * To avoid circular locking dependency, call destroy_workqueue()
+	 * without holding lo->lo_mutex.
+	 */
+	mutex_unlock(&lo->lo_mutex);
 	destroy_workqueue(lo->workqueue);
+	mutex_lock(&lo->lo_mutex);
+
+	/*
+	 * As explained above, lo->lo_state cannot be changed while waiting for
+	 * lo->lo_mutex.
+	 */
+	BUG_ON(lo->lo_state != Lo_rundown);
+
 	spin_lock_irq(&lo->lo_work_lock);
 	list_for_each_entry_safe(worker, pos, &lo->idle_worker_list,
 				idle_list) {
@@ -1156,7 +1173,6 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
 	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) {
 		/*
-- 
2.18.4



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [syzbot] possible deadlock in __loop_clr_fd (3)
  2021-11-11 15:14     ` Tetsuo Handa
@ 2021-11-12  6:20       ` Christoph Hellwig
  2021-11-12 16:25         ` Tetsuo Handa
  0 siblings, 1 reply; 10+ messages in thread
From: Christoph Hellwig @ 2021-11-12  6:20 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Dan Schatzberg, Ming Lei, axboe, linux-block, Christoph Hellwig

Hi Tetsuo,

I think this approach is fine, but we can further simplify it.

> +	/*
> +	 * Since ioctl(LOOP_CLR_FD) depends on lo->lo_state == Lo_bound, it is
> +	 * a sign of something going wrong if lo->lo_backing_file was not
> +	 * assigned by ioctl(LOOP_SET_FD) or ioctl(LOOP_CONFIGURE).
> +	 */
>  	filp = lo->lo_backing_file;
> -	if (filp == NULL) {
> -		err = -EINVAL;
> -		goto out_unlock;
> -	}
> +	BUG_ON(!filp);

I'd just drop the check here entirely.

>  	if (test_bit(QUEUE_FLAG_WC, &lo->lo_queue->queue_flags))
>  		blk_queue_write_cache(lo->lo_queue, false, false);
> @@ -1121,7 +1125,20 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
>  	/* freeze request queue during the transition */
>  	blk_mq_freeze_queue(lo->lo_queue);
>  
> +	/*
> +	 * To avoid circular locking dependency, call destroy_workqueue()
> +	 * without holding lo->lo_mutex.
> +	 */
> +	mutex_unlock(&lo->lo_mutex);
>  	destroy_workqueue(lo->workqueue);
> +	mutex_lock(&lo->lo_mutex);

As far as I can tell there is absolutely no need to hold lo_mutex
above these changes at all, as the Lo_rundown check prevents
access to all the other fields we're changing.  So I think we can
drop this entire critical section and just keep the one at the
end of the funtion where lo_state is changed.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [syzbot] possible deadlock in __loop_clr_fd (3)
  2021-11-12  6:20       ` Christoph Hellwig
@ 2021-11-12 16:25         ` Tetsuo Handa
  2021-11-15  9:58           ` Christoph Hellwig
  0 siblings, 1 reply; 10+ messages in thread
From: Tetsuo Handa @ 2021-11-12 16:25 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Dan Schatzberg, Ming Lei, axboe, linux-block

On 2021/11/12 15:20, Christoph Hellwig wrote:
>> @@ -1121,7 +1125,20 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
>>  	/* freeze request queue during the transition */
>>  	blk_mq_freeze_queue(lo->lo_queue);
>>  
>> +	/*
>> +	 * To avoid circular locking dependency, call destroy_workqueue()
>> +	 * without holding lo->lo_mutex.
>> +	 */
>> +	mutex_unlock(&lo->lo_mutex);
>>  	destroy_workqueue(lo->workqueue);
>> +	mutex_lock(&lo->lo_mutex);
> 
> As far as I can tell there is absolutely no need to hold lo_mutex
> above these changes at all, as the Lo_rundown check prevents
> access to all the other fields we're changing.  So I think we can
> drop this entire critical section and just keep the one at the
> end of the funtion where lo_state is changed.

Indeed, since the access pattern is

  mutex_lock(&lo->lo_mutex);
  if (lo->lo_state == Lo_expected_state) {
    /* Do something here. */
    lo->lo_state = Lo_new_state;
  }
  mutex_unlock(&lo->lo_mutex);

, assigning a dedicated state for individual operation (e.g.
Lo_deleting for loop_remove(), Lo_rundown for __loop_clr_fd())
eliminates the need to hold lo->lo_mutex throughout that operation
(which in turn helps shortening locking dependency chains).

  mutex_lock(&lo->lo_mutex);
  if (lo->lo_state != Lo_expected_state) {
    mutex_unlock(&lo->lo_mutex);
    return;
  }
  lo->lo_state = Lo_state_for_this_operation;
  mutex_unlock(&lo->lo_mutex);
  /* Do something here without lo->lo_mutex. */
  mutex_lock(&lo->lo_mutex);
  lo->lo_state = Lo_new_state;
  mutex_unlock(&lo->lo_mutex);

From 4ba8c1de297d6a09649a434ac4fa81e3f07bedba Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Sat, 13 Nov 2021 10:19:15 +0900
Subject: [PATCH v2] loop: don't hold lo_mutex during __loop_clr_fd()

syzbot is reporting circular locking problem at __loop_clr_fd() [1], for
commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker")
is calling destroy_workqueue() with lo->lo_mutex held.

Since all functions where lo->lo_state matters are already checking
lo->lo_state with lo->lo_mutex held (in order to avoid racing with e.g.
ioctl(LOOP_CTL_REMOVE)), and __loop_clr_fd() can be called from either
ioctl(LOOP_CLR_FD) xor close(), lo->lo_state == Lo_rundown is considered
as an exclusive lock for __loop_clr_fd(). Therefore, hold lo->lo_mutex
inside __loop_clr_fd() only when asserting/updating lo->lo_state.

Since ioctl(LOOP_CLR_FD) depends on lo->lo_state == Lo_bound, a valid
lo->lo_backing_file must have been assigned by ioctl(LOOP_SET_FD) or
ioctl(LOOP_CONFIGURE). Thus, we can remove lo->lo_backing_file test,
and convert __loop_clr_fd() into a void function.

Link: https://syzkaller.appspot.com/bug?extid=63614029dfb79abd4383 [1]
Reported-by: syzbot <syzbot+63614029dfb79abd4383@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
Changes in v2:
  Hold lo->lo_mutex only when asserting/updating lo->lo_state.
  Convert __loop_clr_fd() to return void.

 drivers/block/loop.c | 55 ++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 33 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index a154cab6cd98..30ee34c6498e 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1082,13 +1082,10 @@ static int loop_configure(struct loop_device *lo, fmode_t mode,
 	return error;
 }
 
-static int __loop_clr_fd(struct loop_device *lo, bool release)
+static void __loop_clr_fd(struct loop_device *lo, bool release)
 {
-	struct file *filp = NULL;
+	struct file *filp;
 	gfp_t gfp = lo->old_gfp_mask;
-	int err = 0;
-	bool partscan = false;
-	int lo_number;
 	struct loop_worker *pos, *worker;
 
 	/*
@@ -1103,17 +1100,14 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	 * 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);
-	if (WARN_ON_ONCE(lo->lo_state != Lo_rundown)) {
-		err = -ENXIO;
-		goto out_unlock;
-	}
-
-	filp = lo->lo_backing_file;
-	if (filp == NULL) {
-		err = -EINVAL;
-		goto out_unlock;
-	}
+	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);
@@ -1134,6 +1128,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	del_timer_sync(&lo->timer);
 
 	spin_lock_irq(&lo->lo_lock);
+	filp = lo->lo_backing_file;
 	lo->lo_backing_file = NULL;
 	spin_unlock_irq(&lo->lo_lock);
 
@@ -1153,12 +1148,11 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	module_put(THIS_MODULE);
 	blk_mq_unfreeze_queue(lo->lo_queue);
 
-	partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
-	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) {
+
+	if (lo->lo_flags & LO_FLAGS_PARTSCAN) {
+		int err;
+
 		/*
 		 * open_mutex has been held already in release path, so don't
 		 * acquire it if this function is called in such case.
@@ -1174,24 +1168,20 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 			mutex_unlock(&lo->lo_disk->open_mutex);
 		if (err)
 			pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
-				__func__, lo_number, err);
+				__func__, lo->lo_number, err);
 		/* Device is gone, no point in returning error */
-		err = 0;
 	}
 
 	/*
 	 * lo->lo_state is set to Lo_unbound here after above partscan has
-	 * finished.
-	 *
-	 * There cannot be anybody else entering __loop_clr_fd() as
-	 * lo->lo_backing_file is already cleared and Lo_rundown state
-	 * protects us from all the other places trying to change the 'lo'
-	 * device.
+	 * finished. There cannot be anybody else entering __loop_clr_fd() as
+	 * Lo_rundown state protects us from all the other places trying to
+	 * change the 'lo' device.
 	 */
-	mutex_lock(&lo->lo_mutex);
 	lo->lo_flags = 0;
 	if (!part_shift)
 		lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
+	mutex_lock(&lo->lo_mutex);
 	lo->lo_state = Lo_unbound;
 	mutex_unlock(&lo->lo_mutex);
 
@@ -1200,9 +1190,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	 * lo_mutex triggers a circular lock dependency possibility warning as
 	 * fput can take open_mutex which is usually taken before lo_mutex.
 	 */
-	if (filp)
-		fput(filp);
-	return err;
+	fput(filp);
 }
 
 static int loop_clr_fd(struct loop_device *lo)
@@ -1234,7 +1222,8 @@ static int loop_clr_fd(struct loop_device *lo)
 	lo->lo_state = Lo_rundown;
 	mutex_unlock(&lo->lo_mutex);
 
-	return __loop_clr_fd(lo, false);
+	__loop_clr_fd(lo, false);
+	return 0;
 }
 
 static int
-- 
2.18.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [syzbot] possible deadlock in __loop_clr_fd (3)
  2021-11-12 16:25         ` Tetsuo Handa
@ 2021-11-15  9:58           ` Christoph Hellwig
  2021-11-15 10:20             ` [PATCH v2] loop: don't hold lo_mutex during __loop_clr_fd() Tetsuo Handa
  0 siblings, 1 reply; 10+ messages in thread
From: Christoph Hellwig @ 2021-11-15  9:58 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Christoph Hellwig, Dan Schatzberg, Ming Lei, axboe, linux-block

Looks good,

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2] loop: don't hold lo_mutex during __loop_clr_fd()
  2021-11-15  9:58           ` Christoph Hellwig
@ 2021-11-15 10:20             ` Tetsuo Handa
  2021-11-24 10:47               ` [PATCH v3] " Tetsuo Handa
  0 siblings, 1 reply; 10+ messages in thread
From: Tetsuo Handa @ 2021-11-15 10:20 UTC (permalink / raw)
  To: axboe; +Cc: linux-block

syzbot is reporting circular locking problem at __loop_clr_fd() [1], for
commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker")
is calling destroy_workqueue() with lo->lo_mutex held.

Since all functions where lo->lo_state matters are already checking
lo->lo_state with lo->lo_mutex held (in order to avoid racing with e.g.
ioctl(LOOP_CTL_REMOVE)), and __loop_clr_fd() can be called from either
ioctl(LOOP_CLR_FD) xor close(), lo->lo_state == Lo_rundown is considered
as an exclusive lock for __loop_clr_fd(). Therefore, hold lo->lo_mutex
inside __loop_clr_fd() only when asserting/updating lo->lo_state.

Since ioctl(LOOP_CLR_FD) depends on lo->lo_state == Lo_bound, a valid
lo->lo_backing_file must have been assigned by ioctl(LOOP_SET_FD) or
ioctl(LOOP_CONFIGURE). Thus, we can remove lo->lo_backing_file test,
and convert __loop_clr_fd() into a void function.

Link: https://syzkaller.appspot.com/bug?extid=63614029dfb79abd4383 [1]
Reported-by: syzbot <syzbot+63614029dfb79abd4383@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
Changes in v2:
  Hold lo->lo_mutex only when asserting/updating lo->lo_state.
  Convert __loop_clr_fd() to return void.

 drivers/block/loop.c | 55 ++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 33 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index a154cab6cd98..30ee34c6498e 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1082,13 +1082,10 @@ static int loop_configure(struct loop_device *lo, fmode_t mode,
 	return error;
 }
 
-static int __loop_clr_fd(struct loop_device *lo, bool release)
+static void __loop_clr_fd(struct loop_device *lo, bool release)
 {
-	struct file *filp = NULL;
+	struct file *filp;
 	gfp_t gfp = lo->old_gfp_mask;
-	int err = 0;
-	bool partscan = false;
-	int lo_number;
 	struct loop_worker *pos, *worker;
 
 	/*
@@ -1103,17 +1100,14 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	 * 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);
-	if (WARN_ON_ONCE(lo->lo_state != Lo_rundown)) {
-		err = -ENXIO;
-		goto out_unlock;
-	}
-
-	filp = lo->lo_backing_file;
-	if (filp == NULL) {
-		err = -EINVAL;
-		goto out_unlock;
-	}
+	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);
@@ -1134,6 +1128,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	del_timer_sync(&lo->timer);
 
 	spin_lock_irq(&lo->lo_lock);
+	filp = lo->lo_backing_file;
 	lo->lo_backing_file = NULL;
 	spin_unlock_irq(&lo->lo_lock);
 
@@ -1153,12 +1148,11 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	module_put(THIS_MODULE);
 	blk_mq_unfreeze_queue(lo->lo_queue);
 
-	partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
-	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) {
+
+	if (lo->lo_flags & LO_FLAGS_PARTSCAN) {
+		int err;
+
 		/*
 		 * open_mutex has been held already in release path, so don't
 		 * acquire it if this function is called in such case.
@@ -1174,24 +1168,20 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 			mutex_unlock(&lo->lo_disk->open_mutex);
 		if (err)
 			pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
-				__func__, lo_number, err);
+				__func__, lo->lo_number, err);
 		/* Device is gone, no point in returning error */
-		err = 0;
 	}
 
 	/*
 	 * lo->lo_state is set to Lo_unbound here after above partscan has
-	 * finished.
-	 *
-	 * There cannot be anybody else entering __loop_clr_fd() as
-	 * lo->lo_backing_file is already cleared and Lo_rundown state
-	 * protects us from all the other places trying to change the 'lo'
-	 * device.
+	 * finished. There cannot be anybody else entering __loop_clr_fd() as
+	 * Lo_rundown state protects us from all the other places trying to
+	 * change the 'lo' device.
 	 */
-	mutex_lock(&lo->lo_mutex);
 	lo->lo_flags = 0;
 	if (!part_shift)
 		lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
+	mutex_lock(&lo->lo_mutex);
 	lo->lo_state = Lo_unbound;
 	mutex_unlock(&lo->lo_mutex);
 
@@ -1200,9 +1190,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	 * lo_mutex triggers a circular lock dependency possibility warning as
 	 * fput can take open_mutex which is usually taken before lo_mutex.
 	 */
-	if (filp)
-		fput(filp);
-	return err;
+	fput(filp);
 }
 
 static int loop_clr_fd(struct loop_device *lo)
@@ -1234,7 +1222,8 @@ static int loop_clr_fd(struct loop_device *lo)
 	lo->lo_state = Lo_rundown;
 	mutex_unlock(&lo->lo_mutex);
 
-	return __loop_clr_fd(lo, false);
+	__loop_clr_fd(lo, false);
+	return 0;
 }
 
 static int
-- 
2.18.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v3] loop: don't hold lo_mutex during __loop_clr_fd()
  2021-11-15 10:20             ` [PATCH v2] loop: don't hold lo_mutex during __loop_clr_fd() Tetsuo Handa
@ 2021-11-24 10:47               ` Tetsuo Handa
  2021-11-24 15:35                 ` Jens Axboe
  0 siblings, 1 reply; 10+ messages in thread
From: Tetsuo Handa @ 2021-11-24 10:47 UTC (permalink / raw)
  To: axboe; +Cc: linux-block

syzbot is reporting circular locking problem at __loop_clr_fd() [1], for
commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker")
is calling destroy_workqueue() with lo->lo_mutex held.

Since all functions where lo->lo_state matters are already checking
lo->lo_state with lo->lo_mutex held (in order to avoid racing with e.g.
ioctl(LOOP_CTL_REMOVE)), and __loop_clr_fd() can be called from either
ioctl(LOOP_CLR_FD) xor close(), lo->lo_state == Lo_rundown is considered
as an exclusive lock for __loop_clr_fd(). Therefore, hold lo->lo_mutex
inside __loop_clr_fd() only when asserting/updating lo->lo_state.

Since ioctl(LOOP_CLR_FD) depends on lo->lo_state == Lo_bound, a valid
lo->lo_backing_file must have been assigned by ioctl(LOOP_SET_FD) or
ioctl(LOOP_CONFIGURE). Thus, we can remove lo->lo_backing_file test,
and convert __loop_clr_fd() into a void function.

Link: https://syzkaller.appspot.com/bug?extid=63614029dfb79abd4383 [1]
Reported-by: syzbot <syzbot+63614029dfb79abd4383@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
Changes in v3:
  Rebased on for-5.17/block.

Changes in v2:
  Hold lo->lo_mutex only when asserting/updating lo->lo_state.
  Convert __loop_clr_fd() to return void.

 drivers/block/loop.c | 55 ++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 33 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 0954ea8cf9e3..ba76319b5544 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1082,13 +1082,10 @@ static int loop_configure(struct loop_device *lo, fmode_t mode,
 	return error;
 }
 
-static int __loop_clr_fd(struct loop_device *lo, bool release)
+static void __loop_clr_fd(struct loop_device *lo, bool release)
 {
-	struct file *filp = NULL;
+	struct file *filp;
 	gfp_t gfp = lo->old_gfp_mask;
-	int err = 0;
-	bool partscan = false;
-	int lo_number;
 	struct loop_worker *pos, *worker;
 
 	/*
@@ -1103,17 +1100,14 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	 * 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);
-	if (WARN_ON_ONCE(lo->lo_state != Lo_rundown)) {
-		err = -ENXIO;
-		goto out_unlock;
-	}
-
-	filp = lo->lo_backing_file;
-	if (filp == NULL) {
-		err = -EINVAL;
-		goto out_unlock;
-	}
+	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);
@@ -1134,6 +1128,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	del_timer_sync(&lo->timer);
 
 	spin_lock_irq(&lo->lo_lock);
+	filp = lo->lo_backing_file;
 	lo->lo_backing_file = NULL;
 	spin_unlock_irq(&lo->lo_lock);
 
@@ -1153,12 +1148,11 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	module_put(THIS_MODULE);
 	blk_mq_unfreeze_queue(lo->lo_queue);
 
-	partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
-	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) {
+
+	if (lo->lo_flags & LO_FLAGS_PARTSCAN) {
+		int err;
+
 		/*
 		 * open_mutex has been held already in release path, so don't
 		 * acquire it if this function is called in such case.
@@ -1174,24 +1168,20 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 			mutex_unlock(&lo->lo_disk->open_mutex);
 		if (err)
 			pr_warn("%s: partition scan of loop%d failed (rc=%d)\n",
-				__func__, lo_number, err);
+				__func__, lo->lo_number, err);
 		/* Device is gone, no point in returning error */
-		err = 0;
 	}
 
 	/*
 	 * lo->lo_state is set to Lo_unbound here after above partscan has
-	 * finished.
-	 *
-	 * There cannot be anybody else entering __loop_clr_fd() as
-	 * lo->lo_backing_file is already cleared and Lo_rundown state
-	 * protects us from all the other places trying to change the 'lo'
-	 * device.
+	 * finished. There cannot be anybody else entering __loop_clr_fd() as
+	 * Lo_rundown state protects us from all the other places trying to
+	 * change the 'lo' device.
 	 */
-	mutex_lock(&lo->lo_mutex);
 	lo->lo_flags = 0;
 	if (!part_shift)
 		lo->lo_disk->flags |= GENHD_FL_NO_PART;
+	mutex_lock(&lo->lo_mutex);
 	lo->lo_state = Lo_unbound;
 	mutex_unlock(&lo->lo_mutex);
 
@@ -1200,9 +1190,7 @@ static int __loop_clr_fd(struct loop_device *lo, bool release)
 	 * lo_mutex triggers a circular lock dependency possibility warning as
 	 * fput can take open_mutex which is usually taken before lo_mutex.
 	 */
-	if (filp)
-		fput(filp);
-	return err;
+	fput(filp);
 }
 
 static int loop_clr_fd(struct loop_device *lo)
@@ -1234,7 +1222,8 @@ static int loop_clr_fd(struct loop_device *lo)
 	lo->lo_state = Lo_rundown;
 	mutex_unlock(&lo->lo_mutex);
 
-	return __loop_clr_fd(lo, false);
+	__loop_clr_fd(lo, false);
+	return 0;
 }
 
 static int
-- 
2.18.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3] loop: don't hold lo_mutex during __loop_clr_fd()
  2021-11-24 10:47               ` [PATCH v3] " Tetsuo Handa
@ 2021-11-24 15:35                 ` Jens Axboe
  0 siblings, 0 replies; 10+ messages in thread
From: Jens Axboe @ 2021-11-24 15:35 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: linux-block

On Wed, 24 Nov 2021 19:47:40 +0900, Tetsuo Handa wrote:
> syzbot is reporting circular locking problem at __loop_clr_fd() [1], for
> commit 87579e9b7d8dc36e ("loop: use worker per cgroup instead of kworker")
> is calling destroy_workqueue() with lo->lo_mutex held.
> 
> Since all functions where lo->lo_state matters are already checking
> lo->lo_state with lo->lo_mutex held (in order to avoid racing with e.g.
> ioctl(LOOP_CTL_REMOVE)), and __loop_clr_fd() can be called from either
> ioctl(LOOP_CLR_FD) xor close(), lo->lo_state == Lo_rundown is considered
> as an exclusive lock for __loop_clr_fd(). Therefore, hold lo->lo_mutex
> inside __loop_clr_fd() only when asserting/updating lo->lo_state.
> 
> [...]

Applied, thanks!

[1/1] loop: don't hold lo_mutex during __loop_clr_fd()
      commit: c895b784c699224d690c7dfbdcff309df82366e3

Best regards,
-- 
Jens Axboe



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-11-24 15:36 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-10 17:00 [syzbot] possible deadlock in __loop_clr_fd (3) syzbot
2021-11-10 22:10 ` Tetsuo Handa
2021-11-11  0:55   ` Dan Schatzberg
2021-11-11 15:14     ` Tetsuo Handa
2021-11-12  6:20       ` Christoph Hellwig
2021-11-12 16:25         ` Tetsuo Handa
2021-11-15  9:58           ` Christoph Hellwig
2021-11-15 10:20             ` [PATCH v2] loop: don't hold lo_mutex during __loop_clr_fd() Tetsuo Handa
2021-11-24 10:47               ` [PATCH v3] " Tetsuo Handa
2021-11-24 15:35                 ` Jens Axboe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.