fuse: allow interrupt queuing without fc->lock
[cascardo/linux.git] / fs / fuse / dev.c
index 92c7691..c7f1a63 100644 (file)
@@ -48,6 +48,7 @@ static void fuse_request_init(struct fuse_req *req, struct page **pages,
        req->pages = pages;
        req->page_descs = page_descs;
        req->max_pages = npages;
+       __set_bit(FR_PENDING, &req->flags);
 }
 
 static struct fuse_req *__fuse_request_alloc(unsigned npages, gfp_t flags)
@@ -317,41 +318,39 @@ static unsigned len_args(unsigned numargs, struct fuse_arg *args)
        return nbytes;
 }
 
-static u64 fuse_get_unique(struct fuse_conn *fc)
+static u64 fuse_get_unique(struct fuse_iqueue *fiq)
 {
-       fc->reqctr++;
-       /* zero is special */
-       if (fc->reqctr == 0)
-               fc->reqctr = 1;
-
-       return fc->reqctr;
+       return ++fiq->reqctr;
 }
 
-static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
+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, &fc->pending);
-       req->state = FUSE_REQ_PENDING;
-       wake_up(&fc->waitq);
-       kill_fasync(&fc->fasync, SIGIO, POLL_IN);
+       list_add_tail(&req->list, &fiq->pending);
+       wake_up_locked(&fiq->waitq);
+       kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
 }
 
 void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
                       u64 nodeid, u64 nlookup)
 {
+       struct fuse_iqueue *fiq = &fc->iq;
+
        forget->forget_one.nodeid = nodeid;
        forget->forget_one.nlookup = nlookup;
 
        spin_lock(&fc->lock);
-       if (fc->connected) {
-               fc->forget_list_tail->next = forget;
-               fc->forget_list_tail = forget;
-               wake_up(&fc->waitq);
-               kill_fasync(&fc->fasync, SIGIO, POLL_IN);
+       spin_lock(&fiq->waitq.lock);
+       if (fiq->connected) {
+               fiq->forget_list_tail->next = forget;
+               fiq->forget_list_tail = forget;
+               wake_up_locked(&fiq->waitq);
+               kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
        } else {
                kfree(forget);
        }
+       spin_unlock(&fiq->waitq.lock);
        spin_unlock(&fc->lock);
 }
 
@@ -360,12 +359,15 @@ static void flush_bg_queue(struct fuse_conn *fc)
        while (fc->active_background < fc->max_background &&
               !list_empty(&fc->bg_queue)) {
                struct fuse_req *req;
+               struct fuse_iqueue *fiq = &fc->iq;
 
                req = list_entry(fc->bg_queue.next, struct fuse_req, list);
                list_del(&req->list);
                fc->active_background++;
-               req->in.h.unique = fuse_get_unique(fc);
-               queue_request(fc, req);
+               spin_lock(&fiq->waitq.lock);
+               req->in.h.unique = fuse_get_unique(fiq);
+               queue_request(fiq, req);
+               spin_unlock(&fiq->waitq.lock);
        }
 }
 
@@ -382,11 +384,17 @@ static void flush_bg_queue(struct fuse_conn *fc)
 static void request_end(struct fuse_conn *fc, struct fuse_req *req)
 __releases(fc->lock)
 {
+       struct fuse_iqueue *fiq = &fc->iq;
        void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
        req->end = NULL;
        list_del_init(&req->list);
+       spin_lock(&fiq->waitq.lock);
        list_del_init(&req->intr_entry);
-       req->state = FUSE_REQ_FINISHED;
+       spin_unlock(&fiq->waitq.lock);
+       WARN_ON(test_bit(FR_PENDING, &req->flags));
+       WARN_ON(test_bit(FR_SENT, &req->flags));
+       smp_wmb();
+       set_bit(FR_FINISHED, &req->flags);
        if (test_bit(FR_BACKGROUND, &req->flags)) {
                clear_bit(FR_BACKGROUND, &req->flags);
                if (fc->num_background == fc->max_background)
@@ -412,40 +420,36 @@ __releases(fc->lock)
        fuse_put_request(fc, req);
 }
 
-static void wait_answer_interruptible(struct fuse_conn *fc,
-                                     struct fuse_req *req)
-__releases(fc->lock)
-__acquires(fc->lock)
-{
-       if (signal_pending(current))
-               return;
-
-       spin_unlock(&fc->lock);
-       wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED);
-       spin_lock(&fc->lock);
-}
-
-static void queue_interrupt(struct fuse_conn *fc, struct fuse_req *req)
+static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
 {
-       list_add_tail(&req->intr_entry, &fc->interrupts);
-       wake_up(&fc->waitq);
-       kill_fasync(&fc->fasync, SIGIO, POLL_IN);
+       spin_lock(&fiq->waitq.lock);
+       if (list_empty(&req->intr_entry)) {
+               list_add_tail(&req->intr_entry, &fiq->interrupts);
+               wake_up_locked(&fiq->waitq);
+       }
+       spin_unlock(&fiq->waitq.lock);
+       kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
 }
 
 static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
