]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
fixes for 4.4
authorSasha Levin <sashal@kernel.org>
Thu, 3 Oct 2019 14:45:47 +0000 (10:45 -0400)
committerSasha Levin <sashal@kernel.org>
Thu, 3 Oct 2019 14:45:47 +0000 (10:45 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-4.4/btrfs-fix-race-setting-up-and-completing-qgroup-resc.patch [new file with mode: 0644]
queue-4.4/fuse-fix-deadlock-with-aio-poll-and-fuse_iqueue-wait.patch [new file with mode: 0644]
queue-4.4/series

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 (file)
index 0000000..b832967
--- /dev/null
@@ -0,0 +1,206 @@
+From 90981de1b9a37ac9847125499bd66ea70a642bbd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2019 10:49:54 +0100
+Subject: Btrfs: fix race setting up and completing qgroup rescan workers
+
+From: Filipe Manana <fdmanana@suse.com>
+
+[ 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 <josef@toxicpanda.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 (file)
index 0000000..9283abe
--- /dev/null
@@ -0,0 +1,401 @@
+From a7cdb9053b16ef05bdd896df8e548215ff97223a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 8 Sep 2019 20:15:18 -0700
+Subject: fuse: fix deadlock with aio poll and fuse_iqueue::waitq.lock
+
+From: Eric Biggers <ebiggers@google.com>
+
+[ 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 <fcntl.h>
+       #include <stdio.h>
+       #include <sys/mount.h>
+       #include <sys/stat.h>
+       #include <sys/syscall.h>
+       #include <unistd.h>
+       #include <linux/aio_abi.h>
+
+       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: <stable@vger.kernel.org> # v4.19+
+Cc: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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
+
index 72fe477ff2339d5353bd33b15c152eeaa5b403fc..9c4fff8e456ae45a9fe2cbbfd9cff663edc3cf89 100644 (file)
@@ -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