From: Sasha Levin Date: Thu, 3 Oct 2019 14:45:47 +0000 (-0400) Subject: fixes for 4.9 X-Git-Tag: v4.4.195~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d0c8cb0069359055799fffd7f8c5674060436ff6;p=thirdparty%2Fkernel%2Fstable-queue.git fixes for 4.9 Signed-off-by: Sasha Levin --- diff --git a/queue-4.9/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch b/queue-4.9/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch new file mode 100644 index 00000000000..1a06a47cc5c --- /dev/null +++ b/queue-4.9/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch @@ -0,0 +1,206 @@ +From 62b23c7b9519b24d85231d235419e167df532119 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Sep 2019 10:49:54 +0100 +Subject: Btrfs: fix race setting up and completing qgroup rescan workers + +From: Filipe Manana + +[ Upstream commit 13fc1d271a2e3ab8a02071e711add01fab9271f6 ] + +There is a race between setting up a qgroup rescan worker and completing +a qgroup rescan worker that can lead to callers of the qgroup rescan wait +ioctl to either not wait for the rescan worker to complete or to hang +forever due to missing wake ups. The following diagram shows a sequence +of steps that illustrates the race. + + CPU 1 CPU 2 CPU 3 + + btrfs_ioctl_quota_rescan() + btrfs_qgroup_rescan() + qgroup_rescan_init() + mutex_lock(&fs_info->qgroup_rescan_lock) + spin_lock(&fs_info->qgroup_lock) + + fs_info->qgroup_flags |= + BTRFS_QGROUP_STATUS_FLAG_RESCAN + + init_completion( + &fs_info->qgroup_rescan_completion) + + fs_info->qgroup_rescan_running = true + + mutex_unlock(&fs_info->qgroup_rescan_lock) + spin_unlock(&fs_info->qgroup_lock) + + btrfs_init_work() + --> starts the worker + + btrfs_qgroup_rescan_worker() + mutex_lock(&fs_info->qgroup_rescan_lock) + + fs_info->qgroup_flags &= + ~BTRFS_QGROUP_STATUS_FLAG_RESCAN + + mutex_unlock(&fs_info->qgroup_rescan_lock) + + starts transaction, updates qgroup status + item, etc + + btrfs_ioctl_quota_rescan() + btrfs_qgroup_rescan() + qgroup_rescan_init() + mutex_lock(&fs_info->qgroup_rescan_lock) + spin_lock(&fs_info->qgroup_lock) + + fs_info->qgroup_flags |= + BTRFS_QGROUP_STATUS_FLAG_RESCAN + + init_completion( + &fs_info->qgroup_rescan_completion) + + fs_info->qgroup_rescan_running = true + + mutex_unlock(&fs_info->qgroup_rescan_lock) + spin_unlock(&fs_info->qgroup_lock) + + btrfs_init_work() + --> starts another worker + + mutex_lock(&fs_info->qgroup_rescan_lock) + + fs_info->qgroup_rescan_running = false + + mutex_unlock(&fs_info->qgroup_rescan_lock) + + complete_all(&fs_info->qgroup_rescan_completion) + +Before the rescan worker started by the task at CPU 3 completes, if +another task calls btrfs_ioctl_quota_rescan(), it will get -EINPROGRESS +because the flag BTRFS_QGROUP_STATUS_FLAG_RESCAN is set at +fs_info->qgroup_flags, which is expected and correct behaviour. + +However if other task calls btrfs_ioctl_quota_rescan_wait() before the +rescan worker started by the task at CPU 3 completes, it will return +immediately without waiting for the new rescan worker to complete, +because fs_info->qgroup_rescan_running is set to false by CPU 2. + +This race is making test case btrfs/171 (from fstests) to fail often: + + btrfs/171 9s ... - output mismatch (see /home/fdmanana/git/hub/xfstests/results//btrfs/171.out.bad) + --- tests/btrfs/171.out 2018-09-16 21:30:48.505104287 +0100 + +++ /home/fdmanana/git/hub/xfstests/results//btrfs/171.out.bad 2019-09-19 02:01:36.938486039 +0100 + @@ -1,2 +1,3 @@ + QA output created by 171 + +ERROR: quota rescan failed: Operation now in progress + Silence is golden + ... + (Run 'diff -u /home/fdmanana/git/hub/xfstests/tests/btrfs/171.out /home/fdmanana/git/hub/xfstests/results//btrfs/171.out.bad' to see the entire diff) + +That is because the test calls the btrfs-progs commands "qgroup quota +rescan -w", "qgroup assign" and "qgroup remove" in a sequence that makes +calls to the rescan start ioctl fail with -EINPROGRESS (note the "btrfs" +commands 'qgroup assign' and 'qgroup remove' often call the rescan start +ioctl after calling the qgroup assign ioctl, +btrfs_ioctl_qgroup_assign()), since previous waits didn't actually wait +for a rescan worker to complete. + +Another problem the race can cause is missing wake ups for waiters, +since the call to complete_all() happens outside a critical section and +after clearing the flag BTRFS_QGROUP_STATUS_FLAG_RESCAN. In the sequence +diagram above, if we have a waiter for the first rescan task (executed +by CPU 2), then fs_info->qgroup_rescan_completion.wait is not empty, and +if after the rescan worker clears BTRFS_QGROUP_STATUS_FLAG_RESCAN and +before it calls complete_all() against +fs_info->qgroup_rescan_completion, the task at CPU 3 calls +init_completion() against fs_info->qgroup_rescan_completion which +re-initilizes its wait queue to an empty queue, therefore causing the +rescan worker at CPU 2 to call complete_all() against an empty queue, +never waking up the task waiting for that rescan worker. + +Fix this by clearing BTRFS_QGROUP_STATUS_FLAG_RESCAN and setting +fs_info->qgroup_rescan_running to false in the same critical section, +delimited by the mutex fs_info->qgroup_rescan_lock, as well as doing the +call to complete_all() in that same critical section. This gives the +protection needed to avoid rescan wait ioctl callers not waiting for a +running rescan worker and the lost wake ups problem, since setting that +rescan flag and boolean as well as initializing the wait queue is done +already in a critical section delimited by that mutex (at +qgroup_rescan_init()). + +Fixes: 57254b6ebce4ce ("Btrfs: add ioctl to wait for qgroup rescan completion") +Fixes: d2c609b834d62f ("btrfs: properly track when rescan worker is running") +CC: stable@vger.kernel.org # 4.4+ +Reviewed-by: Josef Bacik +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/qgroup.c | 33 +++++++++++++++++++-------------- + 1 file changed, 19 insertions(+), 14 deletions(-) + +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 088dc7d38c136..0355e6d9e21cb 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -2380,9 +2380,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) + btrfs_free_path(path); + + mutex_lock(&fs_info->qgroup_rescan_lock); +- if (!btrfs_fs_closing(fs_info)) +- fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN; +- + if (err > 0 && + fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT) { + fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; +@@ -2398,16 +2395,30 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) + trans = btrfs_start_transaction(fs_info->quota_root, 1); + if (IS_ERR(trans)) { + err = PTR_ERR(trans); ++ trans = NULL; + btrfs_err(fs_info, + "fail to start transaction for status update: %d\n", + err); +- goto done; + } +- ret = update_qgroup_status_item(trans); +- if (ret < 0) { +- err = ret; +- btrfs_err(fs_info, "fail to update qgroup status: %d", err); ++ ++ mutex_lock(&fs_info->qgroup_rescan_lock); ++ if (!btrfs_fs_closing(fs_info)) ++ fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN; ++ if (trans) { ++ ret = update_qgroup_status_item(trans); ++ if (ret < 0) { ++ err = ret; ++ btrfs_err(fs_info, "fail to update qgroup status: %d", ++ err); ++ } + } ++ fs_info->qgroup_rescan_running = false; ++ complete_all(&fs_info->qgroup_rescan_completion); ++ mutex_unlock(&fs_info->qgroup_rescan_lock); ++ ++ if (!trans) ++ return; ++ + btrfs_end_transaction(trans, fs_info->quota_root); + + if (btrfs_fs_closing(fs_info)) { +@@ -2418,12 +2429,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) + } else { + btrfs_err(fs_info, "qgroup scan failed with %d", err); + } +- +-done: +- mutex_lock(&fs_info->qgroup_rescan_lock); +- fs_info->qgroup_rescan_running = false; +- mutex_unlock(&fs_info->qgroup_rescan_lock); +- complete_all(&fs_info->qgroup_rescan_completion); + } + + /* +-- +2.20.1 + diff --git a/queue-4.9/btrfs-qgroup-drop-quota_root-and-fs_info-parameters-.patch b/queue-4.9/btrfs-qgroup-drop-quota_root-and-fs_info-parameters-.patch new file mode 100644 index 00000000000..8bd008ab496 --- /dev/null +++ b/queue-4.9/btrfs-qgroup-drop-quota_root-and-fs_info-parameters-.patch @@ -0,0 +1,67 @@ +From 4bc17577373089cea92ef7f1fa286c428b9ee82a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 18 Jul 2018 14:45:29 +0800 +Subject: btrfs: qgroup: Drop quota_root and fs_info parameters from + update_qgroup_status_item + +From: Lu Fengqi + +[ Upstream commit 2e980acdd829742966c6a7e565ef3382c0717295 ] + +They can be fetched from the transaction handle. + +Signed-off-by: Lu Fengqi +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/qgroup.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index f25233093d68c..088dc7d38c136 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -759,10 +759,10 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans, + return ret; + } + +-static int update_qgroup_status_item(struct btrfs_trans_handle *trans, +- struct btrfs_fs_info *fs_info, +- struct btrfs_root *root) ++static int update_qgroup_status_item(struct btrfs_trans_handle *trans) + { ++ struct btrfs_fs_info *fs_info = trans->fs_info; ++ struct btrfs_root *quota_root = fs_info->quota_root; + struct btrfs_path *path; + struct btrfs_key key; + struct extent_buffer *l; +@@ -778,7 +778,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans, + if (!path) + return -ENOMEM; + +- ret = btrfs_search_slot(trans, root, &key, path, 0, 1); ++ ret = btrfs_search_slot(trans, quota_root, &key, path, 0, 1); + if (ret > 0) + ret = -ENOENT; + +@@ -1863,7 +1863,7 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans, + fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON; + spin_unlock(&fs_info->qgroup_lock); + +- ret = update_qgroup_status_item(trans, fs_info, quota_root); ++ ret = update_qgroup_status_item(trans); + if (ret) + fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; + +@@ -2403,7 +2403,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) + err); + goto done; + } +- ret = update_qgroup_status_item(trans, fs_info, fs_info->quota_root); ++ ret = update_qgroup_status_item(trans); + if (ret < 0) { + err = ret; + btrfs_err(fs_info, "fail to update qgroup status: %d", err); +-- +2.20.1 + diff --git a/queue-4.9/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch b/queue-4.9/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch new file mode 100644 index 00000000000..e8ed0cabb9a --- /dev/null +++ b/queue-4.9/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch @@ -0,0 +1,401 @@ +From 5242268a759931c34ad6262d1646f3eb45c8d19f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 8 Sep 2019 20:15:18 -0700 +Subject: fuse: fix deadlock with aio poll and fuse_iqueue::waitq.lock + +From: Eric Biggers + +[ Upstream commit 76e43c8ccaa35c30d5df853013561145a0f750a5 ] + +When IOCB_CMD_POLL is used on the FUSE device, aio_poll() disables IRQs +and takes kioctx::ctx_lock, then fuse_iqueue::waitq.lock. + +This may have to wait for fuse_iqueue::waitq.lock to be released by one +of many places that take it with IRQs enabled. Since the IRQ handler +may take kioctx::ctx_lock, lockdep reports that a deadlock is possible. + +Fix it by protecting the state of struct fuse_iqueue with a separate +spinlock, and only accessing fuse_iqueue::waitq using the versions of +the waitqueue functions which do IRQ-safe locking internally. + +Reproducer: + + #include + #include + #include + #include + #include + #include + #include + + int main() + { + char opts[128]; + int fd = open("/dev/fuse", O_RDWR); + aio_context_t ctx = 0; + struct iocb cb = { .aio_lio_opcode = IOCB_CMD_POLL, .aio_fildes = fd }; + struct iocb *cbp = &cb; + + sprintf(opts, "fd=%d,rootmode=040000,user_id=0,group_id=0", fd); + mkdir("mnt", 0700); + mount("foo", "mnt", "fuse", 0, opts); + syscall(__NR_io_setup, 1, &ctx); + syscall(__NR_io_submit, ctx, 1, &cbp); + } + +Beginning of lockdep output: + + ===================================================== + WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected + 5.3.0-rc5 #9 Not tainted + ----------------------------------------------------- + syz_fuse/135 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire: + 000000003590ceda (&fiq->waitq){+.+.}, at: spin_lock include/linux/spinlock.h:338 [inline] + 000000003590ceda (&fiq->waitq){+.+.}, at: aio_poll fs/aio.c:1751 [inline] + 000000003590ceda (&fiq->waitq){+.+.}, at: __io_submit_one.constprop.0+0x203/0x5b0 fs/aio.c:1825 + + and this task is already holding: + 0000000075037284 (&(&ctx->ctx_lock)->rlock){..-.}, at: spin_lock_irq include/linux/spinlock.h:363 [inline] + 0000000075037284 (&(&ctx->ctx_lock)->rlock){..-.}, at: aio_poll fs/aio.c:1749 [inline] + 0000000075037284 (&(&ctx->ctx_lock)->rlock){..-.}, at: __io_submit_one.constprop.0+0x1f4/0x5b0 fs/aio.c:1825 + which would create a new lock dependency: + (&(&ctx->ctx_lock)->rlock){..-.} -> (&fiq->waitq){+.+.} + + but this new dependency connects a SOFTIRQ-irq-safe lock: + (&(&ctx->ctx_lock)->rlock){..-.} + + [...] + +Reported-by: syzbot+af05535bb79520f95431@syzkaller.appspotmail.com +Reported-by: syzbot+d86c4426a01f60feddc7@syzkaller.appspotmail.com +Fixes: bfe4037e722e ("aio: implement IOCB_CMD_POLL") +Cc: # v4.19+ +Cc: Christoph Hellwig +Signed-off-by: Eric Biggers +Signed-off-by: Miklos Szeredi +Signed-off-by: Sasha Levin +--- + fs/fuse/dev.c | 89 +++++++++++++++++++++++++----------------------- + fs/fuse/fuse_i.h | 3 ++ + fs/fuse/inode.c | 1 + + 3 files changed, 50 insertions(+), 43 deletions(-) + +diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c +index 8016cd059db12..638ec50e606e2 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -319,7 +319,7 @@ static void queue_request(struct fuse_iqueue *fiq, struct fuse_req *req) + req->in.h.len = sizeof(struct fuse_in_header) + + len_args(req->in.numargs, (struct fuse_arg *) req->in.args); + list_add_tail(&req->list, &fiq->pending); +- wake_up_locked(&fiq->waitq); ++ wake_up(&fiq->waitq); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); + } + +@@ -331,16 +331,16 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, + forget->forget_one.nodeid = nodeid; + forget->forget_one.nlookup = nlookup; + +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + if (fiq->connected) { + fiq->forget_list_tail->next = forget; + fiq->forget_list_tail = forget; +- wake_up_locked(&fiq->waitq); ++ wake_up(&fiq->waitq); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); + } else { + kfree(forget); + } +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + } + + static void flush_bg_queue(struct fuse_conn *fc) +@@ -353,10 +353,10 @@ static void flush_bg_queue(struct fuse_conn *fc) + req = list_entry(fc->bg_queue.next, struct fuse_req, list); + list_del(&req->list); + fc->active_background++; +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + req->in.h.unique = fuse_get_unique(fiq); + queue_request(fiq, req); +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + } + } + +@@ -375,9 +375,9 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) + if (test_and_set_bit(FR_FINISHED, &req->flags)) + goto put_request; + +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + list_del_init(&req->intr_entry); +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + WARN_ON(test_bit(FR_PENDING, &req->flags)); + WARN_ON(test_bit(FR_SENT, &req->flags)); + if (test_bit(FR_BACKGROUND, &req->flags)) { +@@ -416,16 +416,16 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) + + static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) + { +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + if (test_bit(FR_FINISHED, &req->flags)) { +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + return; + } + if (list_empty(&req->intr_entry)) { + list_add_tail(&req->intr_entry, &fiq->interrupts); + wake_up_locked(&fiq->waitq); + } +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); + } + +@@ -455,16 +455,16 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) + if (!err) + return; + +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + /* Request is not yet in userspace, bail out */ + if (test_bit(FR_PENDING, &req->flags)) { + list_del(&req->list); +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + __fuse_put_request(req); + req->out.h.error = -EINTR; + return; + } +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + } + + /* +@@ -479,9 +479,9 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) + struct fuse_iqueue *fiq = &fc->iq; + + BUG_ON(test_bit(FR_BACKGROUND, &req->flags)); +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + if (!fiq->connected) { +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + req->out.h.error = -ENOTCONN; + } else { + req->in.h.unique = fuse_get_unique(fiq); +@@ -489,7 +489,7 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) + /* acquire extra reference, since request is still needed + after request_end() */ + __fuse_get_request(req); +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + + request_wait_answer(fc, req); + /* Pairs with smp_wmb() in request_end() */ +@@ -623,12 +623,12 @@ static int fuse_request_send_notify_reply(struct fuse_conn *fc, + + __clear_bit(FR_ISREPLY, &req->flags); + req->in.h.unique = unique; +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + if (fiq->connected) { + queue_request(fiq, req); + err = 0; + } +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + + return err; + } +@@ -1072,12 +1072,12 @@ static int request_pending(struct fuse_iqueue *fiq) + * Unlike other requests this is assembled on demand, without a need + * to allocate a separate fuse_req structure. + * +- * Called with fiq->waitq.lock held, releases it ++ * Called with fiq->lock held, releases it + */ + static int fuse_read_interrupt(struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, + size_t nbytes, struct fuse_req *req) +-__releases(fiq->waitq.lock) ++__releases(fiq->lock) + { + struct fuse_in_header ih; + struct fuse_interrupt_in arg; +@@ -1093,7 +1093,7 @@ __releases(fiq->waitq.lock) + ih.unique = req->intr_unique; + arg.unique = req->in.h.unique; + +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + if (nbytes < reqsize) + return -EINVAL; + +@@ -1130,7 +1130,7 @@ static struct fuse_forget_link *dequeue_forget(struct fuse_iqueue *fiq, + static int fuse_read_single_forget(struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, + size_t nbytes) +-__releases(fiq->waitq.lock) ++__releases(fiq->lock) + { + int err; + struct fuse_forget_link *forget = dequeue_forget(fiq, 1, NULL); +@@ -1144,7 +1144,7 @@ __releases(fiq->waitq.lock) + .len = sizeof(ih) + sizeof(arg), + }; + +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + kfree(forget); + if (nbytes < ih.len) + return -EINVAL; +@@ -1162,7 +1162,7 @@ __releases(fiq->waitq.lock) + + static int fuse_read_batch_forget(struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, size_t nbytes) +-__releases(fiq->waitq.lock) ++__releases(fiq->lock) + { + int err; + unsigned max_forgets; +@@ -1176,13 +1176,13 @@ __releases(fiq->waitq.lock) + }; + + if (nbytes < ih.len) { +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + return -EINVAL; + } + + max_forgets = (nbytes - ih.len) / sizeof(struct fuse_forget_one); + head = dequeue_forget(fiq, max_forgets, &count); +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + + arg.count = count; + ih.len += count * sizeof(struct fuse_forget_one); +@@ -1212,7 +1212,7 @@ __releases(fiq->waitq.lock) + static int fuse_read_forget(struct fuse_conn *fc, struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, + size_t nbytes) +-__releases(fiq->waitq.lock) ++__releases(fiq->lock) + { + if (fc->minor < 16 || fiq->forget_list_head.next->next == NULL) + return fuse_read_single_forget(fiq, cs, nbytes); +@@ -1241,16 +1241,19 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, + unsigned reqsize; + + restart: +- spin_lock(&fiq->waitq.lock); +- err = -EAGAIN; +- if ((file->f_flags & O_NONBLOCK) && fiq->connected && +- !request_pending(fiq)) +- goto err_unlock; ++ for (;;) { ++ spin_lock(&fiq->lock); ++ if (!fiq->connected || request_pending(fiq)) ++ break; ++ spin_unlock(&fiq->lock); + +- err = wait_event_interruptible_exclusive_locked(fiq->waitq, ++ if (file->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ err = wait_event_interruptible_exclusive(fiq->waitq, + !fiq->connected || request_pending(fiq)); +- if (err) +- goto err_unlock; ++ if (err) ++ return err; ++ } + + err = -ENODEV; + if (!fiq->connected) +@@ -1273,7 +1276,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, + req = list_entry(fiq->pending.next, struct fuse_req, list); + clear_bit(FR_PENDING, &req->flags); + list_del_init(&req->list); +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + + in = &req->in; + reqsize = in->h.len; +@@ -1329,7 +1332,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, + return err; + + err_unlock: +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + return err; + } + +@@ -2040,12 +2043,12 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) + fiq = &fud->fc->iq; + poll_wait(file, &fiq->waitq, wait); + +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + if (!fiq->connected) + mask = POLLERR; + else if (request_pending(fiq)) + mask |= POLLIN | POLLRDNORM; +- spin_unlock(&fiq->waitq.lock); ++ spin_unlock(&fiq->lock); + + return mask; + } +@@ -2136,15 +2139,15 @@ void fuse_abort_conn(struct fuse_conn *fc) + fc->max_background = UINT_MAX; + flush_bg_queue(fc); + +- spin_lock(&fiq->waitq.lock); ++ spin_lock(&fiq->lock); + fiq->connected = 0; + list_splice_init(&fiq->pending, &to_end2); + list_for_each_entry(req, &to_end2, list) + clear_bit(FR_PENDING, &req->flags); + while (forget_pending(fiq)) + kfree(dequeue_forget(fiq, 1, NULL)); +- wake_up_all_locked(&fiq->waitq); +- spin_unlock(&fiq->waitq.lock); ++ wake_up_all(&fiq->waitq); ++ spin_unlock(&fiq->lock); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); + end_polls(fc); + wake_up_all(&fc->blocked_waitq); +diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h +index 1c905c7666dec..ac9add774db77 100644 +--- a/fs/fuse/fuse_i.h ++++ b/fs/fuse/fuse_i.h +@@ -385,6 +385,9 @@ struct fuse_iqueue { + /** Connection established */ + unsigned connected; + ++ /** Lock protecting accesses to members of this structure */ ++ spinlock_t lock; ++ + /** Readers of the connection are waiting on this */ + wait_queue_head_t waitq; + +diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c +index 7a9b1069d267b..2c942a8279040 100644 +--- a/fs/fuse/inode.c ++++ b/fs/fuse/inode.c +@@ -590,6 +590,7 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root) + static void fuse_iqueue_init(struct fuse_iqueue *fiq) + { + memset(fiq, 0, sizeof(struct fuse_iqueue)); ++ spin_lock_init(&fiq->lock); + init_waitqueue_head(&fiq->waitq); + INIT_LIST_HEAD(&fiq->pending); + INIT_LIST_HEAD(&fiq->interrupts); +-- +2.20.1 + diff --git a/queue-4.9/series b/queue-4.9/series index 7a3904ae7ff..c00d8ddfcd5 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -125,3 +125,6 @@ hwrng-core-don-t-wait-on-add_early_randomness.patch i2c-riic-clear-nack-in-tend-isr.patch cifs-fix-max-ea-value-size.patch cifs-fix-oplock-handling-for-smb-2.1-protocols.patch +fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch +btrfs-qgroup-drop-quota_root-and-fs_info-parameters-.patch +btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch