fuse: use per req lock for lock/unlock_request()
[deliverable/linux.git] / fs / fuse / dev.c
index c8b68ab2e574a86f13fab97f9ed47b14a4e139d6..92c7691df429802cef2a167197bf39ebbe45527b 100644 (file)
@@ -168,6 +168,10 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages,
        if (!fc->connected)
                goto out;
 
+       err = -ECONNREFUSED;
+       if (fc->conn_error)
+               goto out;
+
        req = fuse_request_alloc(npages);
        err = -ENOMEM;
        if (!req) {
@@ -177,8 +181,10 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages,
        }
 
        fuse_req_init_context(req);
-       req->waiting = 1;
-       req->background = for_background;
+       __set_bit(FR_WAITING, &req->flags);
+       if (for_background)
+               __set_bit(FR_BACKGROUND, &req->flags);
+
        return req;
 
  out:
@@ -268,15 +274,15 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc,
                req = get_reserved_req(fc, file);
 
        fuse_req_init_context(req);
-       req->waiting = 1;
-       req->background = 0;
+       __set_bit(FR_WAITING, &req->flags);
+       __clear_bit(FR_BACKGROUND, &req->flags);
        return req;
 }
 
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
 {
        if (atomic_dec_and_test(&req->count)) {
-               if (unlikely(req->background)) {
+               if (test_bit(FR_BACKGROUND, &req->flags)) {
                        /*
                         * We get here in the unlikely case that a background
                         * request was allocated but not sent
@@ -287,8 +293,10 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
                        spin_unlock(&fc->lock);
                }
 
-               if (req->waiting)
+               if (test_bit(FR_WAITING, &req->flags)) {
+                       __clear_bit(FR_WAITING, &req->flags);
                        atomic_dec(&fc->num_waiting);
+               }
 
                if (req->stolen_file)
                        put_reserved_req(fc, req);
@@ -325,10 +333,6 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
                len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
        list_add_tail(&req->list, &fc->pending);
        req->state = FUSE_REQ_PENDING;
-       if (!req->waiting) {
-               req->waiting = 1;
-               atomic_inc(&fc->num_waiting);
-       }
        wake_up(&fc->waitq);
        kill_fasync(&fc->fasync, SIGIO, POLL_IN);
 }
@@ -380,12 +384,11 @@ __releases(fc->lock)
 {
        void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
        req->end = NULL;
-       list_del(&req->list);
-       list_del(&req->intr_entry);
+       list_del_init(&req->list);
+       list_del_init(&req->intr_entry);
        req->state = FUSE_REQ_FINISHED;
-       if (req->background) {
-               req->background = 0;
-
+       if (test_bit(FR_BACKGROUND, &req->flags)) {
+               clear_bit(FR_BACKGROUND, &req->flags);
                if (fc->num_background == fc->max_background)
                        fc->blocked = 0;
 
@@ -437,17 +440,15 @@ __acquires(fc->lock)
                /* Any signal may interrupt this */
                wait_answer_interruptible(fc, req);
 
-               if (req->aborted)
-                       goto aborted;
                if (req->state == FUSE_REQ_FINISHED)
                        return;
 
-               req->interrupted = 1;
+               set_bit(FR_INTERRUPTED, &req->flags);
                if (req->state == FUSE_REQ_SENT)
                        queue_interrupt(fc, req);
        }
 
-       if (!req->force) {
+       if (!test_bit(FR_FORCE, &req->flags)) {
                sigset_t oldset;
 
                /* Only fatal signals may interrupt this */
@@ -455,8 +456,6 @@ __acquires(fc->lock)
                wait_answer_interruptible(fc, req);
                restore_sigs(&oldset);
 
-               if (req->aborted)
-                       goto aborted;
                if (req->state == FUSE_REQ_FINISHED)
                        return;
 
@@ -476,32 +475,14 @@ __acquires(fc->lock)
        spin_unlock(&fc->lock);
        wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
        spin_lock(&fc->lock);
-
-       if (!req->aborted)
-               return;
-
- aborted:
-       BUG_ON(req->state != FUSE_REQ_FINISHED);
-       if (req->locked) {
-               /* This is uninterruptible sleep, because data is
-                  being copied to/from the buffers of req.  During
-                  locked state, there mustn't be any filesystem
-                  operation (e.g. page fault), since that could lead
-                  to deadlock */
-               spin_unlock(&fc->lock);
-               wait_event(req->waitq, !req->locked);
-               spin_lock(&fc->lock);
-       }
 }
 
 static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
 {
-       BUG_ON(req->background);
+       BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
        spin_lock(&fc->lock);
        if (!fc->connected)
                req->out.h.error = -ENOTCONN;
-       else if (fc->conn_error)
-               req->out.h.error = -ECONNREFUSED;
        else {
                req->in.h.unique = fuse_get_unique(fc);
                queue_request(fc, req);
@@ -516,7 +497,11 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
 
 void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
 {
-       req->isreply = 1;
+       __set_bit(FR_ISREPLY, &req->flags);
+       if (!test_bit(FR_WAITING, &req->flags)) {
+               __set_bit(FR_WAITING, &req->flags);
+               atomic_inc(&fc->num_waiting);
+       }
        __fuse_request_send(fc, req);
 }
 EXPORT_SYMBOL_GPL(fuse_request_send);
@@ -586,10 +571,20 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
        return ret;
 }
 
-static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
-                                           struct fuse_req *req)
+/*
+ * Called under fc->lock
+ *
+ * fc->connected must have been checked previously
+ */
+void fuse_request_send_background_locked(struct fuse_conn *fc,
+                                        struct fuse_req *req)
 {
-       BUG_ON(!req->background);
+       BUG_ON(!test_bit(FR_BACKGROUND, &req->flags));
+       if (!test_bit(FR_WAITING, &req->flags)) {
+               __set_bit(FR_WAITING, &req->flags);
+               atomic_inc(&fc->num_waiting);
+       }
+       __set_bit(FR_ISREPLY, &req->flags);
        fc->num_background++;
        if (fc->num_background == fc->max_background)
                fc->blocked = 1;
@@ -602,23 +597,20 @@ static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
        flush_bg_queue(fc);
 }
 
-static void fuse_request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
 {
+       BUG_ON(!req->end);
        spin_lock(&fc->lock);
        if (fc->connected) {
-               fuse_request_send_nowait_locked(fc, req);
+               fuse_request_send_background_locked(fc, req);
                spin_unlock(&fc->lock);
        } else {
+               spin_unlock(&fc->lock);
                req->out.h.error = -ENOTCONN;
-               request_end(fc, req);
+               req->end(fc, req);
+               fuse_put_request(fc, req);
        }
 }
-
-void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
-{
-       req->isreply = 1;
-       fuse_request_send_nowait(fc, req);
-}
 EXPORT_SYMBOL_GPL(fuse_request_send_background);
 
 static int fuse_request_send_notify_reply(struct fuse_conn *fc,
@@ -626,7 +618,7 @@ static int fuse_request_send_notify_reply(struct fuse_conn *fc,
 {
        int err = -ENODEV;
 
-       req->isreply = 0;
+       __clear_bit(FR_ISREPLY, &req->flags);
        req->in.h.unique = unique;
        spin_lock(&fc->lock);
        if (fc->connected) {
@@ -638,18 +630,6 @@ static int fuse_request_send_notify_reply(struct fuse_conn *fc,
        return err;
 }
 
-/*
- * Called under fc->lock
- *
- * fc->connected must have been checked previously
- */
-void fuse_request_send_background_locked(struct fuse_conn *fc,
-                                        struct fuse_req *req)
-{
-       req->isreply = 1;
-       fuse_request_send_nowait_locked(fc, req);
-}
-
 void fuse_force_forget(struct file *file, u64 nodeid)
 {
        struct inode *inode = file_inode(file);
@@ -665,7 +645,7 @@ void fuse_force_forget(struct file *file, u64 nodeid)
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(inarg);
        req->in.args[0].value = &inarg;
-       req->isreply = 0;
+       __clear_bit(FR_ISREPLY, &req->flags);
        __fuse_request_send(fc, req);
        /* ignore errors */
        fuse_put_request(fc, req);
@@ -676,38 +656,39 @@ void fuse_force_forget(struct file *file, u64 nodeid)
  * anything that could cause a page-fault.  If the request was already
  * aborted bail out.
  */
-static int lock_request(struct fuse_conn *fc, struct fuse_req *req)
+static int lock_request(struct fuse_req *req)
 {
        int err = 0;
        if (req) {
-               spin_lock(&fc->lock);
-               if (req->aborted)
+               spin_lock(&req->waitq.lock);
+               if (test_bit(FR_ABORTED, &req->flags))
                        err = -ENOENT;
                else
-                       req->locked = 1;
-               spin_unlock(&fc->lock);
+                       set_bit(FR_LOCKED, &req->flags);
+               spin_unlock(&req->waitq.lock);
        }
        return err;
 }
 
 /*
- * Unlock request.  If it was aborted during being locked, the
- * requester thread is currently waiting for it to be unlocked, so
- * wake it up.
+ * Unlock request.  If it was aborted while locked, caller is responsible
+ * for unlocking and ending the request.
  */
-static void unlock_request(struct fuse_conn *fc, struct fuse_req *req)
+static int unlock_request(struct fuse_req *req)
 {
+       int err = 0;
        if (req) {
-               spin_lock(&fc->lock);
-               req->locked = 0;
-               if (req->aborted)
-                       wake_up(&req->waitq);
-               spin_unlock(&fc->lock);
+               spin_lock(&req->waitq.lock);
+               if (test_bit(FR_ABORTED, &req->flags))
+                       err = -ENOENT;
+               else
+                       clear_bit(FR_LOCKED, &req->flags);
+               spin_unlock(&req->waitq.lock);
        }
+       return err;
 }
 
 struct fuse_copy_state {
-       struct fuse_conn *fc;
        int write;
        struct fuse_req *req;
        struct iov_iter *iter;
@@ -721,13 +702,10 @@ struct fuse_copy_state {
        unsigned move_pages:1;
 };
 
-static void fuse_copy_init(struct fuse_copy_state *cs,
-                          struct fuse_conn *fc,
-                          int write,
+static void fuse_copy_init(struct fuse_copy_state *cs, int write,
                           struct iov_iter *iter)
 {
        memset(cs, 0, sizeof(*cs));
-       cs->fc = fc;
        cs->write = write;
        cs->iter = iter;
 }
@@ -760,7 +738,10 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
        struct page *page;
        int err;
 
-       unlock_request(cs->fc, cs->req);
+       err = unlock_request(cs->req);
+       if (err)
+               return err;
+
        fuse_copy_finish(cs);
        if (cs->pipebufs) {
                struct pipe_buffer *buf = cs->pipebufs;
@@ -809,7 +790,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)
                iov_iter_advance(cs->iter, err);
        }
 
-       return lock_request(cs->fc, cs->req);
+       return lock_request(cs->req);
 }
 
 /* Do as much copy to/from userspace buffer as we can */
@@ -860,7 +841,10 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
        struct page *newpage;
        struct pipe_buffer *buf = cs->pipebufs;
 
-       unlock_request(cs->fc, cs->req);
+       err = unlock_request(cs->req);
+       if (err)
+               return err;
+
        fuse_copy_finish(cs);
 
        err = buf->ops->confirm(cs->pipe, buf);
@@ -914,12 +898,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
                lru_cache_add_file(newpage);
 
        err = 0;
-       spin_lock(&cs->fc->lock);
-       if (cs->req->aborted)
+       spin_lock(&cs->req->waitq.lock);
+       if (test_bit(FR_ABORTED, &cs->req->flags))
                err = -ENOENT;
        else
                *pagep = newpage;
-       spin_unlock(&cs->fc->lock);
+       spin_unlock(&cs->req->waitq.lock);
 
        if (err) {
                unlock_page(newpage);
@@ -939,7 +923,7 @@ out_fallback:
        cs->pg = buf->page;
        cs->offset = buf->offset;
 
-       err = lock_request(cs->fc, cs->req);
+       err = lock_request(cs->req);
        if (err)
                return err;
 
@@ -950,11 +934,15 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
                         unsigned offset, unsigned count)
 {
        struct pipe_buffer *buf;
+       int err;
 
        if (cs->nr_segs == cs->pipe->buffers)
                return -EIO;
 
-       unlock_request(cs->fc, cs->req);
+       err = unlock_request(cs->req);
+       if (err)
+               return err;
+
        fuse_copy_finish(cs);
 
        buf = cs->pipebufs;
@@ -1318,8 +1306,8 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
                                     (struct fuse_arg *) in->args, 0);
        fuse_copy_finish(cs);
        spin_lock(&fc->lock);
-       req->locked = 0;
-       if (req->aborted) {
+       clear_bit(FR_LOCKED, &req->flags);
+       if (!fc->connected) {
                request_end(fc, req);
                return -ENODEV;
        }
@@ -1328,12 +1316,12 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
                request_end(fc, req);
                return err;
        }
-       if (!req->isreply)
+       if (!test_bit(FR_ISREPLY, &req->flags)) {
                request_end(fc, req);
-       else {
+       else {
                req->state = FUSE_REQ_SENT;
                list_move_tail(&req->list, &fc->processing);
-               if (req->interrupted)
+               if (test_bit(FR_INTERRUPTED, &req->flags))
                        queue_interrupt(fc, req);
                spin_unlock(&fc->lock);
        }
@@ -1366,7 +1354,7 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to)
        if (!iter_is_iovec(to))
                return -EINVAL;
 
-       fuse_copy_init(&cs, fc, 1, to);
+       fuse_copy_init(&cs, 1, to);
 
        return fuse_dev_do_read(fc, file, &cs, iov_iter_count(to));
 }
@@ -1388,7 +1376,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
        if (!bufs)
                return -ENOMEM;
 
-       fuse_copy_init(&cs, fc, 1, NULL);
+       fuse_copy_init(&cs, 1, NULL);
        cs.pipebufs = bufs;
        cs.pipe = pipe;
        ret = fuse_dev_do_read(fc, in, &cs, len);
@@ -1911,13 +1899,6 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
        if (!req)
                goto err_unlock;
 
-       if (req->aborted) {
-               spin_unlock(&fc->lock);
-               fuse_copy_finish(cs);
-               spin_lock(&fc->lock);
-               request_end(fc, req);
-               return -ENOENT;
-       }
        /* Is it an interrupt reply? */
        if (req->intr_unique == oh.unique) {
                err = -EINVAL;
@@ -1937,7 +1918,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
        req->state = FUSE_REQ_WRITING;
        list_move(&req->list, &fc->io);
        req->out.h = oh;
-       req->locked = 1;
+       set_bit(FR_LOCKED, &req->flags);
        cs->req = req;
        if (!req->out.page_replace)
                cs->move_pages = 0;
@@ -1947,11 +1928,10 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
        fuse_copy_finish(cs);
 
        spin_lock(&fc->lock);
-       req->locked = 0;
-       if (!err) {
-               if (req->aborted)
-                       err = -ENOENT;
-       } else if (!req->aborted)
+       clear_bit(FR_LOCKED, &req->flags);
+       if (!fc->connected)
+               err = -ENOENT;
+       else if (err)
                req->out.h.error = -EIO;
        request_end(fc, req);
 
@@ -1974,7 +1954,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from)
        if (!iter_is_iovec(from))
                return -EINVAL;
 
-       fuse_copy_init(&cs, fc, 0, from);
+       fuse_copy_init(&cs, 0, from);
 
        return fuse_dev_do_write(fc, &cs, iov_iter_count(from));
 }
@@ -2039,7 +2019,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
        }
        pipe_unlock(pipe);
 
-       fuse_copy_init(&cs, fc, 0, NULL);
+       fuse_copy_init(&cs, 0, NULL);
        cs.pipebufs = bufs;
        cs.nr_segs = nbuf;
        cs.pipe = pipe;
@@ -2098,37 +2078,33 @@ __acquires(fc->lock)
 /*
  * Abort requests under I/O
  *
- * The requests are set to aborted and finished, and the request
- * waiter is woken up.  This will make request_wait_answer() wait
- * until the request is unlocked and then return.
+ * Separate out unlocked requests, they should be finished off immediately.
+ * Locked requests will be finished after unlock; see unlock_request().
  *
- * If the request is asynchronous, then the end function needs to be
- * called after waiting for the request to be unlocked (if it was
- * locked).
+ * 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)
 {
-       while (!list_empty(&fc->io)) {
-               struct fuse_req *req =
-                       list_entry(fc->io.next, struct fuse_req, list);
-               void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
+       struct fuse_req *req, *next;
+       LIST_HEAD(to_end);
 
-               req->aborted = 1;
+       list_for_each_entry_safe(req, next, &fc->io, list) {
                req->out.h.error = -ECONNABORTED;
-               req->state = FUSE_REQ_FINISHED;
-               list_del_init(&req->list);
-               wake_up(&req->waitq);
-               if (end) {
-                       req->end = NULL;
-                       __fuse_get_request(req);
-                       spin_unlock(&fc->lock);
-                       wait_event(req->waitq, !req->locked);
-                       end(fc, req);
-                       fuse_put_request(fc, req);
-                       spin_lock(&fc->lock);
-               }
+               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);
        }
 }
 
@@ -2170,13 +2146,8 @@ static void end_polls(struct fuse_conn *fc)
  * is the combination of an asynchronous request and the tricky
  * deadlock (see Documentation/filesystems/fuse.txt).
  *
- * During the aborting, progression of requests from the pending and
- * processing lists onto the io list, and progression of new requests
- * onto the pending list is prevented by req->connected being false.
- *
- * Progression of requests under I/O to the processing list is
- * prevented by the req->aborted flag being true for these requests.
- * For this reason requests on the io list must be aborted first.
+ * Request progression from one list to the next is prevented by
+ * fc->connected being false.
  */
 void fuse_abort_conn(struct fuse_conn *fc)
 {
@@ -2200,14 +2171,9 @@ int fuse_dev_release(struct inode *inode, struct file *file)
 {
        struct fuse_conn *fc = fuse_get_conn(file);
        if (fc) {
-               spin_lock(&fc->lock);
-               fc->connected = 0;
-               fc->blocked = 0;
-               fuse_set_initialized(fc);
-               end_queued_requests(fc);
-               end_polls(fc);
-               wake_up_all(&fc->blocked_waitq);
-               spin_unlock(&fc->lock);
+               WARN_ON(!list_empty(&fc->io));
+               WARN_ON(fc->fasync != NULL);
+               fuse_abort_conn(fc);
                fuse_conn_put(fc);
        }
 
This page took 0.035054 seconds and 5 git commands to generate.