-__releases(fc->lock)
-__acquires(fc->lock)
 {
+       struct fuse_iqueue *fiq = &fc->iq;
+       int err;
+
        if (!fc->no_interrupt) {
                /* Any signal may interrupt this */
-               wait_answer_interruptible(fc, req);
-
-               if (req->state == FUSE_REQ_FINISHED)
+               err = wait_event_interruptible(req->waitq,
+                                       test_bit(FR_FINISHED, &req->flags));
+               if (!err)
                        return;
 
+               spin_lock(&fc->lock);
                set_bit(FR_INTERRUPTED, &req->flags);
-               if (req->state == FUSE_REQ_SENT)
-                       queue_interrupt(fc, req);
+               /* matches barrier in fuse_dev_do_read() */
+               smp_mb__after_atomic();
+               if (test_bit(FR_SENT, &req->flags))
+                       queue_interrupt(fiq, req);
+               spin_unlock(&fc->lock);
        }
 
        if (!test_bit(FR_FORCE, &req->flags)) {
@@ -453,46 +457,59 @@ __acquires(fc->lock)
 
                /* Only fatal signals may interrupt this */
                block_sigs(&oldset);
-               wait_answer_interruptible(fc, req);
+               err = wait_event_interruptible(req->waitq,
+                                       test_bit(FR_FINISHED, &req->flags));
                restore_sigs(&oldset);
 
-               if (req->state == FUSE_REQ_FINISHED)
+               if (!err)
                        return;
 
+               spin_lock(&fc->lock);
+               spin_lock(&fiq->waitq.lock);
                /* Request is not yet in userspace, bail out */
-               if (req->state == FUSE_REQ_PENDING) {
+               if (test_bit(FR_PENDING, &req->flags)) {
                        list_del(&req->list);
+                       spin_unlock(&fiq->waitq.lock);
+                       spin_unlock(&fc->lock);
                        __fuse_put_request(req);
                        req->out.h.error = -EINTR;
                        return;
                }
+               spin_unlock(&fiq->waitq.lock);
+               spin_unlock(&fc->lock);
        }
 
        /*
         * Either request is already in userspace, or it was forced.
         * Wait it out.
         */
-       spin_unlock(&fc->lock);
-       wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
-       spin_lock(&fc->lock);
+       wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags));
 }
 
 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(&fc->lock);
