* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).