From f608a5900e69115f6a93e70a3c785d73018542a2 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 3 Oct 2019 10:45:47 -0400 Subject: [PATCH] fixes for 4.4 Signed-off-by: Sasha Levin --- ...etting-up-and-completing-qgroup-resc.patch | 206 +++++++++ ...k-with-aio-poll-and-fuse_iqueue-wait.patch | 401 ++++++++++++++++++ queue-4.4/series | 2 + 3 files changed, 609 insertions(+) create mode 100644 queue-4.4/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch create mode 100644 queue-4.4/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch diff --git a/queue-4.4/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch b/queue-4.4/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch new file mode 100644 index 00000000000..b832967ac3d --- /dev/null +++ b/queue-4.4/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch @@ -0,0 +1,206 @@ +From 90981de1b9a37ac9847125499bd66ea70a642bbd 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 90e29d40aa82e..734babb6626c4 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -2328,9 +2328,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; +@@ -2346,16 +2343,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, fs_info, fs_info->quota_root); +- if (ret < 0) { +- err = ret; +- btrfs_err(fs_info, "fail to update qgroup status: %d\n", 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, fs_info, fs_info->quota_root); ++ 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)) { +@@ -2366,12 +2377,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.4/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch b/queue-4.4/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch new file mode 100644 index 00000000000..9283abe68c7 --- /dev/null +++ b/queue-4.4/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch @@ -0,0 +1,401 @@ +From a7cdb9053b16ef05bdd896df8e548215ff97223a 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 f5d2d2340b44d..8cd5a775d13be 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -338,7 +338,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); + } + +@@ -350,16 +350,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) +@@ -372,10 +372,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); + } + } + +@@ -394,9 +394,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)) { +@@ -435,16 +435,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); + } + +@@ -479,16 +479,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); + } + + /* +@@ -503,9 +503,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); +@@ -513,7 +513,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() */ +@@ -647,12 +647,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; + } +@@ -1097,12 +1097,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; +@@ -1118,7 +1118,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; + +@@ -1155,7 +1155,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); +@@ -1169,7 +1169,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; +@@ -1187,7 +1187,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; +@@ -1201,13 +1201,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); +@@ -1237,7 +1237,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); +@@ -1266,16 +1266,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) +@@ -1298,7 +1301,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; +@@ -1354,7 +1357,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; + } + +@@ -2098,12 +2101,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; + } +@@ -2194,15 +2197,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 c6eb35a95fcc9..5109f1acda0df 100644 +--- a/fs/fuse/fuse_i.h ++++ b/fs/fuse/fuse_i.h +@@ -390,6 +390,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 4b2eb65be0d40..b353787fa35a0 100644 +--- a/fs/fuse/inode.c ++++ b/fs/fuse/inode.c +@@ -567,6 +567,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.4/series b/queue-4.4/series index 72fe477ff23..9c4fff8e456 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -96,3 +96,5 @@ cifs-fix-oplock-handling-for-smb-2.1-protocols.patch ovl-filter-of-trusted-xattr-results-in-audit.patch btrfs-fix-use-after-free-when-using-the-tree-modification-log.patch btrfs-relinquish-cpus-in-btrfs_compare_trees.patch +fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch +btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch -- 2.47.2