-       if (!fc->connected)
+       spin_lock(&fiq->waitq.lock);
+       if (!fiq->connected) {
+               spin_unlock(&fc->lock);
+               spin_unlock(&fiq->waitq.lock);
                req->out.h.error = -ENOTCONN;
-       else {
-               req->in.h.unique = fuse_get_unique(fc);
-               queue_request(fc, req);
+       else {
+               req->in.h.unique = fuse_get_unique(fiq);
+               queue_request(fiq, req);
                /* acquire extra reference, since request is still needed
                   after request_end() */
                __fuse_get_request(req);
+               spin_unlock(&fiq->waitq.lock);
+               spin_unlock(&fc->lock);
 
                request_wait_answer(fc, req);
+               /* Pairs with smp_wmb() in request_end() */
+               smp_rmb();
        }
-       spin_unlock(&fc->lock);
 }
 
 void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
@@ -617,14 +634,17 @@ static int fuse_request_send_notify_reply(struct fuse_conn *fc,
                                          struct fuse_req *req, u64 unique)
 {
        int err = -ENODEV;
+       struct fuse_iqueue *fiq = &fc->iq;
 
        __clear_bit(FR_ISREPLY, &req->flags);
        req->in.h.unique = unique;
        spin_lock(&fc->lock);
-       if (fc->connected) {
-               queue_request(fc, req);
+       spin_lock(&fiq->waitq.lock);
+       if (fiq->connected) {
+               queue_request(fiq, req);
                err = 0;
        }
+       spin_unlock(&fiq->waitq.lock);
        spin_unlock(&fc->lock);
 
        return err;
@@ -1053,36 +1073,41 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
        return err;
 }
 
-static int forget_pending(struct fuse_conn *fc)
+static int forget_pending(struct fuse_iqueue *fiq)
 {
-       return fc->forget_list_head.next != NULL;
+       return fiq->forget_list_head.next != NULL;
 }
 
-static int request_pending(struct fuse_conn *fc)
+static int request_pending(struct fuse_iqueue *fiq)
 {
-       return !list_empty(&fc->pending) || !list_empty(&fc->interrupts) ||
-               forget_pending(fc);
+       return !list_empty(&fiq->pending) || !list_empty(&fiq->interrupts) ||
+               forget_pending(fiq);
 }
 
 /* Wait until a request is available on the pending list */
 static void request_wait(struct fuse_conn *fc)
+__releases(fc->iq.waitq.lock)
 __releases(fc->lock)
 __acquires(fc->lock)
+__acquires(fc->iq.waitq.lock)
 {
+       struct fuse_iqueue *fiq = &fc->iq;
        DECLARE_WAITQUEUE(wait, current);
 
-       add_wait_queue_exclusive(&fc->waitq, &wait);
-       while (fc->connected && !request_pending(fc)) {
+       add_wait_queue_exclusive(&fiq->waitq, &wait);
+       while (fiq->connected && !request_pending(fiq)) {
                set_current_state(TASK_INTERRUPTIBLE);
                if (signal_pending(current))
                        break;
 
+               spin_unlock(&fiq->waitq.lock);
                spin_unlock(&fc->lock);
                schedule();
                spin_lock(&fc->lock);
+               spin_lock(&fiq->waitq.lock);
        }
        set_current_state(TASK_RUNNING);
-       remove_wait_queue(&fc->waitq, &wait);
+       remove_wait_queue(&fiq->waitq, &wait);
 }
 
 /*
@@ -1095,15 +1120,17 @@ __acquires(fc->lock)
  */
 static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_copy_state *cs,
                               size_t nbytes, struct fuse_req *req)
+__releases(fc->iq.waitq.lock)
 __releases(fc->lock)
 {
+       struct fuse_iqueue *fiq = &fc->iq;
        struct fuse_in_header ih;
        struct fuse_interrupt_in arg;
        unsigned reqsize = sizeof(ih) + sizeof(arg);
        int err;
 
        list_del_init(&req->intr_entry);
-       req->intr_unique = fuse_get_unique(fc);
+       req->intr_unique = fuse_get_unique(fiq);
        memset(&ih, 0, sizeof(ih));
        memset(&arg, 0, sizeof(arg));
        ih.len = reqsize;
@@ -1111,6 +1138,7 @@ __releases(fc->lock)
        ih.unique = req->intr_unique;
        arg.unique = req->in.h.unique;
 
+       spin_unlock(&fiq->waitq.lock);
        spin_unlock(&fc->lock);
        if (nbytes < reqsize)
                return -EINVAL;
@@ -1123,21 +1151,21 @@ __releases(fc->lock)
        return err ? err : reqsize;
 }
 
-static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc,
+static struct fuse_forget_link *dequeue_forget(struct fuse_iqueue *fiq,
                                               unsigned max,
                                               unsigned *countp)
 {
-       struct fuse_forget_link *head = fc->forget_list_head.next;
+       struct fuse_forget_link *head = fiq->forget_list_head.next;
        struct fuse_forget_link **newhead = &head;
        unsigned count;
 
        for (count = 0; *newhead != NULL && count < max; count++)
                newhead = &(*newhead)->next;
 
-       fc->forget_list_head.next = *newhead;
+       fiq->forget_list_head.next = *newhead;
        *newhead = NULL;
-       if (fc->forget_list_head.next == NULL)
-               fc->forget_list_tail = &fc->forget_list_head;
+       if (fiq->forget_list_head.next == NULL)
+               fiq->forget_list_tail = &fiq->forget_list_head;
 
        if (countp != NULL)
                *countp = count;
@@ -1148,20 +1176,23 @@ static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc,
 static int fuse_read_single_forget(struct fuse_conn *fc,
                                   struct fuse_copy_state *cs,
                                   size_t nbytes)
+__releases(fc->iq.waitq.lock)
 __releases(fc->lock)
 {
        int err;
-       struct fuse_forget_link *forget = dequeue_forget(fc, 1, NULL);
+       struct fuse_iqueue *fiq = &fc->iq;
+       struct fuse_forget_link *forget = dequeue_forget(fiq, 1, NULL);
        struct fuse_forget_in arg = {
                .nlookup = forget->forget_one.nlookup,
        };
        struct fuse_in_header ih = {
                .opcode = FUSE_FORGET,
                .nodeid = forget->forget_one.nodeid,
-               .unique = fuse_get_unique(fc),
+               .unique = fuse_get_unique(fiq),
                .len = sizeof(ih) + sizeof(arg),
        };
 
+       spin_unlock(&fiq->waitq.lock);
        spin_unlock(&fc->lock);
        kfree(forget);
        if (nbytes < ih.len)
@@ -1180,26 +1211,30 @@ __releases(fc->lock)
 
 static int fuse_read_batch_forget(struct fuse_conn *fc,
                                   struct fuse_copy_state *cs, size_t nbytes)
+__releases(fc->iq.waitq.lock)
 __releases(fc->lock)
 {
        int err;
        unsigned max_forgets;
        unsigned count;
        struct fuse_forget_link *head;
+       struct fuse_iqueue *fiq = &fc->iq;
        struct fuse_batch_forget_in arg = { .count = 0 };
        struct fuse_in_header ih = {
                .opcode = FUSE_BATCH_FORGET,
-               .unique = fuse_get_unique(fc),
+               .unique = fuse_get_unique(fiq),
                .len = sizeof(ih) + sizeof(arg),
        };
 
        if (nbytes < ih.len) {
+               spin_unlock(&fiq->waitq.lock);
                spin_unlock(&fc->lock);
                return -EINVAL;
        }
 
        max_forgets = (nbytes - ih.len) / sizeof(struct fuse_forget_one);
-       head = dequeue_forget(fc, max_forgets, &count);
+       head = dequeue_forget(fiq, max_forgets, &count);
+       spin_unlock(&fiq->waitq.lock);
        spin_unlock(&fc->lock);
 
        arg.count = count;
@@ -1229,9 +1264,12 @@ __releases(fc->lock)
 
 static int fuse_read_forget(struct fuse_conn *fc, struct fuse_copy_state *cs,
                            size_t nbytes)
+__releases(fc->iq.waitq.lock)
 __releases(fc->lock)
 {
-       if (fc->minor < 16 || fc->forget_list_head.next->next == NULL)
+       struct fuse_iqueue *fiq = &fc->iq;
+
+       if (fc->minor < 16 || fiq->forget_list_head.next->next == NULL)
                return fuse_read_single_forget(fc, cs, nbytes);
        else
                return fuse_read_batch_forget(fc, cs, nbytes);
@@ -1250,42 +1288,47 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
                                struct fuse_copy_state *cs, size_t nbytes)
 {
        int err;
+       struct fuse_iqueue *fiq = &fc->iq;
        struct fuse_req *req;
        struct fuse_in *in;
        unsigned reqsize;
 
  restart:
        spin_lock(&fc->lock);
+       spin_lock(&fiq->waitq.lock);
        err = -EAGAIN;
-       if ((file->f_flags & O_NONBLOCK) && fc->connected &&
-           !request_pending(fc))
+       if ((file->f_flags & O_NONBLOCK) && fiq->connected &&
+           !request_pending(fiq))
                goto err_unlock;
 
        request_wait(fc);
        err = -ENODEV;
-       if (!fc->connected)
+       if (!fiq->connected)
                goto err_unlock;
        err = -ERESTARTSYS;
-       if (!request_pending(fc))
+       if (!request_pending(fiq))
                goto err_unlock;
 
-       if (!list_empty(&fc->interrupts)) {
-               req = list_entry(fc->interrupts.next, struct fuse_req,
+       if (!list_empty(&fiq->interrupts)) {
+               req = list_entry(fiq->interrupts.next, struct fuse_req,
                                 intr_entry);
                return fuse_read_interrupt(fc, cs, nbytes, req);
        }
 
-       if (forget_pending(fc)) {
-               if (list_empty(&fc->pending) || fc->forget_batch-- > 0)
+       if (forget_pending(fiq)) {
+               if (list_empty(&fiq->pending) || fiq->forget_batch-- > 0)
                        return fuse_read_forget(fc, cs, nbytes);
 
-               if (fc->forget_batch <= -8)
-                       fc->forget_batch = 16;
+               if (fiq->forget_batch <= -8)
+                       fiq->forget_batch = 16;
        }
 
-       req = list_entry(fc->pending.next, struct fuse_req, list);
-       req->state = FUSE_REQ_READING;
-       list_move(&req->list, &fc->io);
+       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);
+
+       list_add(&req->list, &fc->io);
 
        in = &req->in;
        reqsize = in->h.len;
@@ -1319,15 +1362,18 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
        if (!test_bit(FR_ISREPLY, &req->flags)) {
                request_end(fc, req);
        } else {
-               req->state = FUSE_REQ_SENT;
                list_move_tail(&req->list, &fc->processing);
+               set_bit(FR_SENT, &req->flags);
+               /* matches barrier in request_wait_answer() */
+               smp_mb__after_atomic();
                if (test_bit(FR_INTERRUPTED, &req->flags))
-                       queue_interrupt(fc, req);
+                       queue_interrupt(fiq, req);
                spin_unlock(&fc->lock);
        }
        return reqsize;
 
  err_unlock:
+       spin_unlock(&fiq->waitq.lock);
        spin_unlock(&fc->lock);
        return err;
 }
@@ -1908,14 +1954,14 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
                if (oh.error == -ENOSYS)
                        fc->no_interrupt = 1;
                else if (oh.error == -EAGAIN)
-                       queue_interrupt(fc, req);
+                       queue_interrupt(&fc->iq, req);
 
                spin_unlock(&fc->lock);
                fuse_copy_finish(cs);
                return nbytes;
        }
 
-       req->state = FUSE_REQ_WRITING;
+       clear_bit(FR_SENT, &req->flags);
        list_move(&req->list, &fc->io);
        req->out.h = oh;
        set_bit(FR_LOCKED, &req->flags);
@@ -2041,17 +2087,21 @@ out:
 static unsigned fuse_dev_poll(struct file *file, poll_table *wait)
 {
        unsigned mask = POLLOUT | POLLWRNORM;
+       struct fuse_iqueue *fiq;
        struct fuse_conn *fc = fuse_get_conn(file);
        if (!fc)
                return POLLERR;
 
-       poll_wait(file, &fc->waitq, wait);
+       fiq = &fc->iq;
+       poll_wait(file, &fiq->waitq, wait);
 
        spin_lock(&fc->lock);
-       if (!fc->connected)
+       spin_lock(&fiq->waitq.lock);
+       if (!fiq->connected)
                mask = POLLERR;
-       else if (request_pending(fc))
+       else if (request_pending(fiq))
                mask |= POLLIN | POLLRDNORM;
+       spin_unlock(&fiq->waitq.lock);
        spin_unlock(&fc->lock);
 
        return mask;
@@ -2070,56 +2120,13 @@ __acquires(fc->lock)
                struct fuse_req *req;
                req = list_entry(head->next, struct fuse_req, list);
                req->out.h.error = -ECONNABORTED;
+               clear_bit(FR_PENDING, &req->flags);
+               clear_bit(FR_SENT, &req->flags);
                request_end(fc, req);
                spin_lock(&fc->lock);
        }
 }
 
-/*
- * Abort requests under I/O
- *
- * Separate out unlocked requests, they should be finished off immediately.
- * Locked requests will be finished after unlock; see unlock_request().
- *
- * Next finish off the unlocked requests.  It is possible that some request will
- * finish before we can.  This is OK, the request will in that case be removed
- * from the list before we touch it.
- */
-static void end_io_requests(struct fuse_conn *fc)
-__releases(fc->lock)
-__acquires(fc->lock)
-{
-       struct fuse_req *req, *next;
-       LIST_HEAD(to_end);
-
-       list_for_each_entry_safe(req, next, &fc->io, list) {
-               req->out.h.error = -ECONNABORTED;
-               spin_lock(&req->waitq.lock);
-               set_bit(FR_ABORTED, &req->flags);
-               if (!test_bit(FR_LOCKED, &req->flags))
-                       list_move(&req->list, &to_end);
-               spin_unlock(&req->waitq.lock);
-       }
-       while (!list_empty(&to_end)) {
-               req = list_first_entry(&to_end, struct fuse_req, list);
-               __fuse_get_request(req);
-               request_end(fc, req);
-               spin_lock(&fc->lock);
-       }
-}
-
-static void end_queued_requests(struct fuse_conn *fc)
-__releases(fc->lock)
-__acquires(fc->lock)
-{
-       fc->max_background = UINT_MAX;
-       flush_bg_queue(fc);
-       end_requests(fc, &fc->pending);
-       end_requests(fc, &fc->processing);
-       while (forget_pending(fc))
-               kfree(dequeue_forget(fc, 1, NULL));
-}
-
 static void end_polls(struct fuse_conn *fc)
 {
        struct rb_node *p;
@@ -2138,30 +2145,64 @@ static void end_polls(struct fuse_conn *fc)
 /*
  * Abort all requests.
  *
- * Emergency exit in case of a malicious or accidental deadlock, or
- * just a hung filesystem.
+ * Emergency exit in case of a malicious or accidental deadlock, or just a hung
+ * filesystem.
  *
- * The same effect is usually achievable through killing the
- * filesystem daemon and all users of the filesystem.  The exception
- * is the combination of an asynchronous request and the tricky
- * deadlock (see Documentation/filesystems/fuse.txt).
+ * The same effect is usually achievable through killing the filesystem daemon
+ * and all users of the filesystem.  The exception is the combination of an
+ * asynchronous request and the tricky deadlock (see
+ * Documentation/filesystems/fuse.txt).
  *
- * Request progression from one list to the next is prevented by
- * fc->connected being false.
+ * Aborting requests under I/O goes as follows: 1: Separate out unlocked
+ * requests, they should be finished off immediately.  Locked requests will be
+ * finished after unlock; see unlock_request(). 2: Finish off the unlocked
+ * requests.  It is possible that some request will finish before we can.  This
+ * is OK, the request will in that case be removed from the list before we touch
+ * it.
  */
 void fuse_abort_conn(struct fuse_conn *fc)
 {
+       struct fuse_iqueue *fiq = &fc->iq;
+
        spin_lock(&fc->lock);
        if (fc->connected) {
+               struct fuse_req *req, *next;
+               LIST_HEAD(to_end1);
+               LIST_HEAD(to_end2);
+
                fc->connected = 0;
                fc->blocked = 0;
                fuse_set_initialized(fc);
-               end_io_requests(fc);
-               end_queued_requests(fc);
+               list_for_each_entry_safe(req, next, &fc->io, list) {
+                       req->out.h.error = -ECONNABORTED;
+                       spin_lock(&req->waitq.lock);
+                       set_bit(FR_ABORTED, &req->flags);
+                       if (!test_bit(FR_LOCKED, &req->flags))
+                               list_move(&req->list, &to_end1);
+                       spin_unlock(&req->waitq.lock);
+               }
+               fc->max_background = UINT_MAX;
+               flush_bg_queue(fc);
+
+               spin_lock(&fiq->waitq.lock);
+               fiq->connected = 0;
+               list_splice_init(&fiq->pending, &to_end2);
+               while (forget_pending(fiq))
+                       kfree(dequeue_forget(fiq, 1, NULL));
+               wake_up_all_locked(&fiq->waitq);
+               spin_unlock(&fiq->waitq.lock);
+               kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
+
+               list_splice_init(&fc->processing, &to_end2);
+               while (!list_empty(&to_end1)) {
+                       req = list_first_entry(&to_end1, struct fuse_req, list);
+                       __fuse_get_request(req);
+                       request_end(fc, req);
+                       spin_lock(&fc->lock);
+               }
+               end_requests(fc, &to_end2);
                end_polls(fc);
-               wake_up_all(&fc->waitq);
                wake_up_all(&fc->blocked_waitq);
-               kill_fasync(&fc->fasync, SIGIO, POLL_IN);
        }
        spin_unlock(&fc->lock);
 }
@@ -2172,7 +2213,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)
        struct fuse_conn *fc = fuse_get_conn(file);
        if (fc) {
                WARN_ON(!list_empty(&fc->io));
-               WARN_ON(fc->fasync != NULL);
+               WARN_ON(fc->iq.fasync != NULL);
                fuse_abort_conn(fc);
                fuse_conn_put(fc);
        }
@@ -2188,7 +2229,7 @@ static int fuse_dev_fasync(int fd, struct file *file, int on)
                return -EPERM;
 
        /* No locking - fasync_helper does its own locking */
-       return fasync_helper(fd, file, on, &fc->fasync);
+       return fasync_helper(fd, file, on, &fc->iq.fasync);
 }
 
 const struct file_operations fuse_dev_operations = {