From 0ad0b3255a08020eaf50e34ef0d6df5bdf5e09ed Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:55 +0200 Subject: fuse: initialize fc->release before calling it fc->release is called from fuse_conn_put() which was used in the error cleanup before fc->release was initialized. [Jeremiah Mahler : assign fc->release after calling fuse_conn_init(fc) instead of before.] Signed-off-by: Miklos Szeredi Fixes: a325f9b92273 ("fuse: update fuse_conn_init() and separate out fuse_conn_kill()") Cc: #v2.6.31+ --- fs/fuse/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/fuse') diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 082ac1c97f39..cec8abbe2c8c 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1026,6 +1026,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) goto err_fput; fuse_conn_init(fc); + fc->release = fuse_free_conn; fc->dev = sb->s_dev; fc->sb = sb; @@ -1040,7 +1041,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fc->dont_mask = 1; sb->s_flags |= MS_POSIXACL; - fc->release = fuse_free_conn; fc->flags = d.flags; fc->user_id = d.user_id; fc->group_id = d.group_id; -- cgit From 42dc6211c54aa93e6a483469d07bd06f91713af7 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:56 +0200 Subject: fuse: fix background request if not connected request_end() expects fc->num_background and fc->active_background to have been incremented, which is not the case in fuse_request_send_nowait() failure path. So instead just call the ->end() callback (which is actually set by all callers). Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index c8b68ab2e574..6aa4803510e7 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -604,13 +604,16 @@ static void fuse_request_send_nowait_locked(struct fuse_conn *fc, static void fuse_request_send_nowait(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); 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); } } -- cgit From 73e0e738441b26a2dfc1ccdf1462cd1dc13c8cea Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:56 +0200 Subject: fuse: reset waiting Reset req->waiting in fuse_put_request(). This is needed for correct accounting in fc->num_waiting for reserved requests. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 6aa4803510e7..24f1d77b87a4 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -287,8 +287,10 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) spin_unlock(&fc->lock); } - if (req->waiting) + if (req->waiting) { atomic_dec(&fc->num_waiting); + req->waiting = 0; + } if (req->stolen_file) put_reserved_req(fc, req); -- cgit From 5437f2417225dc89c785867f4790012668006abc Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:56 +0200 Subject: fuse: account as waiting before queuing for background Move accounting of fc->num_waiting to the point where the request actually starts waiting. This is earlier than the current queue_request() for background requests, since they might be waiting on the fc->bg_queue before being queued on fc->pending. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 24f1d77b87a4..9e0ed3e714cb 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -327,10 +327,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); } @@ -519,6 +515,10 @@ 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; + if (!req->waiting) { + req->waiting = 1; + atomic_inc(&fc->num_waiting); + } __fuse_request_send(fc, req); } EXPORT_SYMBOL_GPL(fuse_request_send); @@ -592,6 +592,10 @@ static void fuse_request_send_nowait_locked(struct fuse_conn *fc, struct fuse_req *req) { BUG_ON(!req->background); + if (!req->waiting) { + req->waiting = 1; + atomic_inc(&fc->num_waiting); + } fc->num_background++; if (fc->num_background == fc->max_background) fc->blocked = 1; -- cgit From de15522646b9822d82b613d84cfeb4482370db3d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:57 +0200 Subject: fuse: check conn_error earlier fc->conn_error is set once in FUSE_INIT reply and never cleared. Check it in request allocation, there's no sense in doing all the preparation if sending will surely fail. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 9e0ed3e714cb..155610fef4a7 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -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) { @@ -498,8 +502,6 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) 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); -- cgit From f0139aa819dfc8e80f664bd08800fc48233cb94e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:57 +0200 Subject: fuse: fold fuse_request_send_nowait() into single caller And the same with fuse_request_send_nowait_locked(). Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 155610fef4a7..de110de881f7 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -590,14 +590,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); if (!req->waiting) { req->waiting = 1; atomic_inc(&fc->num_waiting); } + req->isreply = 1; fc->num_background++; if (fc->num_background == fc->max_background) fc->blocked = 1; @@ -610,12 +616,12 @@ 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); @@ -624,12 +630,6 @@ static void fuse_request_send_nowait(struct fuse_conn *fc, struct fuse_req *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, @@ -649,18 +649,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); -- cgit From ccd0a0bd16ff01bb27bacbeb2e0e4983dc299502 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:57 +0200 Subject: fuse: call fuse_abort_conn() in dev release fuse_abort_conn() does all the work done by fuse_dev_release() and more. "More" consists of: end_io_requests(fc); wake_up_all(&fc->waitq); kill_fasync(&fc->fasync, SIGIO, POLL_IN); All of which should be no-op (WARN_ON's added). Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index de110de881f7..e5901bf8d600 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2199,14 +2199,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); } -- cgit From 0d8e84b0432beb6d11a1c82deeb9dc1a7bee02c0 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:58 +0200 Subject: fuse: simplify request abort - don't end the request while req->locked is true - make unlock_request() return an error if the connection was aborted Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 119 +++++++++++++++++++++++----------------------------------- 1 file changed, 46 insertions(+), 73 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index e5901bf8d600..3b979abb7b54 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -382,8 +382,8 @@ __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; @@ -439,8 +439,6 @@ __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; @@ -457,8 +455,6 @@ __acquires(fc->lock) wait_answer_interruptible(fc, req); restore_sigs(&oldset); - if (req->aborted) - goto aborted; if (req->state == FUSE_REQ_FINISHED) return; @@ -478,22 +474,6 @@ __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) @@ -690,19 +670,21 @@ static int lock_request(struct fuse_conn *fc, struct fuse_req *req) } /* - * 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_conn *fc, struct fuse_req *req) { + int err = 0; if (req) { spin_lock(&fc->lock); - req->locked = 0; if (req->aborted) - wake_up(&req->waitq); + err = -ENOENT; + else + req->locked = 0; spin_unlock(&fc->lock); } + return err; } struct fuse_copy_state { @@ -759,7 +741,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->fc, cs->req); + if (err) + return err; + fuse_copy_finish(cs); if (cs->pipebufs) { struct pipe_buffer *buf = cs->pipebufs; @@ -859,7 +844,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->fc, cs->req); + if (err) + return err; + fuse_copy_finish(cs); err = buf->ops->confirm(cs->pipe, buf); @@ -949,11 +937,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->fc, cs->req); + if (err) + return err; + fuse_copy_finish(cs); buf = cs->pipebufs; @@ -1318,7 +1310,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, fuse_copy_finish(cs); spin_lock(&fc->lock); req->locked = 0; - if (req->aborted) { + if (!fc->connected) { request_end(fc, req); return -ENODEV; } @@ -1910,13 +1902,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; @@ -1947,10 +1932,9 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, spin_lock(&fc->lock); req->locked = 0; - if (!err) { - if (req->aborted) - err = -ENOENT; - } else if (!req->aborted) + if (!fc->connected) + err = -ENOENT; + else if (err) req->out.h.error = -EIO; request_end(fc, req); @@ -2097,37 +2081,31 @@ __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); - } + req->aborted = 1; + if (!req->locked) + list_move(&req->list, &to_end); + } + 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); } } @@ -2169,13 +2147,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) { -- cgit From 825d6d3395e88a616e4c953984d77eeacbad4310 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:58 +0200 Subject: fuse: req use bitops Finer grained locking will mean there's no single lock to protect modification of bitfileds in fuse_req. So move to using bitops. Can use the non-atomic variants for those which happen while the request definitely has only one reference. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 71 ++++++++++++++++++++++++++++---------------------------- fs/fuse/file.c | 17 +++++++------- fs/fuse/fuse_i.h | 49 ++++++++++++++++++-------------------- fs/fuse/inode.c | 6 ++--- 4 files changed, 71 insertions(+), 72 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 3b979abb7b54..dcfef5475ada 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -181,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: @@ -272,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 @@ -291,9 +293,9 @@ 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); - req->waiting = 0; } if (req->stolen_file) @@ -385,9 +387,8 @@ __releases(fc->lock) 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; @@ -442,12 +443,12 @@ __acquires(fc->lock) 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 */ @@ -478,7 +479,7 @@ __acquires(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; @@ -496,9 +497,9 @@ 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; - if (!req->waiting) { - req->waiting = 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); @@ -578,12 +579,12 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args) void fuse_request_send_background_locked(struct fuse_conn *fc, struct fuse_req *req) { - BUG_ON(!req->background); - if (!req->waiting) { - req->waiting = 1; + 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); } - req->isreply = 1; + __set_bit(FR_ISREPLY, &req->flags); fc->num_background++; if (fc->num_background == fc->max_background) fc->blocked = 1; @@ -617,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) { @@ -644,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); @@ -660,10 +661,10 @@ static int lock_request(struct fuse_conn *fc, struct fuse_req *req) int err = 0; if (req) { spin_lock(&fc->lock); - if (req->aborted) + if (test_bit(FR_ABORTED, &req->flags)) err = -ENOENT; else - req->locked = 1; + set_bit(FR_LOCKED, &req->flags); spin_unlock(&fc->lock); } return err; @@ -678,10 +679,10 @@ static int unlock_request(struct fuse_conn *fc, struct fuse_req *req) int err = 0; if (req) { spin_lock(&fc->lock); - if (req->aborted) + if (test_bit(FR_ABORTED, &req->flags)) err = -ENOENT; else - req->locked = 0; + clear_bit(FR_LOCKED, &req->flags); spin_unlock(&fc->lock); } return err; @@ -902,7 +903,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) err = 0; spin_lock(&cs->fc->lock); - if (cs->req->aborted) + if (test_bit(FR_ABORTED, &cs->req->flags)) err = -ENOENT; else *pagep = newpage; @@ -1309,7 +1310,7 @@ 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; + clear_bit(FR_LOCKED, &req->flags); if (!fc->connected) { request_end(fc, req); return -ENODEV; @@ -1319,12 +1320,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); } @@ -1921,7 +1922,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; @@ -1931,7 +1932,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, fuse_copy_finish(cs); spin_lock(&fc->lock); - req->locked = 0; + clear_bit(FR_LOCKED, &req->flags); if (!fc->connected) err = -ENOENT; else if (err) @@ -2097,8 +2098,8 @@ __acquires(fc->lock) list_for_each_entry_safe(req, next, &fc->io, list) { req->out.h.error = -ECONNABORTED; - req->aborted = 1; - if (!req->locked) + set_bit(FR_ABORTED, &req->flags); + if (!test_bit(FR_LOCKED, &req->flags)) list_move(&req->list, &to_end); } while (!list_empty(&to_end)) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 5ef05b5c4cff..bf272263c1a2 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -96,17 +96,17 @@ static void fuse_file_put(struct fuse_file *ff, bool sync) * Drop the release request when client does not * implement 'open' */ - req->background = 0; + __clear_bit(FR_BACKGROUND, &req->flags); iput(req->misc.release.inode); fuse_put_request(ff->fc, req); } else if (sync) { - req->background = 0; + __clear_bit(FR_BACKGROUND, &req->flags); fuse_request_send(ff->fc, req); iput(req->misc.release.inode); fuse_put_request(ff->fc, req); } else { req->end = fuse_release_end; - req->background = 1; + __set_bit(FR_BACKGROUND, &req->flags); fuse_request_send_background(ff->fc, req); } kfree(ff); @@ -299,8 +299,8 @@ void fuse_sync_release(struct fuse_file *ff, int flags) { WARN_ON(atomic_read(&ff->count) > 1); fuse_prepare_release(ff, flags, FUSE_RELEASE); - ff->reserved_req->force = 1; - ff->reserved_req->background = 0; + __set_bit(FR_FORCE, &ff->reserved_req->flags); + __clear_bit(FR_BACKGROUND, &ff->reserved_req->flags); fuse_request_send(ff->fc, ff->reserved_req); fuse_put_request(ff->fc, ff->reserved_req); kfree(ff); @@ -426,7 +426,7 @@ static int fuse_flush(struct file *file, fl_owner_t id) req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; - req->force = 1; + __set_bit(FR_FORCE, &req->flags); fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); @@ -1611,7 +1611,8 @@ static int fuse_writepage_locked(struct page *page) if (!req) goto err; - req->background = 1; /* writeback always goes to bg_queue */ + /* writeback always goes to bg_queue */ + __set_bit(FR_BACKGROUND, &req->flags); tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); if (!tmp_page) goto err_free; @@ -1830,7 +1831,7 @@ static int fuse_writepages_fill(struct page *page, req->misc.write.in.write_flags |= FUSE_WRITE_CACHE; req->misc.write.next = NULL; req->in.argpages = 1; - req->background = 1; + __set_bit(FR_BACKGROUND, &req->flags); req->num_pages = 0; req->end = fuse_writepage_end; req->inode = inode; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 7354dc142a50..4503e995c7b2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -266,6 +266,27 @@ struct fuse_io_priv { struct completion *done; }; +/** + * Request flags + * + * FR_ISREPLY: set if the request has reply + * FR_FORCE: force sending of the request even if interrupted + * FR_BACKGROUND: request is sent in the background + * FR_WAITING: request is counted as "waiting" + * FR_ABORTED: the request was aborted + * FR_INTERRUPTED: the request has been interrupted + * FR_LOCKED: data is being copied to/from the request + */ +enum fuse_req_flag { + FR_ISREPLY, + FR_FORCE, + FR_BACKGROUND, + FR_WAITING, + FR_ABORTED, + FR_INTERRUPTED, + FR_LOCKED, +}; + /** * A request to the client */ @@ -283,32 +304,8 @@ struct fuse_req { /** Unique ID for the interrupt request */ u64 intr_unique; - /* - * The following bitfields are either set once before the - * request is queued or setting/clearing them is protected by - * fuse_conn->lock - */ - - /** True if the request has reply */ - unsigned isreply:1; - - /** Force sending of the request even if interrupted */ - unsigned force:1; - - /** The request was aborted */ - unsigned aborted:1; - - /** Request is sent in the background */ - unsigned background:1; - - /** The request has been interrupted */ - unsigned interrupted:1; - - /** Data is being copied to/from the request */ - unsigned locked:1; - - /** Request is counted as "waiting" */ - unsigned waiting:1; + /* Request flags, updated with test/set/clear_bit() */ + unsigned long flags; /** State of the request */ enum fuse_req_state state; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index cec8abbe2c8c..f7c9b7225ec5 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -362,8 +362,8 @@ static void fuse_send_destroy(struct fuse_conn *fc) if (req && fc->conn_init) { fc->destroy_req = NULL; req->in.h.opcode = FUSE_DESTROY; - req->force = 1; - req->background = 0; + __set_bit(FR_FORCE, &req->flags); + __clear_bit(FR_BACKGROUND, &req->flags); fuse_request_send(fc, req); fuse_put_request(fc, req); } @@ -1060,7 +1060,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) init_req = fuse_request_alloc(0); if (!init_req) goto err_put_root; - init_req->background = 1; + __set_bit(FR_BACKGROUND, &init_req->flags); if (is_bdev) { fc->destroy_req = fuse_request_alloc(0); -- cgit From dc00809a53edd15369906b90407a2d5b976289f5 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:58 +0200 Subject: fuse: use per req lock for lock/unlock_request() Reuse req->waitq.lock for protecting FR_ABORTED and FR_LOCKED flags. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 42 ++++++++++++++++++++---------------------- fs/fuse/fuse_i.h | 4 ++++ 2 files changed, 24 insertions(+), 22 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index dcfef5475ada..92c7691df429 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -656,16 +656,16 @@ 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); + spin_lock(&req->waitq.lock); if (test_bit(FR_ABORTED, &req->flags)) err = -ENOENT; else set_bit(FR_LOCKED, &req->flags); - spin_unlock(&fc->lock); + spin_unlock(&req->waitq.lock); } return err; } @@ -674,22 +674,21 @@ static int lock_request(struct fuse_conn *fc, struct fuse_req *req) * Unlock request. If it was aborted while locked, caller is responsible * for unlocking and ending the request. */ -static int 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); + spin_lock(&req->waitq.lock); if (test_bit(FR_ABORTED, &req->flags)) err = -ENOENT; else clear_bit(FR_LOCKED, &req->flags); - spin_unlock(&fc->lock); + 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; @@ -703,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; } @@ -742,7 +738,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) struct page *page; int err; - err = unlock_request(cs->fc, cs->req); + err = unlock_request(cs->req); if (err) return err; @@ -794,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 */ @@ -845,7 +841,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) struct page *newpage; struct pipe_buffer *buf = cs->pipebufs; - err = unlock_request(cs->fc, cs->req); + err = unlock_request(cs->req); if (err) return err; @@ -902,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); + 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); @@ -927,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; @@ -943,7 +939,7 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page, if (cs->nr_segs == cs->pipe->buffers) return -EIO; - err = unlock_request(cs->fc, cs->req); + err = unlock_request(cs->req); if (err) return err; @@ -1358,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)); } @@ -1380,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); @@ -1958,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)); } @@ -2023,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,9 +2094,11 @@ __acquires(fc->lock) 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); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4503e995c7b2..7257adba7ecd 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -289,6 +289,10 @@ enum fuse_req_flag { /** * A request to the client + * + * .waitq.lock protects the following fields: + * - FR_ABORTED + * - FR_LOCKED (may also be modified under fc->lock, tested under both) */ struct fuse_req { /** This can be on either pending processing or io lists in -- cgit From b716d425385ed392adc8e619020c1d77ae5ec1cb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:59 +0200 Subject: fuse: fold helpers into abort Fold end_io_requests() and end_queued_requests() into fuse_abort_conn(). Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 93 ++++++++++++++++++++++++----------------------------------- 1 file changed, 38 insertions(+), 55 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 92c7691df429..fc3268b65bf8 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2075,51 +2075,6 @@ __acquires(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,26 +2093,54 @@ 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. * - * 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) { spin_lock(&fc->lock); if (fc->connected) { + struct fuse_req *req, *next; + LIST_HEAD(to_end); + 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_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); + } + 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)); end_polls(fc); wake_up_all(&fc->waitq); wake_up_all(&fc->blocked_waitq); -- cgit From 41f982747e8175a4eb5e8d1939bdbb10f435b7f6 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:25:59 +0200 Subject: fuse: rework abort Splice fc->pending and fc->processing lists into a common kill list while holding fc->lock. By the time we release fc->lock, pending and processing lists are empty and the io list contains only locked requests. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index fc3268b65bf8..6cb0b0bc9029 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2101,9 +2101,6 @@ static void end_polls(struct fuse_conn *fc) * 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 @@ -2116,7 +2113,8 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_lock(&fc->lock); if (fc->connected) { struct fuse_req *req, *next; - LIST_HEAD(to_end); + LIST_HEAD(to_end1); + LIST_HEAD(to_end2); fc->connected = 0; fc->blocked = 0; @@ -2126,19 +2124,20 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_lock(&req->waitq.lock); set_bit(FR_ABORTED, &req->flags); if (!test_bit(FR_LOCKED, &req->flags)) - list_move(&req->list, &to_end); + list_move(&req->list, &to_end1); spin_unlock(&req->waitq.lock); } - while (!list_empty(&to_end)) { - req = list_first_entry(&to_end, struct fuse_req, list); + fc->max_background = UINT_MAX; + flush_bg_queue(fc); + list_splice_init(&fc->pending, &to_end2); + 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); } - fc->max_background = UINT_MAX; - flush_bg_queue(fc); - end_requests(fc, &fc->pending); - end_requests(fc, &fc->processing); + end_requests(fc, &to_end2); while (forget_pending(fc)) kfree(dequeue_forget(fc, 1, NULL)); end_polls(fc); -- cgit From 7d2e0a099c7685a7355c27a2c3dc76ea7cfc8283 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:00 +0200 Subject: fuse: simplify unique ctr Since it's a 64bit counter, it's never gonna wrap around. Remove code dealing with that possibility. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 7 +------ fs/fuse/inode.c | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 6cb0b0bc9029..2014cee76036 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -319,12 +319,7 @@ static unsigned len_args(unsigned numargs, struct fuse_arg *args) static u64 fuse_get_unique(struct fuse_conn *fc) { - fc->reqctr++; - /* zero is special */ - if (fc->reqctr == 0) - fc->reqctr = 1; - - return fc->reqctr; + return ++fc->reqctr; } static void queue_request(struct fuse_conn *fc, struct fuse_req *req) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index f7c9b7225ec5..2f902b8edcf2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -588,7 +588,6 @@ void fuse_conn_init(struct fuse_conn *fc) fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD; fc->khctr = 0; fc->polled_files = RB_ROOT; - fc->reqctr = 0; fc->blocked = 0; fc->initialized = 0; fc->attr_version = 1; -- cgit From c47752673acb130e5132db0e52363e15be260ca4 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:00 +0200 Subject: fuse: don't hold lock over request_wait_answer() Only hold fc->lock over sections of request_wait_answer() that actually need it. If wait_event_interruptible() returns zero, it means that the request finished. Need to add memory barriers, though, to make sure that all relevant data in the request is synchronized. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 2014cee76036..638aafde6e22 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -381,6 +381,7 @@ __releases(fc->lock) req->end = NULL; list_del_init(&req->list); list_del_init(&req->intr_entry); + smp_wmb(); req->state = FUSE_REQ_FINISHED; if (test_bit(FR_BACKGROUND, &req->flags)) { clear_bit(FR_BACKGROUND, &req->flags); @@ -407,19 +408,6 @@ __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) { list_add_tail(&req->intr_entry, &fc->interrupts); @@ -428,19 +416,21 @@ static void queue_interrupt(struct fuse_conn *fc, struct fuse_req *req) } static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) -__releases(fc->lock) -__acquires(fc->lock) { + 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, + req->state == FUSE_REQ_FINISHED); + if (!err) return; + spin_lock(&fc->lock); set_bit(FR_INTERRUPTED, &req->flags); if (req->state == FUSE_REQ_SENT) queue_interrupt(fc, req); + spin_unlock(&fc->lock); } if (!test_bit(FR_FORCE, &req->flags)) { @@ -448,46 +438,51 @@ __acquires(fc->lock) /* Only fatal signals may interrupt this */ block_sigs(&oldset); - wait_answer_interruptible(fc, req); + err = wait_event_interruptible(req->waitq, + req->state == FUSE_REQ_FINISHED); restore_sigs(&oldset); - if (req->state == FUSE_REQ_FINISHED) + if (!err) return; + spin_lock(&fc->lock); /* Request is not yet in userspace, bail out */ if (req->state == FUSE_REQ_PENDING) { list_del(&req->list); + spin_unlock(&fc->lock); __fuse_put_request(req); req->out.h.error = -EINTR; return; } + 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); } static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) { BUG_ON(test_bit(FR_BACKGROUND, &req->flags)); spin_lock(&fc->lock); - if (!fc->connected) + if (!fc->connected) { + spin_unlock(&fc->lock); req->out.h.error = -ENOTCONN; - else { + } else { req->in.h.unique = fuse_get_unique(fc); queue_request(fc, req); /* acquire extra reference, since request is still needed after request_end() */ __fuse_get_request(req); + 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) -- cgit From 7a3b2c754749c73b4a255b2a1070c24dba589098 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:00 +0200 Subject: fuse: simplify req states FUSE_REQ_INIT is actually the same state as FUSE_REQ_PENDING and FUSE_REQ_READING and FUSE_REQ_WRITING can be merged into a common FUSE_REQ_IO state. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 5 ++--- fs/fuse/file.c | 3 +-- fs/fuse/fuse_i.h | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 638aafde6e22..e5c541b7c7af 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -327,7 +327,6 @@ static void queue_request(struct fuse_conn *fc, 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); } @@ -1274,7 +1273,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, } req = list_entry(fc->pending.next, struct fuse_req, list); - req->state = FUSE_REQ_READING; + req->state = FUSE_REQ_IO; list_move(&req->list, &fc->io); in = &req->in; @@ -1905,7 +1904,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, return nbytes; } - req->state = FUSE_REQ_WRITING; + req->state = FUSE_REQ_IO; list_move(&req->list, &fc->io); req->out.h = oh; set_bit(FR_LOCKED, &req->flags); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index bf272263c1a2..d0c23d075427 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1743,8 +1743,7 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, } } - if (old_req->num_pages == 1 && (old_req->state == FUSE_REQ_INIT || - old_req->state == FUSE_REQ_PENDING)) { + if (old_req->num_pages == 1 && old_req->state == FUSE_REQ_PENDING) { struct backing_dev_info *bdi = inode_to_bdi(page->mapping->host); copy_highpage(old_req->pages[0], page); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 7257adba7ecd..3fd65f613515 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -243,11 +243,9 @@ struct fuse_args { /** The request state */ enum fuse_req_state { - FUSE_REQ_INIT = 0, - FUSE_REQ_PENDING, - FUSE_REQ_READING, + FUSE_REQ_PENDING = 0, + FUSE_REQ_IO, FUSE_REQ_SENT, - FUSE_REQ_WRITING, FUSE_REQ_FINISHED }; -- cgit From 33e14b4dfdc477344efbcd9b4218f2b350f0f893 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:01 +0200 Subject: fuse: req state use flags Use flags for representing the state in fuse_req. This is needed since req->list will be protected by different locks in different states, hence we'll want the state itself to be split into distinct bits, each protected with the relevant lock in that state. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 23 ++++++++++++++--------- fs/fuse/file.c | 2 +- fs/fuse/fuse_i.h | 17 ++++++----------- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index e5c541b7c7af..f1e2efd6b186 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -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) @@ -380,8 +381,10 @@ __releases(fc->lock) req->end = NULL; list_del_init(&req->list); list_del_init(&req->intr_entry); + WARN_ON(test_bit(FR_PENDING, &req->flags)); + WARN_ON(test_bit(FR_SENT, &req->flags)); smp_wmb(); - req->state = FUSE_REQ_FINISHED; + 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) @@ -421,13 +424,13 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) if (!fc->no_interrupt) { /* Any signal may interrupt this */ err = wait_event_interruptible(req->waitq, - req->state == FUSE_REQ_FINISHED); + 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) + if (test_bit(FR_SENT, &req->flags)) queue_interrupt(fc, req); spin_unlock(&fc->lock); } @@ -438,7 +441,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) /* Only fatal signals may interrupt this */ block_sigs(&oldset); err = wait_event_interruptible(req->waitq, - req->state == FUSE_REQ_FINISHED); + test_bit(FR_FINISHED, &req->flags)); restore_sigs(&oldset); if (!err) @@ -446,7 +449,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) spin_lock(&fc->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(&fc->lock); __fuse_put_request(req); @@ -460,7 +463,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) * Either request is already in userspace, or it was forced. * Wait it out. */ - wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); + wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags)); } static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) @@ -1273,7 +1276,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, } req = list_entry(fc->pending.next, struct fuse_req, list); - req->state = FUSE_REQ_IO; + clear_bit(FR_PENDING, &req->flags); list_move(&req->list, &fc->io); in = &req->in; @@ -1308,7 +1311,7 @@ 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; + set_bit(FR_SENT, &req->flags); list_move_tail(&req->list, &fc->processing); if (test_bit(FR_INTERRUPTED, &req->flags)) queue_interrupt(fc, req); @@ -1904,7 +1907,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, return nbytes; } - req->state = FUSE_REQ_IO; + clear_bit(FR_SENT, &req->flags); list_move(&req->list, &fc->io); req->out.h = oh; set_bit(FR_LOCKED, &req->flags); @@ -2059,6 +2062,8 @@ __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); } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index d0c23d075427..64835cf58936 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1743,7 +1743,7 @@ static bool fuse_writepage_in_flight(struct fuse_req *new_req, } } - if (old_req->num_pages == 1 && old_req->state == FUSE_REQ_PENDING) { + if (old_req->num_pages == 1 && test_bit(FR_PENDING, &old_req->flags)) { struct backing_dev_info *bdi = inode_to_bdi(page->mapping->host); copy_highpage(old_req->pages[0], page); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 3fd65f613515..8eaf3b0de033 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -241,14 +241,6 @@ struct fuse_args { #define FUSE_ARGS(args) struct fuse_args args = {} -/** The request state */ -enum fuse_req_state { - FUSE_REQ_PENDING = 0, - FUSE_REQ_IO, - FUSE_REQ_SENT, - FUSE_REQ_FINISHED -}; - /** The request IO state (for asynchronous processing) */ struct fuse_io_priv { int async; @@ -274,6 +266,9 @@ struct fuse_io_priv { * FR_ABORTED: the request was aborted * FR_INTERRUPTED: the request has been interrupted * FR_LOCKED: data is being copied to/from the request + * FR_PENDING: request is not yet in userspace + * FR_SENT: request is in userspace, waiting for an answer + * FR_FINISHED: request is finished */ enum fuse_req_flag { FR_ISREPLY, @@ -283,6 +278,9 @@ enum fuse_req_flag { FR_ABORTED, FR_INTERRUPTED, FR_LOCKED, + FR_PENDING, + FR_SENT, + FR_FINISHED, }; /** @@ -309,9 +307,6 @@ struct fuse_req { /* Request flags, updated with test/set/clear_bit() */ unsigned long flags; - /** State of the request */ - enum fuse_req_state state; - /** The request input */ struct fuse_in in; -- cgit From f88996a93324483ff3ec027312bbacacf97a555b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:01 +0200 Subject: fuse: separate out input queue The input queue contains normal requests (fc->pending), forgets (fc->forget_*) and interrupts (fc->interrupts). There's also fc->waitq and fc->fasync for waking up the readers of the fuse device when a request is available. The fc->reqctr is also moved to the input queue (assigned to the request when the request is added to the input queue. This patch just rearranges the fields, no functional change. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 134 +++++++++++++++++++++++++++++++------------------------ fs/fuse/fuse_i.h | 47 ++++++++++--------- fs/fuse/inode.c | 14 ++++-- 3 files changed, 111 insertions(+), 84 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index f1e2efd6b186..24407e21fb82 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -318,32 +318,34 @@ 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) { - 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); - wake_up(&fc->waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); + list_add_tail(&req->list, &fiq->pending); + wake_up(&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); + fiq->forget_list_tail->next = forget; + fiq->forget_list_tail = forget; + wake_up(&fiq->waitq); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } else { kfree(forget); } @@ -355,12 +357,13 @@ 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); + req->in.h.unique = fuse_get_unique(fiq); + queue_request(fiq, req); } } @@ -410,11 +413,11 @@ __releases(fc->lock) fuse_put_request(fc, req); } -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); + list_add_tail(&req->intr_entry, &fiq->interrupts); + wake_up(&fiq->waitq); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) @@ -431,7 +434,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) spin_lock(&fc->lock); set_bit(FR_INTERRUPTED, &req->flags); if (test_bit(FR_SENT, &req->flags)) - queue_interrupt(fc, req); + queue_interrupt(&fc->iq, req); spin_unlock(&fc->lock); } @@ -474,8 +477,10 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) spin_unlock(&fc->lock); req->out.h.error = -ENOTCONN; } else { - req->in.h.unique = fuse_get_unique(fc); - queue_request(fc, req); + struct fuse_iqueue *fiq = &fc->iq; + + 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); @@ -609,12 +614,13 @@ 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); + queue_request(fiq, req); err = 0; } spin_unlock(&fc->lock); @@ -1045,15 +1051,15 @@ 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 */ @@ -1061,10 +1067,11 @@ static void request_wait(struct fuse_conn *fc) __releases(fc->lock) __acquires(fc->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 (fc->connected && !request_pending(fiq)) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) break; @@ -1074,7 +1081,7 @@ __acquires(fc->lock) spin_lock(&fc->lock); } set_current_state(TASK_RUNNING); - remove_wait_queue(&fc->waitq, &wait); + remove_wait_queue(&fiq->waitq, &wait); } /* @@ -1095,7 +1102,7 @@ __releases(fc->lock) int err; list_del_init(&req->intr_entry); - req->intr_unique = fuse_get_unique(fc); + req->intr_unique = fuse_get_unique(&fc->iq); memset(&ih, 0, sizeof(ih)); memset(&arg, 0, sizeof(arg)); ih.len = reqsize; @@ -1115,21 +1122,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; @@ -1143,14 +1150,15 @@ static int fuse_read_single_forget(struct fuse_conn *fc, __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), }; @@ -1178,10 +1186,11 @@ __releases(fc->lock) 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), }; @@ -1191,7 +1200,7 @@ __releases(fc->lock) } 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(&fc->lock); arg.count = count; @@ -1223,7 +1232,9 @@ static int fuse_read_forget(struct fuse_conn *fc, struct fuse_copy_state *cs, size_t nbytes) __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); @@ -1242,6 +1253,7 @@ 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; @@ -1250,7 +1262,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, spin_lock(&fc->lock); err = -EAGAIN; if ((file->f_flags & O_NONBLOCK) && fc->connected && - !request_pending(fc)) + !request_pending(fiq)) goto err_unlock; request_wait(fc); @@ -1258,24 +1270,24 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, if (!fc->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 = list_entry(fiq->pending.next, struct fuse_req, list); clear_bit(FR_PENDING, &req->flags); list_move(&req->list, &fc->io); @@ -1314,7 +1326,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, set_bit(FR_SENT, &req->flags); list_move_tail(&req->list, &fc->processing); if (test_bit(FR_INTERRUPTED, &req->flags)) - queue_interrupt(fc, req); + queue_interrupt(fiq, req); spin_unlock(&fc->lock); } return reqsize; @@ -1900,7 +1912,7 @@ 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); @@ -2033,16 +2045,18 @@ 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) mask = POLLERR; - else if (request_pending(fc)) + else if (request_pending(fiq)) mask |= POLLIN | POLLRDNORM; spin_unlock(&fc->lock); @@ -2104,6 +2118,8 @@ static void end_polls(struct fuse_conn *fc) */ 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; @@ -2123,7 +2139,7 @@ void fuse_abort_conn(struct fuse_conn *fc) } fc->max_background = UINT_MAX; flush_bg_queue(fc); - list_splice_init(&fc->pending, &to_end2); + list_splice_init(&fiq->pending, &to_end2); list_splice_init(&fc->processing, &to_end2); while (!list_empty(&to_end1)) { req = list_first_entry(&to_end1, struct fuse_req, list); @@ -2132,12 +2148,12 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_lock(&fc->lock); } end_requests(fc, &to_end2); - while (forget_pending(fc)) - kfree(dequeue_forget(fc, 1, NULL)); + while (forget_pending(fiq)) + kfree(dequeue_forget(fiq, 1, NULL)); end_polls(fc); - wake_up_all(&fc->waitq); + wake_up_all(&fiq->waitq); wake_up_all(&fc->blocked_waitq); - kill_fasync(&fc->fasync, SIGIO, POLL_IN); + kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } spin_unlock(&fc->lock); } @@ -2148,7 +2164,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); } @@ -2164,7 +2180,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 = { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8eaf3b0de033..3d075417042a 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -374,6 +374,30 @@ struct fuse_req { struct file *stolen_file; }; +struct fuse_iqueue { + /** Readers of the connection are waiting on this */ + wait_queue_head_t waitq; + + /** The next unique request id */ + u64 reqctr; + + /** The list of pending requests */ + struct list_head pending; + + /** Pending interrupts */ + struct list_head interrupts; + + /** Queue of pending forgets */ + struct fuse_forget_link forget_list_head; + struct fuse_forget_link *forget_list_tail; + + /** Batching of FORGET requests (positive indicates FORGET batch) */ + int forget_batch; + + /** O_ASYNC requests */ + struct fasync_struct *fasync; +}; + /** * A Fuse connection. * @@ -405,11 +429,8 @@ struct fuse_conn { /** Maximum write size */ unsigned max_write; - /** Readers of the connection are waiting on this */ - wait_queue_head_t waitq; - - /** The list of pending requests */ - struct list_head pending; + /** Input queue */ + struct fuse_iqueue iq; /** The list of requests being processed */ struct list_head processing; @@ -438,16 +459,6 @@ struct fuse_conn { /** The list of background requests set aside for later queuing */ struct list_head bg_queue; - /** Pending interrupts */ - struct list_head interrupts; - - /** Queue of pending forgets */ - struct fuse_forget_link forget_list_head; - struct fuse_forget_link *forget_list_tail; - - /** Batching of FORGET requests (positive indicates FORGET batch) */ - int forget_batch; - /** Flag indicating that INIT reply has been received. Allocating * any fuse request will be suspended until the flag is set */ int initialized; @@ -463,9 +474,6 @@ struct fuse_conn { /** waitq for reserved requests */ wait_queue_head_t reserved_req_waitq; - /** The next unique request id */ - u64 reqctr; - /** Connection established, cleared on umount, connection abort and device release */ unsigned connected; @@ -588,9 +596,6 @@ struct fuse_conn { /** number of dentries used in the above array */ int ctl_ndents; - /** O_ASYNC requests */ - struct fasync_struct *fasync; - /** Key for lock owner ID scrambling */ u32 scramble_key[4]; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 2f902b8edcf2..0890428bbad0 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -567,22 +567,28 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root) return 0; } +static void fuse_iqueue_init(struct fuse_iqueue *fiq) +{ + memset(fiq, 0, sizeof(struct fuse_iqueue)); + init_waitqueue_head(&fiq->waitq); + INIT_LIST_HEAD(&fiq->pending); + INIT_LIST_HEAD(&fiq->interrupts); + fiq->forget_list_tail = &fiq->forget_list_head; +} + void fuse_conn_init(struct fuse_conn *fc) { memset(fc, 0, sizeof(*fc)); spin_lock_init(&fc->lock); init_rwsem(&fc->killsb); atomic_set(&fc->count, 1); - init_waitqueue_head(&fc->waitq); init_waitqueue_head(&fc->blocked_waitq); init_waitqueue_head(&fc->reserved_req_waitq); - INIT_LIST_HEAD(&fc->pending); + fuse_iqueue_init(&fc->iq); INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->io); - INIT_LIST_HEAD(&fc->interrupts); INIT_LIST_HEAD(&fc->bg_queue); INIT_LIST_HEAD(&fc->entry); - fc->forget_list_tail = &fc->forget_list_head; atomic_set(&fc->num_waiting, 0); fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND; fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD; -- cgit From e16714d8756dc1237a66994e139b61feebcf707a Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:01 +0200 Subject: fuse: duplicate ->connected in iqueue This will allow checking ->connected just with the input queue lock. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/cuse.c | 1 - fs/fuse/dev.c | 19 ++++++++++--------- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 3 ++- 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index e5bbf748b698..0993d9a266d3 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -502,7 +502,6 @@ static int cuse_channel_open(struct inode *inode, struct file *file) INIT_LIST_HEAD(&cc->list); cc->fc.release = cuse_fc_release; - cc->fc.connected = 1; cc->fc.initialized = 1; rc = cuse_send_init(cc); if (rc) { diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 24407e21fb82..a24ead993650 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -341,7 +341,7 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, forget->forget_one.nlookup = nlookup; spin_lock(&fc->lock); - if (fc->connected) { + if (fiq->connected) { fiq->forget_list_tail->next = forget; fiq->forget_list_tail = forget; wake_up(&fiq->waitq); @@ -471,14 +471,14 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) 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) { + if (!fiq->connected) { spin_unlock(&fc->lock); req->out.h.error = -ENOTCONN; } else { - struct fuse_iqueue *fiq = &fc->iq; - req->in.h.unique = fuse_get_unique(fiq); queue_request(fiq, req); /* acquire extra reference, since request is still needed @@ -619,7 +619,7 @@ static int fuse_request_send_notify_reply(struct fuse_conn *fc, __clear_bit(FR_ISREPLY, &req->flags); req->in.h.unique = unique; spin_lock(&fc->lock); - if (fc->connected) { + if (fiq->connected) { queue_request(fiq, req); err = 0; } @@ -1071,7 +1071,7 @@ __acquires(fc->lock) DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&fiq->waitq, &wait); - while (fc->connected && !request_pending(fiq)) { + while (fiq->connected && !request_pending(fiq)) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) break; @@ -1261,13 +1261,13 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, restart: spin_lock(&fc->lock); err = -EAGAIN; - if ((file->f_flags & O_NONBLOCK) && fc->connected && + 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(fiq)) @@ -2054,7 +2054,7 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) poll_wait(file, &fiq->waitq, wait); spin_lock(&fc->lock); - if (!fc->connected) + if (!fiq->connected) mask = POLLERR; else if (request_pending(fiq)) mask |= POLLIN | POLLRDNORM; @@ -2127,6 +2127,7 @@ void fuse_abort_conn(struct fuse_conn *fc) LIST_HEAD(to_end2); fc->connected = 0; + fiq->connected = 0; fc->blocked = 0; fuse_set_initialized(fc); list_for_each_entry_safe(req, next, &fc->io, list) { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 3d075417042a..e8be4611fb8e 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -375,6 +375,9 @@ struct fuse_req { }; struct fuse_iqueue { + /** Connection established */ + unsigned connected; + /** 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 0890428bbad0..caf77d5a6e8d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -574,6 +574,7 @@ static void fuse_iqueue_init(struct fuse_iqueue *fiq) INIT_LIST_HEAD(&fiq->pending); INIT_LIST_HEAD(&fiq->interrupts); fiq->forget_list_tail = &fiq->forget_list_head; + fiq->connected = 1; } void fuse_conn_init(struct fuse_conn *fc) @@ -596,6 +597,7 @@ void fuse_conn_init(struct fuse_conn *fc) fc->polled_files = RB_ROOT; fc->blocked = 0; fc->initialized = 0; + fc->connected = 1; fc->attr_version = 1; get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); } @@ -1084,7 +1086,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) list_add_tail(&fc->entry, &fuse_conn_list); sb->s_root = root_dentry; - fc->connected = 1; file->private_data = fuse_conn_get(fc); mutex_unlock(&fuse_mutex); /* -- cgit From 8c91189a2a8f5e69457bea9f48350c48310cec5b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:02 +0200 Subject: fuse: abort: group iqueue accesses Rearrange fuse_abort_conn() so that input queue accesses are grouped together. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index a24ead993650..deafbdf278c6 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2127,7 +2127,6 @@ void fuse_abort_conn(struct fuse_conn *fc) LIST_HEAD(to_end2); fc->connected = 0; - fiq->connected = 0; fc->blocked = 0; fuse_set_initialized(fc); list_for_each_entry_safe(req, next, &fc->io, list) { @@ -2140,7 +2139,14 @@ void fuse_abort_conn(struct fuse_conn *fc) } fc->max_background = UINT_MAX; flush_bg_queue(fc); + + fiq->connected = 0; list_splice_init(&fiq->pending, &to_end2); + while (forget_pending(fiq)) + kfree(dequeue_forget(fiq, 1, NULL)); + wake_up_all(&fiq->waitq); + 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); @@ -2149,12 +2155,8 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_lock(&fc->lock); } end_requests(fc, &to_end2); - while (forget_pending(fiq)) - kfree(dequeue_forget(fiq, 1, NULL)); end_polls(fc); - wake_up_all(&fiq->waitq); wake_up_all(&fc->blocked_waitq); - kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } spin_unlock(&fc->lock); } -- cgit From ef759258869c63e8df9b886ebaf9451c4bbe6cea Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:02 +0200 Subject: fuse: dev read: split list_move Different lists will need different locks. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index deafbdf278c6..a450940df45f 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1289,7 +1289,8 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, req = list_entry(fiq->pending.next, struct fuse_req, list); clear_bit(FR_PENDING, &req->flags); - list_move(&req->list, &fc->io); + list_del_init(&req->list); + list_add(&req->list, &fc->io); in = &req->in; reqsize = in->h.len; -- cgit From 4ce6081260ea4c1b5bfa8ecca5cbb93eea279ad4 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:02 +0200 Subject: fuse: iqueue locking Use fiq->waitq.lock for protecting members of struct fuse_iqueue and FR_PENDING request flag, previously protected by fc->lock. Following patches will remove fc->lock protection from these members. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 51 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index a450940df45f..65ad9b1e055d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -328,7 +328,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(&fiq->waitq); + wake_up_locked(&fiq->waitq); kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } @@ -341,14 +341,16 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, forget->forget_one.nlookup = nlookup; spin_lock(&fc->lock); + spin_lock(&fiq->waitq.lock); if (fiq->connected) { fiq->forget_list_tail->next = forget; fiq->forget_list_tail = forget; - wake_up(&fiq->waitq); + wake_up_locked(&fiq->waitq); kill_fasync(&fiq->fasync, SIGIO, POLL_IN); } else { kfree(forget); } + spin_unlock(&fiq->waitq.lock); spin_unlock(&fc->lock); } @@ -362,8 +364,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); req->in.h.unique = fuse_get_unique(fiq); queue_request(fiq, req); + spin_unlock(&fiq->waitq.lock); } } @@ -380,10 +384,13 @@ 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); + spin_unlock(&fiq->waitq.lock); WARN_ON(test_bit(FR_PENDING, &req->flags)); WARN_ON(test_bit(FR_SENT, &req->flags)); smp_wmb(); @@ -415,13 +422,16 @@ __releases(fc->lock) static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) { + spin_lock(&fiq->waitq.lock); list_add_tail(&req->intr_entry, &fiq->interrupts); - wake_up(&fiq->waitq); + 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) { + struct fuse_iqueue *fiq = &fc->iq; int err; if (!fc->no_interrupt) { @@ -434,7 +444,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) spin_lock(&fc->lock); set_bit(FR_INTERRUPTED, &req->flags); if (test_bit(FR_SENT, &req->flags)) - queue_interrupt(&fc->iq, req); + queue_interrupt(fiq, req); spin_unlock(&fc->lock); } @@ -451,14 +461,17 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) return; spin_lock(&fc->lock); + spin_lock(&fiq->waitq.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(&fc->lock); __fuse_put_request(req); req->out.h.error = -EINTR; return; } + spin_unlock(&fiq->waitq.lock); spin_unlock(&fc->lock); } @@ -475,8 +488,10 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) BUG_ON(test_bit(FR_BACKGROUND, &req->flags)); spin_lock(&fc->lock); + 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(fiq); @@ -484,6 +499,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(&fc->lock); request_wait_answer(fc, req); @@ -619,10 +635,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(&fc->lock); + 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; @@ -1064,8 +1082,10 @@ static int request_pending(struct fuse_iqueue *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); @@ -1076,9 +1096,11 @@ __acquires(fc->lock) 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(&fiq->waitq, &wait); @@ -1094,15 +1116,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->iq); + req->intr_unique = fuse_get_unique(fiq); memset(&ih, 0, sizeof(ih)); memset(&arg, 0, sizeof(arg)); ih.len = reqsize; @@ -1110,6 +1134,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; @@ -1147,6 +1172,7 @@ static struct fuse_forget_link *dequeue_forget(struct fuse_iqueue *fiq, 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; @@ -1162,6 +1188,7 @@ __releases(fc->lock) .len = sizeof(ih) + sizeof(arg), }; + spin_unlock(&fiq->waitq.lock); spin_unlock(&fc->lock); kfree(forget); if (nbytes < ih.len) @@ -1180,6 +1207,7 @@ __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; @@ -1195,12 +1223,14 @@ __releases(fc->lock) }; 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(fiq, max_forgets, &count); + spin_unlock(&fiq->waitq.lock); spin_unlock(&fc->lock); arg.count = count; @@ -1230,6 +1260,7 @@ __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) { struct fuse_iqueue *fiq = &fc->iq; @@ -1260,6 +1291,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, restart: spin_lock(&fc->lock); + spin_lock(&fiq->waitq.lock); err = -EAGAIN; if ((file->f_flags & O_NONBLOCK) && fiq->connected && !request_pending(fiq)) @@ -1290,6 +1322,8 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, 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); + list_add(&req->list, &fc->io); in = &req->in; @@ -1333,6 +1367,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, return reqsize; err_unlock: + spin_unlock(&fiq->waitq.lock); spin_unlock(&fc->lock); return err; } @@ -2055,10 +2090,12 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) poll_wait(file, &fiq->waitq, wait); spin_lock(&fc->lock); + spin_lock(&fiq->waitq.lock); if (!fiq->connected) mask = POLLERR; else if (request_pending(fiq)) mask |= POLLIN | POLLRDNORM; + spin_unlock(&fiq->waitq.lock); spin_unlock(&fc->lock); return mask; @@ -2141,11 +2178,13 @@ void fuse_abort_conn(struct fuse_conn *fc) 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(&fiq->waitq); + 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); -- cgit From 8f7bb368dbdda76f5e98e05ee49ae2dc138fd42f Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:03 +0200 Subject: fuse: allow interrupt queuing without fc->lock Interrupt is only queued after the request has been sent to userspace. This is either done in request_wait_answer() or fuse_dev_do_read() depending on which state the request is in at the time of the interrupt. If it's not yet sent, then queuing the interrupt is postponed until the request is read. Otherwise (the request has already been read and is waiting for an answer) the interrupt is queued immedidately. We want to call queue_interrupt() without fc->lock protection, in which case there can be a race between the two functions: - neither of them queue the interrupt (thinking the other one has already done it). - both of them queue the interrupt The first one is prevented by adding memory barriers, the second is prevented by checking (under fiq->waitq.lock) if the interrupt has already been queued. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 65ad9b1e055d..c7f1a633239f 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -423,8 +423,10 @@ __releases(fc->lock) static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) { spin_lock(&fiq->waitq.lock); - list_add_tail(&req->intr_entry, &fiq->interrupts); - wake_up_locked(&fiq->waitq); + 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); } @@ -443,6 +445,8 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) spin_lock(&fc->lock); set_bit(FR_INTERRUPTED, &req->flags); + /* 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); @@ -1358,8 +1362,10 @@ 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 { - set_bit(FR_SENT, &req->flags); 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(fiq, req); spin_unlock(&fc->lock); -- cgit From fd22d62ed0c36e260ac3e13167bc073f28407c70 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:03 +0200 Subject: fuse: no fc->lock for iqueue parts Remove fc->lock protection from input queue members, now protected by fiq->waitq.lock. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 71 +++++++++++++++++------------------------------------------ 1 file changed, 20 insertions(+), 51 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index c7f1a633239f..35453f229ef3 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -340,7 +340,6 @@ 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(&fc->lock); spin_lock(&fiq->waitq.lock); if (fiq->connected) { fiq->forget_list_tail->next = forget; @@ -351,7 +350,6 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, kfree(forget); } spin_unlock(&fiq->waitq.lock); - spin_unlock(&fc->lock); } static void flush_bg_queue(struct fuse_conn *fc) @@ -443,13 +441,11 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) if (!err) return; - spin_lock(&fc->lock); set_bit(FR_INTERRUPTED, &req->flags); /* 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)) { @@ -464,19 +460,16 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) if (!err) return; - spin_lock(&fc->lock); spin_lock(&fiq->waitq.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(&fc->lock); __fuse_put_request(req); req->out.h.error = -EINTR; return; } spin_unlock(&fiq->waitq.lock); - spin_unlock(&fc->lock); } /* @@ -491,10 +484,8 @@ 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); spin_lock(&fiq->waitq.lock); if (!fiq->connected) { - spin_unlock(&fc->lock); spin_unlock(&fiq->waitq.lock); req->out.h.error = -ENOTCONN; } else { @@ -504,7 +495,6 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) 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() */ @@ -638,14 +628,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(&fc->lock); 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; } @@ -1085,13 +1073,10 @@ static int request_pending(struct fuse_iqueue *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) +static void request_wait(struct fuse_iqueue *fiq) +__releases(fiq->waitq.lock) +__acquires(fiq->waitq.lock) { - struct fuse_iqueue *fiq = &fc->iq; DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&fiq->waitq, &wait); @@ -1101,9 +1086,7 @@ __acquires(fc->iq.waitq.lock) 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); @@ -1116,14 +1099,13 @@ __acquires(fc->iq.waitq.lock) * Unlike other requests this is assembled on demand, without a need * to allocate a separate fuse_req structure. * - * Called with fc->lock held, releases it + * Called with fiq->waitq.lock held, releases it */ -static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_copy_state *cs, +static int fuse_read_interrupt(struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, size_t nbytes, struct fuse_req *req) -__releases(fc->iq.waitq.lock) -__releases(fc->lock) +__releases(fiq->waitq.lock) { - struct fuse_iqueue *fiq = &fc->iq; struct fuse_in_header ih; struct fuse_interrupt_in arg; unsigned reqsize = sizeof(ih) + sizeof(arg); @@ -1139,7 +1121,6 @@ __releases(fc->lock) arg.unique = req->in.h.unique; spin_unlock(&fiq->waitq.lock); - spin_unlock(&fc->lock); if (nbytes < reqsize) return -EINVAL; @@ -1173,14 +1154,12 @@ static struct fuse_forget_link *dequeue_forget(struct fuse_iqueue *fiq, return head; } -static int fuse_read_single_forget(struct fuse_conn *fc, +static int fuse_read_single_forget(struct fuse_iqueue *fiq, struct fuse_copy_state *cs, size_t nbytes) -__releases(fc->iq.waitq.lock) -__releases(fc->lock) +__releases(fiq->waitq.lock) { int err; - 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, @@ -1193,7 +1172,6 @@ __releases(fc->lock) }; spin_unlock(&fiq->waitq.lock); - spin_unlock(&fc->lock); kfree(forget); if (nbytes < ih.len) return -EINVAL; @@ -1209,16 +1187,14 @@ __releases(fc->lock) return ih.len; } -static int fuse_read_batch_forget(struct fuse_conn *fc, +static int fuse_read_batch_forget(struct fuse_iqueue *fiq, struct fuse_copy_state *cs, size_t nbytes) -__releases(fc->iq.waitq.lock) -__releases(fc->lock) +__releases(fiq->waitq.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, @@ -1228,14 +1204,12 @@ __releases(fc->lock) 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(fiq, max_forgets, &count); spin_unlock(&fiq->waitq.lock); - spin_unlock(&fc->lock); arg.count = count; ih.len += count * sizeof(struct fuse_forget_one); @@ -1262,17 +1236,15 @@ __releases(fc->lock) return ih.len; } -static int fuse_read_forget(struct fuse_conn *fc, struct fuse_copy_state *cs, +static int fuse_read_forget(struct fuse_conn *fc, struct fuse_iqueue *fiq, + struct fuse_copy_state *cs, size_t nbytes) -__releases(fc->iq.waitq.lock) -__releases(fc->lock) +__releases(fiq->waitq.lock) { - 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); + return fuse_read_single_forget(fiq, cs, nbytes); else - return fuse_read_batch_forget(fc, cs, nbytes); + return fuse_read_batch_forget(fiq, cs, nbytes); } /* @@ -1294,14 +1266,13 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, unsigned reqsize; restart: - spin_lock(&fc->lock); spin_lock(&fiq->waitq.lock); err = -EAGAIN; if ((file->f_flags & O_NONBLOCK) && fiq->connected && !request_pending(fiq)) goto err_unlock; - request_wait(fc); + request_wait(fiq); err = -ENODEV; if (!fiq->connected) goto err_unlock; @@ -1312,12 +1283,12 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, if (!list_empty(&fiq->interrupts)) { req = list_entry(fiq->interrupts.next, struct fuse_req, intr_entry); - return fuse_read_interrupt(fc, cs, nbytes, req); + return fuse_read_interrupt(fiq, cs, nbytes, req); } if (forget_pending(fiq)) { if (list_empty(&fiq->pending) || fiq->forget_batch-- > 0) - return fuse_read_forget(fc, cs, nbytes); + return fuse_read_forget(fc, fiq, cs, nbytes); if (fiq->forget_batch <= -8) fiq->forget_batch = 16; @@ -1328,6 +1299,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, list_del_init(&req->list); spin_unlock(&fiq->waitq.lock); + spin_lock(&fc->lock); list_add(&req->list, &fc->io); in = &req->in; @@ -1374,7 +1346,6 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, err_unlock: spin_unlock(&fiq->waitq.lock); - spin_unlock(&fc->lock); return err; } @@ -2095,14 +2066,12 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) fiq = &fc->iq; poll_wait(file, &fiq->waitq, wait); - spin_lock(&fc->lock); spin_lock(&fiq->waitq.lock); if (!fiq->connected) mask = POLLERR; else if (request_pending(fiq)) mask |= POLLIN | POLLRDNORM; spin_unlock(&fiq->waitq.lock); - spin_unlock(&fc->lock); return mask; } -- cgit From 5250921bb0b25e7fc77ba732e569224410743a05 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:03 +0200 Subject: fuse: simplify request_wait() wait_event_interruptible_exclusive_locked() will do everything request_wait() does, so replace it. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 35453f229ef3..b6f901a0dbdd 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1072,27 +1072,6 @@ static int request_pending(struct fuse_iqueue *fiq) forget_pending(fiq); } -/* Wait until a request is available on the pending list */ -static void request_wait(struct fuse_iqueue *fiq) -__releases(fiq->waitq.lock) -__acquires(fiq->waitq.lock) -{ - DECLARE_WAITQUEUE(wait, current); - - 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); - schedule(); - spin_lock(&fiq->waitq.lock); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&fiq->waitq, &wait); -} - /* * Transfer an interrupt request to userspace * @@ -1272,13 +1251,14 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, !request_pending(fiq)) goto err_unlock; - request_wait(fiq); + err = wait_event_interruptible_exclusive_locked(fiq->waitq, + !fiq->connected || request_pending(fiq)); + if (err) + goto err_unlock; + err = -ENODEV; if (!fiq->connected) goto err_unlock; - err = -ERESTARTSYS; - if (!request_pending(fiq)) - goto err_unlock; if (!list_empty(&fiq->interrupts)) { req = list_entry(fiq->interrupts.next, struct fuse_req, -- cgit From 3a2b5b9cd9610f789f5e5f91a010d9fa3ca78632 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:04 +0200 Subject: fuse: separate out processing queue This is just two fields: fc->io and fc->processing. This patch just rearranges the fields, no functional change. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 21 ++++++++++++--------- fs/fuse/fuse_i.h | 15 ++++++++++----- fs/fuse/inode.c | 10 ++++++++-- 3 files changed, 30 insertions(+), 16 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index b6f901a0dbdd..0fae2a7ded77 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1240,6 +1240,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, { int err; struct fuse_iqueue *fiq = &fc->iq; + struct fuse_pqueue *fpq = &fc->pq; struct fuse_req *req; struct fuse_in *in; unsigned reqsize; @@ -1280,7 +1281,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, spin_unlock(&fiq->waitq.lock); spin_lock(&fc->lock); - list_add(&req->list, &fc->io); + list_add(&req->list, &fpq->io); in = &req->in; reqsize = in->h.len; @@ -1314,7 +1315,7 @@ 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 { - list_move_tail(&req->list, &fc->processing); + list_move_tail(&req->list, &fpq->processing); set_bit(FR_SENT, &req->flags); /* matches barrier in request_wait_answer() */ smp_mb__after_atomic(); @@ -1815,11 +1816,11 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, } /* Look up request on processing list by unique ID */ -static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique) +static struct fuse_req *request_find(struct fuse_pqueue *fpq, u64 unique) { struct fuse_req *req; - list_for_each_entry(req, &fc->processing, list) { + list_for_each_entry(req, &fpq->processing, list) { if (req->in.h.unique == unique || req->intr_unique == unique) return req; } @@ -1860,6 +1861,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, struct fuse_copy_state *cs, size_t nbytes) { int err; + struct fuse_pqueue *fpq = &fc->pq; struct fuse_req *req; struct fuse_out_header oh; @@ -1892,7 +1894,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, if (!fc->connected) goto err_unlock; - req = request_find(fc, oh.unique); + req = request_find(fpq, oh.unique); if (!req) goto err_unlock; @@ -1913,7 +1915,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, } clear_bit(FR_SENT, &req->flags); - list_move(&req->list, &fc->io); + list_move(&req->list, &fpq->io); req->out.h = oh; set_bit(FR_LOCKED, &req->flags); cs->req = req; @@ -2112,6 +2114,7 @@ static void end_polls(struct fuse_conn *fc) void fuse_abort_conn(struct fuse_conn *fc) { struct fuse_iqueue *fiq = &fc->iq; + struct fuse_pqueue *fpq = &fc->pq; spin_lock(&fc->lock); if (fc->connected) { @@ -2122,7 +2125,7 @@ void fuse_abort_conn(struct fuse_conn *fc) fc->connected = 0; fc->blocked = 0; fuse_set_initialized(fc); - list_for_each_entry_safe(req, next, &fc->io, list) { + list_for_each_entry_safe(req, next, &fpq->io, list) { req->out.h.error = -ECONNABORTED; spin_lock(&req->waitq.lock); set_bit(FR_ABORTED, &req->flags); @@ -2142,7 +2145,7 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_unlock(&fiq->waitq.lock); kill_fasync(&fiq->fasync, SIGIO, POLL_IN); - list_splice_init(&fc->processing, &to_end2); + list_splice_init(&fpq->processing, &to_end2); while (!list_empty(&to_end1)) { req = list_first_entry(&to_end1, struct fuse_req, list); __fuse_get_request(req); @@ -2161,7 +2164,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(!list_empty(&fc->pq.io)); WARN_ON(fc->iq.fasync != NULL); fuse_abort_conn(fc); fuse_conn_put(fc); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e8be4611fb8e..3620eec018a4 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -401,6 +401,14 @@ struct fuse_iqueue { struct fasync_struct *fasync; }; +struct fuse_pqueue { + /** The list of requests being processed */ + struct list_head processing; + + /** The list of requests under I/O */ + struct list_head io; +}; + /** * A Fuse connection. * @@ -435,11 +443,8 @@ struct fuse_conn { /** Input queue */ struct fuse_iqueue iq; - /** The list of requests being processed */ - struct list_head processing; - - /** The list of requests under I/O */ - struct list_head io; + /** Processing queue */ + struct fuse_pqueue pq; /** The next unique kernel file handle */ u64 khctr; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index caf77d5a6e8d..7b146bb68dd5 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -577,6 +577,13 @@ static void fuse_iqueue_init(struct fuse_iqueue *fiq) fiq->connected = 1; } +static void fuse_pqueue_init(struct fuse_pqueue *fpq) +{ + memset(fpq, 0, sizeof(struct fuse_pqueue)); + INIT_LIST_HEAD(&fpq->processing); + INIT_LIST_HEAD(&fpq->io); +} + void fuse_conn_init(struct fuse_conn *fc) { memset(fc, 0, sizeof(*fc)); @@ -586,8 +593,7 @@ void fuse_conn_init(struct fuse_conn *fc) init_waitqueue_head(&fc->blocked_waitq); init_waitqueue_head(&fc->reserved_req_waitq); fuse_iqueue_init(&fc->iq); - INIT_LIST_HEAD(&fc->processing); - INIT_LIST_HEAD(&fc->io); + fuse_pqueue_init(&fc->pq); INIT_LIST_HEAD(&fc->bg_queue); INIT_LIST_HEAD(&fc->entry); atomic_set(&fc->num_waiting, 0); -- cgit From e96edd94d0887707fc41c5d21d5b488edcd31689 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:04 +0200 Subject: fuse: duplicate ->connected in pqueue This will allow checking ->connected just with the processing queue lock. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 7 ++++--- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0fae2a7ded77..6321d761c3c3 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1303,7 +1303,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, fuse_copy_finish(cs); spin_lock(&fc->lock); clear_bit(FR_LOCKED, &req->flags); - if (!fc->connected) { + if (!fpq->connected) { request_end(fc, req); return -ENODEV; } @@ -1891,7 +1891,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, spin_lock(&fc->lock); err = -ENOENT; - if (!fc->connected) + if (!fpq->connected) goto err_unlock; req = request_find(fpq, oh.unique); @@ -1928,7 +1928,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, spin_lock(&fc->lock); clear_bit(FR_LOCKED, &req->flags); - if (!fc->connected) + if (!fpq->connected) err = -ENOENT; else if (err) req->out.h.error = -EIO; @@ -2125,6 +2125,7 @@ void fuse_abort_conn(struct fuse_conn *fc) fc->connected = 0; fc->blocked = 0; fuse_set_initialized(fc); + fpq->connected = 0; list_for_each_entry_safe(req, next, &fpq->io, list) { req->out.h.error = -ECONNABORTED; spin_lock(&req->waitq.lock); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 3620eec018a4..5897d89ea2ba 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -402,6 +402,9 @@ struct fuse_iqueue { }; struct fuse_pqueue { + /** Connection established */ + unsigned connected; + /** The list of requests being processed */ struct list_head processing; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7b146bb68dd5..88b9ca401623 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -582,6 +582,7 @@ static void fuse_pqueue_init(struct fuse_pqueue *fpq) memset(fpq, 0, sizeof(struct fuse_pqueue)); INIT_LIST_HEAD(&fpq->processing); INIT_LIST_HEAD(&fpq->io); + fpq->connected = 1; } void fuse_conn_init(struct fuse_conn *fc) -- cgit From f377cb799e4f667d743886ee025f9987cbb6cd12 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:04 +0200 Subject: fuse: move list_del_init() from request_end() into callers Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 6321d761c3c3..98568bd2d81b 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -385,7 +385,6 @@ __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); spin_unlock(&fiq->waitq.lock); @@ -1291,6 +1290,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, /* SETXATTR is special, since it may contain too large data */ if (in->h.opcode == FUSE_SETXATTR) req->out.h.error = -E2BIG; + list_del_init(&req->list); request_end(fc, req); goto restart; } @@ -1304,15 +1304,18 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, spin_lock(&fc->lock); clear_bit(FR_LOCKED, &req->flags); if (!fpq->connected) { + list_del_init(&req->list); request_end(fc, req); return -ENODEV; } if (err) { req->out.h.error = -EIO; + list_del_init(&req->list); request_end(fc, req); return err; } if (!test_bit(FR_ISREPLY, &req->flags)) { + list_del_init(&req->list); request_end(fc, req); } else { list_move_tail(&req->list, &fpq->processing); @@ -1932,6 +1935,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, err = -ENOENT; else if (err) req->out.h.error = -EIO; + list_del_init(&req->list); request_end(fc, req); return err ? err : nbytes; @@ -2073,6 +2077,7 @@ __acquires(fc->lock) req->out.h.error = -ECONNABORTED; clear_bit(FR_PENDING, &req->flags); clear_bit(FR_SENT, &req->flags); + list_del_init(&req->list); request_end(fc, req); spin_lock(&fc->lock); } @@ -2150,6 +2155,7 @@ void fuse_abort_conn(struct fuse_conn *fc) while (!list_empty(&to_end1)) { req = list_first_entry(&to_end1, struct fuse_req, list); __fuse_get_request(req); + list_del_init(&req->list); request_end(fc, req); spin_lock(&fc->lock); } -- cgit From 82cbdcd320852ca68c8d43e8aacce2c07e7c1d4e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:05 +0200 Subject: fuse: cleanup fuse_dev_do_read() - locked list_add() + list_del_init() cancel out - common handling of case when request is ended here in the read phase Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 98568bd2d81b..1ad75e4ceba5 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1237,7 +1237,7 @@ __releases(fiq->waitq.lock) static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, struct fuse_copy_state *cs, size_t nbytes) { - int err; + ssize_t err; struct fuse_iqueue *fiq = &fc->iq; struct fuse_pqueue *fpq = &fc->pq; struct fuse_req *req; @@ -1280,8 +1280,6 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, spin_unlock(&fiq->waitq.lock); spin_lock(&fc->lock); - list_add(&req->list, &fpq->io); - in = &req->in; reqsize = in->h.len; /* If request is too large, reply with an error and restart the read */ @@ -1290,10 +1288,10 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, /* SETXATTR is special, since it may contain too large data */ if (in->h.opcode == FUSE_SETXATTR) req->out.h.error = -E2BIG; - list_del_init(&req->list); request_end(fc, req); goto restart; } + list_add(&req->list, &fpq->io); spin_unlock(&fc->lock); cs->req = req; err = fuse_copy_one(cs, &in->h, sizeof(in->h)); @@ -1304,30 +1302,32 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, spin_lock(&fc->lock); clear_bit(FR_LOCKED, &req->flags); if (!fpq->connected) { - list_del_init(&req->list); - request_end(fc, req); - return -ENODEV; + err = -ENODEV; + goto out_end; } if (err) { req->out.h.error = -EIO; - list_del_init(&req->list); - request_end(fc, req); - return err; + goto out_end; } if (!test_bit(FR_ISREPLY, &req->flags)) { - list_del_init(&req->list); - request_end(fc, req); - } else { - list_move_tail(&req->list, &fpq->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(fiq, req); - spin_unlock(&fc->lock); + err = reqsize; + goto out_end; } + list_move_tail(&req->list, &fpq->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(fiq, req); + spin_unlock(&fc->lock); + return reqsize; +out_end: + list_del_init(&req->list); + request_end(fc, req); + return err; + err_unlock: spin_unlock(&fiq->waitq.lock); return err; -- cgit From 24b4d33d46e9c4c671a43f2640d80fe1159f488c Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:05 +0200 Subject: fuse: abort: group pqueue accesses Rearrange fuse_abort_conn() so that processing queue accesses are grouped together. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 1ad75e4ceba5..3e8430074070 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2139,6 +2139,7 @@ void fuse_abort_conn(struct fuse_conn *fc) list_move(&req->list, &to_end1); spin_unlock(&req->waitq.lock); } + list_splice_init(&fpq->processing, &to_end2); fc->max_background = UINT_MAX; flush_bg_queue(fc); @@ -2151,7 +2152,6 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_unlock(&fiq->waitq.lock); kill_fasync(&fiq->fasync, SIGIO, POLL_IN); - list_splice_init(&fpq->processing, &to_end2); while (!list_empty(&to_end1)) { req = list_first_entry(&to_end1, struct fuse_req, list); __fuse_get_request(req); -- cgit From 45a91cb1a4fd9bb0e53c95e3dc9185dd5b5ba245 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:06 +0200 Subject: fuse: pqueue locking Add a fpq->lock for protecting members of struct fuse_pqueue and FR_LOCKED request flag. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 19 +++++++++++++++++-- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 3e8430074070..32e0e74e8f4d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1291,7 +1291,9 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, request_end(fc, req); goto restart; } + spin_lock(&fpq->lock); list_add(&req->list, &fpq->io); + spin_unlock(&fpq->lock); spin_unlock(&fc->lock); cs->req = req; err = fuse_copy_one(cs, &in->h, sizeof(in->h)); @@ -1300,6 +1302,7 @@ 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); + spin_lock(&fpq->lock); clear_bit(FR_LOCKED, &req->flags); if (!fpq->connected) { err = -ENODEV; @@ -1314,6 +1317,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, goto out_end; } list_move_tail(&req->list, &fpq->processing); + spin_unlock(&fpq->lock); set_bit(FR_SENT, &req->flags); /* matches barrier in request_wait_answer() */ smp_mb__after_atomic(); @@ -1325,6 +1329,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, out_end: list_del_init(&req->list); + spin_unlock(&fpq->lock); request_end(fc, req); return err; @@ -1893,16 +1898,19 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, goto err_finish; spin_lock(&fc->lock); + spin_lock(&fpq->lock); err = -ENOENT; if (!fpq->connected) - goto err_unlock; + goto err_unlock_pq; req = request_find(fpq, oh.unique); if (!req) - goto err_unlock; + goto err_unlock_pq; /* Is it an interrupt reply? */ if (req->intr_unique == oh.unique) { + spin_unlock(&fpq->lock); + err = -EINVAL; if (nbytes != sizeof(struct fuse_out_header)) goto err_unlock; @@ -1921,6 +1929,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, list_move(&req->list, &fpq->io); req->out.h = oh; set_bit(FR_LOCKED, &req->flags); + spin_unlock(&fpq->lock); cs->req = req; if (!req->out.page_replace) cs->move_pages = 0; @@ -1930,16 +1939,20 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, fuse_copy_finish(cs); spin_lock(&fc->lock); + spin_lock(&fpq->lock); clear_bit(FR_LOCKED, &req->flags); if (!fpq->connected) err = -ENOENT; else if (err) req->out.h.error = -EIO; list_del_init(&req->list); + spin_unlock(&fpq->lock); request_end(fc, req); return err ? err : nbytes; + err_unlock_pq: + spin_unlock(&fpq->lock); err_unlock: spin_unlock(&fc->lock); err_finish: @@ -2130,6 +2143,7 @@ void fuse_abort_conn(struct fuse_conn *fc) fc->connected = 0; fc->blocked = 0; fuse_set_initialized(fc); + spin_lock(&fpq->lock); fpq->connected = 0; list_for_each_entry_safe(req, next, &fpq->io, list) { req->out.h.error = -ECONNABORTED; @@ -2140,6 +2154,7 @@ void fuse_abort_conn(struct fuse_conn *fc) spin_unlock(&req->waitq.lock); } list_splice_init(&fpq->processing, &to_end2); + spin_unlock(&fpq->lock); fc->max_background = UINT_MAX; flush_bg_queue(fc); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5897d89ea2ba..ad3799e57efd 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -405,6 +405,9 @@ struct fuse_pqueue { /** Connection established */ unsigned connected; + /** Lock protecting accessess to members of this structure */ + spinlock_t lock; + /** The list of requests being processed */ struct list_head processing; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 88b9ca401623..8373f59dc2a8 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -580,6 +580,7 @@ static void fuse_iqueue_init(struct fuse_iqueue *fiq) static void fuse_pqueue_init(struct fuse_pqueue *fpq) { memset(fpq, 0, sizeof(struct fuse_pqueue)); + spin_lock_init(&fpq->lock); INIT_LIST_HEAD(&fpq->processing); INIT_LIST_HEAD(&fpq->io); fpq->connected = 1; -- cgit From 77cd9d488b32d19be852ad4d310ef13701557d61 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:06 +0200 Subject: fuse: add req flag for private list When an unlocked request is aborted, it is moved from fpq->io to a private list. Then, after unlocking fpq->lock, the private list is processed and the requests are finished off. To protect the private list, we need to mark the request with a flag, so if in the meantime the request is unlocked the list is not corrupted. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 10 +++++++--- fs/fuse/fuse_i.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 32e0e74e8f4d..7f37e55edc0e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1328,7 +1328,8 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, return reqsize; out_end: - list_del_init(&req->list); + if (!test_bit(FR_PRIVATE, &req->flags)) + list_del_init(&req->list); spin_unlock(&fpq->lock); request_end(fc, req); return err; @@ -1945,7 +1946,8 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, err = -ENOENT; else if (err) req->out.h.error = -EIO; - list_del_init(&req->list); + if (!test_bit(FR_PRIVATE, &req->flags)) + list_del_init(&req->list); spin_unlock(&fpq->lock); request_end(fc, req); @@ -2149,8 +2151,10 @@ void fuse_abort_conn(struct fuse_conn *fc) req->out.h.error = -ECONNABORTED; spin_lock(&req->waitq.lock); set_bit(FR_ABORTED, &req->flags); - if (!test_bit(FR_LOCKED, &req->flags)) + if (!test_bit(FR_LOCKED, &req->flags)) { + set_bit(FR_PRIVATE, &req->flags); list_move(&req->list, &to_end1); + } spin_unlock(&req->waitq.lock); } list_splice_init(&fpq->processing, &to_end2); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index ad3799e57efd..a9507fd97d5e 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -269,6 +269,7 @@ struct fuse_io_priv { * FR_PENDING: request is not yet in userspace * FR_SENT: request is in userspace, waiting for an answer * FR_FINISHED: request is finished + * FR_PRIVATE: request is on private list */ enum fuse_req_flag { FR_ISREPLY, @@ -281,6 +282,7 @@ enum fuse_req_flag { FR_PENDING, FR_SENT, FR_FINISHED, + FR_PRIVATE, }; /** -- cgit From 365ae710df91edc97d24817e3271e01bffaaee32 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:06 +0200 Subject: fuse: request_end(): do once When the connection is aborted it is possible that request_end() will be called twice. Use atomic test and set to do the actual ending only once. test_and_set_bit() also provides the necessary barrier semantics so no explicit smp_wmb() is necessary. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 7f37e55edc0e..cd242fc6a92b 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -384,14 +384,18 @@ __releases(fc->lock) { struct fuse_iqueue *fiq = &fc->iq; void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; + + if (test_and_set_bit(FR_FINISHED, &req->flags)) { + spin_unlock(&fc->lock); + return; + } + req->end = NULL; spin_lock(&fiq->waitq.lock); list_del_init(&req->intr_entry); 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) -- cgit From 1e6881c36ebbfd47298c42fa82b544c4988933fa Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:07 +0200 Subject: fuse: cleanup request_end() Now that we atomically test having already done everything we no longer need other protection. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index cd242fc6a92b..4e1144a38438 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -383,14 +383,12 @@ 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; if (test_and_set_bit(FR_FINISHED, &req->flags)) { spin_unlock(&fc->lock); return; } - req->end = NULL; spin_lock(&fiq->waitq.lock); list_del_init(&req->intr_entry); spin_unlock(&fiq->waitq.lock); @@ -416,8 +414,8 @@ __releases(fc->lock) } spin_unlock(&fc->lock); wake_up(&req->waitq); - if (end) - end(fc, req); + if (req->end) + req->end(fc, req); fuse_put_request(fc, req); } -- cgit From efe2800facb4505696e602ec4ed1fc947bf114dd Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:07 +0200 Subject: fuse: no fc->lock in request_end() No longer need to call request_end() with the connection lock held. We still protect the background counters and queue with fc->lock, so acquire it if necessary. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 4e1144a38438..f2c7dd90ebfc 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -376,18 +376,13 @@ static void flush_bg_queue(struct fuse_conn *fc) * was closed. The requester thread is woken up (if still waiting), * the 'end' callback is called if given, else the reference to the * request is released - * - * Called with fc->lock, unlocks it */ static void request_end(struct fuse_conn *fc, struct fuse_req *req) -__releases(fc->lock) { struct fuse_iqueue *fiq = &fc->iq; - if (test_and_set_bit(FR_FINISHED, &req->flags)) { - spin_unlock(&fc->lock); + if (test_and_set_bit(FR_FINISHED, &req->flags)) return; - } spin_lock(&fiq->waitq.lock); list_del_init(&req->intr_entry); @@ -395,6 +390,7 @@ __releases(fc->lock) WARN_ON(test_bit(FR_PENDING, &req->flags)); WARN_ON(test_bit(FR_SENT, &req->flags)); if (test_bit(FR_BACKGROUND, &req->flags)) { + spin_lock(&fc->lock); clear_bit(FR_BACKGROUND, &req->flags); if (fc->num_background == fc->max_background) fc->blocked = 0; @@ -411,8 +407,8 @@ __releases(fc->lock) fc->num_background--; fc->active_background--; flush_bg_queue(fc); + spin_unlock(&fc->lock); } - spin_unlock(&fc->lock); wake_up(&req->waitq); if (req->end) req->end(fc, req); @@ -1290,6 +1286,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, /* SETXATTR is special, since it may contain too large data */ if (in->h.opcode == FUSE_SETXATTR) req->out.h.error = -E2BIG; + spin_unlock(&fc->lock); request_end(fc, req); goto restart; } @@ -1333,6 +1330,7 @@ out_end: if (!test_bit(FR_PRIVATE, &req->flags)) list_del_init(&req->list); spin_unlock(&fpq->lock); + spin_unlock(&fc->lock); request_end(fc, req); return err; @@ -1951,6 +1949,7 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, if (!test_bit(FR_PRIVATE, &req->flags)) list_del_init(&req->list); spin_unlock(&fpq->lock); + spin_unlock(&fc->lock); request_end(fc, req); return err ? err : nbytes; @@ -2095,6 +2094,7 @@ __acquires(fc->lock) clear_bit(FR_PENDING, &req->flags); clear_bit(FR_SENT, &req->flags); list_del_init(&req->list); + spin_unlock(&fc->lock); request_end(fc, req); spin_lock(&fc->lock); } @@ -2177,6 +2177,7 @@ void fuse_abort_conn(struct fuse_conn *fc) req = list_first_entry(&to_end1, struct fuse_req, list); __fuse_get_request(req); list_del_init(&req->list); + spin_unlock(&fc->lock); request_end(fc, req); spin_lock(&fc->lock); } -- cgit From 46c34a348b0ac74fc9646de8ef0c5b8b29f09da9 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:07 +0200 Subject: fuse: no fc->lock for pqueue parts Remove fc->lock protection from processing queue members, now protected by fpq->lock. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index f2c7dd90ebfc..fa53e5e9261a 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1277,7 +1277,6 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, list_del_init(&req->list); spin_unlock(&fiq->waitq.lock); - spin_lock(&fc->lock); in = &req->in; reqsize = in->h.len; /* If request is too large, reply with an error and restart the read */ @@ -1286,21 +1285,18 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, /* SETXATTR is special, since it may contain too large data */ if (in->h.opcode == FUSE_SETXATTR) req->out.h.error = -E2BIG; - spin_unlock(&fc->lock); request_end(fc, req); goto restart; } spin_lock(&fpq->lock); list_add(&req->list, &fpq->io); spin_unlock(&fpq->lock); - spin_unlock(&fc->lock); cs->req = req; err = fuse_copy_one(cs, &in->h, sizeof(in->h)); if (!err) err = fuse_copy_args(cs, in->numargs, in->argpages, (struct fuse_arg *) in->args, 0); fuse_copy_finish(cs); - spin_lock(&fc->lock); spin_lock(&fpq->lock); clear_bit(FR_LOCKED, &req->flags); if (!fpq->connected) { @@ -1322,7 +1318,6 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, smp_mb__after_atomic(); if (test_bit(FR_INTERRUPTED, &req->flags)) queue_interrupt(fiq, req); - spin_unlock(&fc->lock); return reqsize; @@ -1330,7 +1325,6 @@ out_end: if (!test_bit(FR_PRIVATE, &req->flags)) list_del_init(&req->list); spin_unlock(&fpq->lock); - spin_unlock(&fc->lock); request_end(fc, req); return err; @@ -1898,7 +1892,6 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, if (oh.error <= -1000 || oh.error > 0) goto err_finish; - spin_lock(&fc->lock); spin_lock(&fpq->lock); err = -ENOENT; if (!fpq->connected) @@ -1914,14 +1907,13 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, err = -EINVAL; if (nbytes != sizeof(struct fuse_out_header)) - goto err_unlock; + goto err_finish; if (oh.error == -ENOSYS) fc->no_interrupt = 1; else if (oh.error == -EAGAIN) queue_interrupt(&fc->iq, req); - spin_unlock(&fc->lock); fuse_copy_finish(cs); return nbytes; } @@ -1934,12 +1926,10 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, cs->req = req; if (!req->out.page_replace) cs->move_pages = 0; - spin_unlock(&fc->lock); err = copy_out_args(cs, &req->out, nbytes); fuse_copy_finish(cs); - spin_lock(&fc->lock); spin_lock(&fpq->lock); clear_bit(FR_LOCKED, &req->flags); if (!fpq->connected) @@ -1949,15 +1939,13 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, if (!test_bit(FR_PRIVATE, &req->flags)) list_del_init(&req->list); spin_unlock(&fpq->lock); - spin_unlock(&fc->lock); + request_end(fc, req); return err ? err : nbytes; err_unlock_pq: spin_unlock(&fpq->lock); - err_unlock: - spin_unlock(&fc->lock); err_finish: fuse_copy_finish(cs); return err; -- cgit From ee314a870e402f4e63b8a651bc96c740ed69cb31 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:08 +0200 Subject: fuse: abort: no fc->lock needed for request ending In fuse_abort_conn() when all requests are on private lists we no longer need fc->lock protection. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index fa53e5e9261a..dcfc87172a47 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2072,8 +2072,6 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) * This function releases and reacquires fc->lock */ static void end_requests(struct fuse_conn *fc, struct list_head *head) -__releases(fc->lock) -__acquires(fc->lock) { while (!list_empty(head)) { struct fuse_req *req; @@ -2082,9 +2080,7 @@ __acquires(fc->lock) clear_bit(FR_PENDING, &req->flags); clear_bit(FR_SENT, &req->flags); list_del_init(&req->list); - spin_unlock(&fc->lock); request_end(fc, req); - spin_lock(&fc->lock); } } @@ -2160,20 +2156,20 @@ void fuse_abort_conn(struct fuse_conn *fc) wake_up_all_locked(&fiq->waitq); spin_unlock(&fiq->waitq.lock); kill_fasync(&fiq->fasync, SIGIO, POLL_IN); + end_polls(fc); + wake_up_all(&fc->blocked_waitq); + spin_unlock(&fc->lock); while (!list_empty(&to_end1)) { req = list_first_entry(&to_end1, struct fuse_req, list); __fuse_get_request(req); list_del_init(&req->list); - spin_unlock(&fc->lock); request_end(fc, req); - spin_lock(&fc->lock); } end_requests(fc, &to_end2); - end_polls(fc); - wake_up_all(&fc->blocked_waitq); + } else { + spin_unlock(&fc->lock); } - spin_unlock(&fc->lock); } EXPORT_SYMBOL_GPL(fuse_abort_conn); -- cgit From 00c570f4ba43ae73b41fa0a2269c3b0ac20386ef Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:08 +0200 Subject: fuse: device fd clone Allow an open fuse device to be "cloned". Userspace can create a clone by: newfd = open("/dev/fuse", O_RDWR) ioctl(newfd, FUSE_DEV_IOC_CLONE, &oldfd); At this point newfd will refer to the same fuse connection as oldfd. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/dev.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index dcfc87172a47..d405c1fa4618 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2197,6 +2197,44 @@ static int fuse_dev_fasync(int fd, struct file *file, int on) return fasync_helper(fd, file, on, &fc->iq.fasync); } +static int fuse_device_clone(struct fuse_conn *fc, struct file *new) +{ + if (new->private_data) + return -EINVAL; + + new->private_data = fuse_conn_get(fc); + + return 0; +} + +static long fuse_dev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int err = -ENOTTY; + + if (cmd == FUSE_DEV_IOC_CLONE) { + int oldfd; + + err = -EFAULT; + if (!get_user(oldfd, (__u32 __user *) arg)) { + struct file *old = fget(oldfd); + + err = -EINVAL; + if (old) { + struct fuse_conn *fc = fuse_get_conn(old); + + if (fc) { + mutex_lock(&fuse_mutex); + err = fuse_device_clone(fc, file); + mutex_unlock(&fuse_mutex); + } + fput(old); + } + } + } + return err; +} + const struct file_operations fuse_dev_operations = { .owner = THIS_MODULE, .open = fuse_dev_open, @@ -2208,6 +2246,8 @@ const struct file_operations fuse_dev_operations = { .poll = fuse_dev_poll, .release = fuse_dev_release, .fasync = fuse_dev_fasync, + .unlocked_ioctl = fuse_dev_ioctl, + .compat_ioctl = fuse_dev_ioctl, }; EXPORT_SYMBOL_GPL(fuse_dev_operations); -- cgit From cc080e9e9be16ccf26135d366d7d2b65209f1d56 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:08 +0200 Subject: fuse: introduce per-instance fuse_dev structure Allow fuse device clones to refer to be distinguished. This patch just adds the infrastructure by associating a separate "struct fuse_dev" with each clone. Signed-off-by: Miklos Szeredi Reviewed-by: Ashish Samant --- fs/fuse/cuse.c | 14 +++++++++--- fs/fuse/dev.c | 70 +++++++++++++++++++++++++++++++++----------------------- fs/fuse/fuse_i.h | 17 ++++++++++++++ fs/fuse/inode.c | 47 ++++++++++++++++++++++++++++++++++--- 4 files changed, 114 insertions(+), 34 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 0993d9a266d3..eae2c11268bc 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -489,6 +489,7 @@ static void cuse_fc_release(struct fuse_conn *fc) */ static int cuse_channel_open(struct inode *inode, struct file *file) { + struct fuse_dev *fud; struct cuse_conn *cc; int rc; @@ -499,16 +500,22 @@ static int cuse_channel_open(struct inode *inode, struct file *file) fuse_conn_init(&cc->fc); + fud = fuse_dev_alloc(&cc->fc); + if (!fud) { + kfree(cc); + return -ENOMEM; + } + INIT_LIST_HEAD(&cc->list); cc->fc.release = cuse_fc_release; cc->fc.initialized = 1; rc = cuse_send_init(cc); if (rc) { - fuse_conn_put(&cc->fc); + fuse_dev_free(fud); return rc; } - file->private_data = &cc->fc; /* channel owns base reference to cc */ + file->private_data = fud; return 0; } @@ -526,7 +533,8 @@ static int cuse_channel_open(struct inode *inode, struct file *file) */ static int cuse_channel_release(struct inode *inode, struct file *file) { - struct cuse_conn *cc = fc_to_cc(file->private_data); + struct fuse_dev *fud = file->private_data; + struct cuse_conn *cc = fc_to_cc(fud->fc); int rc; /* remove from the conntbl, no more access from this point on */ diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index d405c1fa4618..99e94584f17f 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -25,13 +25,13 @@ MODULE_ALIAS("devname:fuse"); static struct kmem_cache *fuse_req_cachep; -static struct fuse_conn *fuse_get_conn(struct file *file) +static struct fuse_dev *fuse_get_dev(struct file *file) { /* * Lockless access is OK, because file->private data is set * once during mount and is valid until the file is released. */ - return file->private_data; + return ACCESS_ONCE(file->private_data); } static void fuse_request_init(struct fuse_req *req, struct page **pages, @@ -1348,8 +1348,9 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) { struct fuse_copy_state cs; struct file *file = iocb->ki_filp; - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(file); + + if (!fud) return -EPERM; if (!iter_is_iovec(to)) @@ -1357,7 +1358,7 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) fuse_copy_init(&cs, 1, to); - return fuse_dev_do_read(fc, file, &cs, iov_iter_count(to)); + return fuse_dev_do_read(fud->fc, file, &cs, iov_iter_count(to)); } static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, @@ -1369,8 +1370,9 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, int do_wakeup = 0; struct pipe_buffer *bufs; struct fuse_copy_state cs; - struct fuse_conn *fc = fuse_get_conn(in); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(in); + + if (!fud) return -EPERM; bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); @@ -1380,7 +1382,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, fuse_copy_init(&cs, 1, NULL); cs.pipebufs = bufs; cs.pipe = pipe; - ret = fuse_dev_do_read(fc, in, &cs, len); + ret = fuse_dev_do_read(fud->fc, in, &cs, len); if (ret < 0) goto out; @@ -1954,8 +1956,9 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) { struct fuse_copy_state cs; - struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(iocb->ki_filp); + + if (!fud) return -EPERM; if (!iter_is_iovec(from)) @@ -1963,7 +1966,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) fuse_copy_init(&cs, 0, from); - return fuse_dev_do_write(fc, &cs, iov_iter_count(from)); + return fuse_dev_do_write(fud->fc, &cs, iov_iter_count(from)); } static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, @@ -1974,12 +1977,12 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, unsigned idx; struct pipe_buffer *bufs; struct fuse_copy_state cs; - struct fuse_conn *fc; + struct fuse_dev *fud; size_t rem; ssize_t ret; - fc = fuse_get_conn(out); - if (!fc) + fud = fuse_get_dev(out); + if (!fud) return -EPERM; bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL); @@ -2034,7 +2037,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, if (flags & SPLICE_F_MOVE) cs.move_pages = 1; - ret = fuse_dev_do_write(fc, &cs, len); + ret = fuse_dev_do_write(fud->fc, &cs, len); for (idx = 0; idx < nbuf; idx++) { struct pipe_buffer *buf = &bufs[idx]; @@ -2049,11 +2052,12 @@ 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) + struct fuse_dev *fud = fuse_get_dev(file); + + if (!fud) return POLLERR; - fiq = &fc->iq; + fiq = &fud->fc->iq; poll_wait(file, &fiq->waitq, wait); spin_lock(&fiq->waitq.lock); @@ -2175,12 +2179,15 @@ EXPORT_SYMBOL_GPL(fuse_abort_conn); int fuse_dev_release(struct inode *inode, struct file *file) { - struct fuse_conn *fc = fuse_get_conn(file); - if (fc) { + struct fuse_dev *fud = fuse_get_dev(file); + + if (fud) { + struct fuse_conn *fc = fud->fc; + WARN_ON(!list_empty(&fc->pq.io)); WARN_ON(fc->iq.fasync != NULL); fuse_abort_conn(fc); - fuse_conn_put(fc); + fuse_dev_free(fud); } return 0; @@ -2189,20 +2196,27 @@ EXPORT_SYMBOL_GPL(fuse_dev_release); static int fuse_dev_fasync(int fd, struct file *file, int on) { - struct fuse_conn *fc = fuse_get_conn(file); - if (!fc) + struct fuse_dev *fud = fuse_get_dev(file); + + if (!fud) return -EPERM; /* No locking - fasync_helper does its own locking */ - return fasync_helper(fd, file, on, &fc->iq.fasync); + return fasync_helper(fd, file, on, &fud->fc->iq.fasync); } static int fuse_device_clone(struct fuse_conn *fc, struct file *new) { + struct fuse_dev *fud; + if (new->private_data) return -EINVAL; - new->private_data = fuse_conn_get(fc); + fud = fuse_dev_alloc(fc); + if (!fud) + return -ENOMEM; + + new->private_data = fud; return 0; } @@ -2221,11 +2235,11 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, err = -EINVAL; if (old) { - struct fuse_conn *fc = fuse_get_conn(old); + struct fuse_dev *fud = fuse_get_dev(old); - if (fc) { + if (fud) { mutex_lock(&fuse_mutex); - err = fuse_device_clone(fc, file); + err = fuse_device_clone(fud->fc, file); mutex_unlock(&fuse_mutex); } fput(old); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index a9507fd97d5e..42d59cbd47e7 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -417,6 +417,17 @@ struct fuse_pqueue { struct list_head io; }; +/** + * Fuse device instance + */ +struct fuse_dev { + /** Fuse connection for this device */ + struct fuse_conn *fc; + + /** list entry on fc->devices */ + struct list_head entry; +}; + /** * A Fuse connection. * @@ -629,6 +640,9 @@ struct fuse_conn { /** Read/write semaphore to hold when accessing sb. */ struct rw_semaphore killsb; + + /** List of device instances belonging to this connection */ + struct list_head devices; }; static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) @@ -841,6 +855,9 @@ void fuse_conn_init(struct fuse_conn *fc); */ void fuse_conn_put(struct fuse_conn *fc); +struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc); +void fuse_dev_free(struct fuse_dev *fud); + /** * Add connection to control filesystem */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 8373f59dc2a8..e399383d87c8 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -598,6 +598,7 @@ void fuse_conn_init(struct fuse_conn *fc) fuse_pqueue_init(&fc->pq); INIT_LIST_HEAD(&fc->bg_queue); INIT_LIST_HEAD(&fc->entry); + INIT_LIST_HEAD(&fc->devices); atomic_set(&fc->num_waiting, 0); fc->max_background = FUSE_DEFAULT_MAX_BACKGROUND; fc->congestion_threshold = FUSE_DEFAULT_CONGESTION_THRESHOLD; @@ -945,6 +946,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) static void fuse_free_conn(struct fuse_conn *fc) { + WARN_ON(!list_empty(&fc->devices)); kfree_rcu(fc, rcu); } @@ -990,8 +992,41 @@ static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) return 0; } +struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) +{ + struct fuse_dev *fud; + + fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); + if (fud) { + fud->fc = fuse_conn_get(fc); + + spin_lock(&fc->lock); + list_add_tail(&fud->entry, &fc->devices); + spin_unlock(&fc->lock); + } + + return fud; +} +EXPORT_SYMBOL_GPL(fuse_dev_alloc); + +void fuse_dev_free(struct fuse_dev *fud) +{ + struct fuse_conn *fc = fud->fc; + + if (fc) { + spin_lock(&fc->lock); + list_del(&fud->entry); + spin_unlock(&fc->lock); + + fuse_conn_put(fc); + } + kfree(fud); +} +EXPORT_SYMBOL_GPL(fuse_dev_free); + static int fuse_fill_super(struct super_block *sb, void *data, int silent) { + struct fuse_dev *fud; struct fuse_conn *fc; struct inode *root; struct fuse_mount_data d; @@ -1043,11 +1078,15 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fuse_conn_init(fc); fc->release = fuse_free_conn; + fud = fuse_dev_alloc(fc); + if (!fud) + goto err_put_conn; + fc->dev = sb->s_dev; fc->sb = sb; err = fuse_bdi_init(fc, sb); if (err) - goto err_put_conn; + goto err_dev_free; sb->s_bdi = &fc->bdi; @@ -1068,7 +1107,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) root = fuse_get_root_inode(sb, d.rootmode); root_dentry = d_make_root(root); if (!root_dentry) - goto err_put_conn; + goto err_dev_free; /* only now - we want root dentry with NULL ->d_op */ sb->s_d_op = &fuse_dentry_operations; @@ -1094,7 +1133,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) list_add_tail(&fc->entry, &fuse_conn_list); sb->s_root = root_dentry; - file->private_data = fuse_conn_get(fc); + file->private_data = fud; mutex_unlock(&fuse_mutex); /* * atomic_dec_and_test() in fput() provides the necessary @@ -1113,6 +1152,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fuse_request_free(init_req); err_put_root: dput(root_dentry); + err_dev_free: + fuse_dev_free(fud); err_put_conn: fuse_bdi_destroy(fc); fuse_conn_put(fc); -- cgit From c3696046beb3a4479715b48f67f6a8a3aef4b3bb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Jul 2015 16:26:09 +0200 Subject: fuse: separate pqueue for clones Make each fuse device clone refer to a separate processing queue. The only constraint on userspace code is that the request answer must be written to the same device clone as it was read off. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 63 +++++++++++++++++++++++++++++++++----------------------- fs/fuse/fuse_i.h | 9 +++++--- fs/fuse/inode.c | 3 ++- 3 files changed, 45 insertions(+), 30 deletions(-) (limited to 'fs/fuse') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 99e94584f17f..80cc1b35d460 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1232,12 +1232,13 @@ __releases(fiq->waitq.lock) * request_end(). Otherwise add it to the processing list, and set * the 'sent' flag. */ -static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file, +static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, struct fuse_copy_state *cs, size_t nbytes) { ssize_t err; + struct fuse_conn *fc = fud->fc; struct fuse_iqueue *fiq = &fc->iq; - struct fuse_pqueue *fpq = &fc->pq; + struct fuse_pqueue *fpq = &fud->pq; struct fuse_req *req; struct fuse_in *in; unsigned reqsize; @@ -1358,7 +1359,7 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) fuse_copy_init(&cs, 1, to); - return fuse_dev_do_read(fud->fc, file, &cs, iov_iter_count(to)); + return fuse_dev_do_read(fud, file, &cs, iov_iter_count(to)); } static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, @@ -1382,7 +1383,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, fuse_copy_init(&cs, 1, NULL); cs.pipebufs = bufs; cs.pipe = pipe; - ret = fuse_dev_do_read(fud->fc, in, &cs, len); + ret = fuse_dev_do_read(fud, in, &cs, len); if (ret < 0) goto out; @@ -1862,11 +1863,12 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out, * it from the list and copy the rest of the buffer to the request. * The request is finished by calling request_end() */ -static ssize_t fuse_dev_do_write(struct fuse_conn *fc, +static ssize_t fuse_dev_do_write(struct fuse_dev *fud, struct fuse_copy_state *cs, size_t nbytes) { int err; - struct fuse_pqueue *fpq = &fc->pq; + struct fuse_conn *fc = fud->fc; + struct fuse_pqueue *fpq = &fud->pq; struct fuse_req *req; struct fuse_out_header oh; @@ -1966,7 +1968,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) fuse_copy_init(&cs, 0, from); - return fuse_dev_do_write(fud->fc, &cs, iov_iter_count(from)); + return fuse_dev_do_write(fud, &cs, iov_iter_count(from)); } static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, @@ -2037,7 +2039,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, if (flags & SPLICE_F_MOVE) cs.move_pages = 1; - ret = fuse_dev_do_write(fud->fc, &cs, len); + ret = fuse_dev_do_write(fud, &cs, len); for (idx = 0; idx < nbuf; idx++) { struct pipe_buffer *buf = &bufs[idx]; @@ -2124,10 +2126,10 @@ static void end_polls(struct fuse_conn *fc) void fuse_abort_conn(struct fuse_conn *fc) { struct fuse_iqueue *fiq = &fc->iq; - struct fuse_pqueue *fpq = &fc->pq; spin_lock(&fc->lock); if (fc->connected) { + struct fuse_dev *fud; struct fuse_req *req, *next; LIST_HEAD(to_end1); LIST_HEAD(to_end2); @@ -2135,20 +2137,24 @@ void fuse_abort_conn(struct fuse_conn *fc) fc->connected = 0; fc->blocked = 0; fuse_set_initialized(fc); - spin_lock(&fpq->lock); - fpq->connected = 0; - list_for_each_entry_safe(req, next, &fpq->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)) { - set_bit(FR_PRIVATE, &req->flags); - list_move(&req->list, &to_end1); + list_for_each_entry(fud, &fc->devices, entry) { + struct fuse_pqueue *fpq = &fud->pq; + + spin_lock(&fpq->lock); + fpq->connected = 0; + list_for_each_entry_safe(req, next, &fpq->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)) { + set_bit(FR_PRIVATE, &req->flags); + list_move(&req->list, &to_end1); + } + spin_unlock(&req->waitq.lock); } - spin_unlock(&req->waitq.lock); + list_splice_init(&fpq->processing, &to_end2); + spin_unlock(&fpq->lock); } - list_splice_init(&fpq->processing, &to_end2); - spin_unlock(&fpq->lock); fc->max_background = UINT_MAX; flush_bg_queue(fc); @@ -2183,13 +2189,17 @@ int fuse_dev_release(struct inode *inode, struct file *file) if (fud) { struct fuse_conn *fc = fud->fc; - - WARN_ON(!list_empty(&fc->pq.io)); - WARN_ON(fc->iq.fasync != NULL); - fuse_abort_conn(fc); + struct fuse_pqueue *fpq = &fud->pq; + + WARN_ON(!list_empty(&fpq->io)); + end_requests(fc, &fpq->processing); + /* Are we the last open device? */ + if (atomic_dec_and_test(&fc->dev_count)) { + WARN_ON(fc->iq.fasync != NULL); + fuse_abort_conn(fc); + } fuse_dev_free(fud); } - return 0; } EXPORT_SYMBOL_GPL(fuse_dev_release); @@ -2217,6 +2227,7 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new) return -ENOMEM; new->private_data = fud; + atomic_inc(&fc->dev_count); return 0; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 42d59cbd47e7..405113101db8 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -424,6 +424,9 @@ struct fuse_dev { /** Fuse connection for this device */ struct fuse_conn *fc; + /** Processing queue */ + struct fuse_pqueue pq; + /** list entry on fc->devices */ struct list_head entry; }; @@ -442,6 +445,9 @@ struct fuse_conn { /** Refcount */ atomic_t count; + /** Number of fuse_dev's */ + atomic_t dev_count; + struct rcu_head rcu; /** The user id for this mount */ @@ -462,9 +468,6 @@ struct fuse_conn { /** Input queue */ struct fuse_iqueue iq; - /** Processing queue */ - struct fuse_pqueue pq; - /** The next unique kernel file handle */ u64 khctr; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index e399383d87c8..ac81f48ab2f4 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -592,10 +592,10 @@ void fuse_conn_init(struct fuse_conn *fc) spin_lock_init(&fc->lock); init_rwsem(&fc->killsb); atomic_set(&fc->count, 1); + atomic_set(&fc->dev_count, 1); init_waitqueue_head(&fc->blocked_waitq); init_waitqueue_head(&fc->reserved_req_waitq); fuse_iqueue_init(&fc->iq); - fuse_pqueue_init(&fc->pq); INIT_LIST_HEAD(&fc->bg_queue); INIT_LIST_HEAD(&fc->entry); INIT_LIST_HEAD(&fc->devices); @@ -999,6 +999,7 @@ struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc) fud = kzalloc(sizeof(struct fuse_dev), GFP_KERNEL); if (fud) { fud->fc = fuse_conn_get(fc); + fuse_pqueue_init(&fud->pq); spin_lock(&fc->lock); list_add_tail(&fud->entry, &fc->devices); -- cgit