]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
fuse: Call end_queued_requests() after releasing fc->lock in fuse_dev_release()
authorAshish Samant <ashish.samant@oracle.com>
Sat, 22 Jul 2017 02:20:06 +0000 (19:20 -0700)
committerChuck Anderson <chuck.anderson@oracle.com>
Mon, 11 Dec 2017 04:24:43 +0000 (20:24 -0800)
Orabug: 27215268

During fs teardown,the following self deadlock can happen.

[<ffffffff8173891a>] _raw_spin_lock+0x2a/0x60
[<ffffffffa08a7ef1>] ? fuse_abort_conn+0x31/0x270 [fuse]
[<ffffffffa08763c0>] ? cuse_read_iter+0x70/0x70 [cuse]
[<ffffffffa0876414>] cuse_process_init_reply+0x54/0x490 [cuse]
[<ffffffffa08763c0>] ? cuse_read_iter+0x70/0x70 [cuse]
[<ffffffffa08a5bbf>] request_end+0xbf/0x170 [fuse]
[<ffffffffa08a7d16>] end_queued_requests.isra.19+0x86/0x160 [fuse]
[<ffffffffa08a7e8f>] fuse_dev_release+0x9f/0xd0 [fuse]
[<ffffffffa087611a>] cuse_channel_release+0x8a/0xa0 [cuse]
[<ffffffff81214cf4>] __fput+0xe4/0x220
[<ffffffff81214e7e>] ____fput+0xe/0x10
[<ffffffff810a5557>] task_work_run+0xb7/0xf0
[<ffffffff81017c6d>] do_notify_resume+0x8d/0xa0
[<ffffffff81738dbc>] int_signal+0x12/0x17

The deadlock happens when an attempt is made to take fc->lock in
fuse_abort_conn(). The same lock has already been taken in
fuse_dev_release() in the stack.

This flow is initiated from cuse_process_init_reply() in case of an error
and so, it is a very rare scenario, but it can lockup the fuse fs.

Fix this by releasing the spin lock before calling end_queued_requests()
instead of after, since it does not make a difference anyway.

Signed-off-by: Ashish Samant <ashish.samant@oracle.com>
Reviewed-by: Somasundaram Krishnasamy <somasundaram.krishnasamy@oracle.com>
fs/fuse/dev.c

index 9ae98d4719750944e471a10e41d3ca1c7366d2d9..1dbb7129345f426eb7ed1feb1268088ac16d338a 100644 (file)
@@ -2366,9 +2366,9 @@ int fuse_dev_release(struct inode *inode, struct file *file)
                        wake_up_all(&fn->blocked_waitq);
                        spin_unlock(&fn->lock);
                }
-               end_queued_requests(fc);
                end_polls(fc);
                spin_unlock(&fc->lock);
+               end_queued_requests(fc);
                fuse_conn_put(fc);
        }