From 8b41e6715ed555e2d8e8dac52ec1f05a9f04dcb4 Mon Sep 17 00:00:00 2001 From: Maxim Patlasov Date: Thu, 21 Mar 2013 18:02:04 +0400 Subject: fuse: make request allocations for background processing explicit There are two types of processing requests in FUSE: synchronous (via fuse_request_send()) and asynchronous (via adding to fc->bg_queue). Fortunately, the type of processing is always known in advance, at the time of request allocation. This preparatory patch utilizes this fact making fuse_get_req() aware about the type. Next patches will use it. Signed-off-by: Maxim Patlasov Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'fs/fuse/dev.c') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 11dfa0c3fb46..3b8301f5c0e1 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -130,7 +130,8 @@ static void fuse_req_init_context(struct fuse_req *req) req->in.h.pid = current->pid; } -struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages) +static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, + bool for_background) { struct fuse_req *req; sigset_t oldset; @@ -156,14 +157,27 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages) fuse_req_init_context(req); req->waiting = 1; + req->background = for_background; return req; out: atomic_dec(&fc->num_waiting); return ERR_PTR(err); } + +struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages) +{ + return __fuse_get_req(fc, npages, false); +} EXPORT_SYMBOL_GPL(fuse_get_req); +struct fuse_req *fuse_get_req_for_background(struct fuse_conn *fc, + unsigned npages) +{ + return __fuse_get_req(fc, npages, true); +} +EXPORT_SYMBOL_GPL(fuse_get_req_for_background); + /* * Return request in fuse_file->reserved_req. However that may * currently be in use. If that is the case, wait for it to become @@ -232,6 +246,7 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc, fuse_req_init_context(req); req->waiting = 1; + req->background = 0; return req; } @@ -442,6 +457,7 @@ __acquires(fc->lock) static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) { + BUG_ON(req->background); spin_lock(&fc->lock); if (!fc->connected) req->out.h.error = -ENOTCONN; @@ -469,7 +485,7 @@ EXPORT_SYMBOL_GPL(fuse_request_send); static void fuse_request_send_nowait_locked(struct fuse_conn *fc, struct fuse_req *req) { - req->background = 1; + BUG_ON(!req->background); fc->num_background++; if (fc->num_background == fc->max_background) fc->blocked = 1; -- cgit v1.2.2 From 796523fb24028639c007f71e02ca21730f7c0af6 Mon Sep 17 00:00:00 2001 From: Maxim Patlasov Date: Thu, 21 Mar 2013 18:02:15 +0400 Subject: fuse: add flag fc->initialized Existing flag fc->blocked is used to suspend request allocation both in case of many background request submitted and period of time before init_reply arrives from userspace. Next patch will skip blocking allocations of synchronous request (disregarding fc->blocked). This is mostly OK, but we still need to suspend allocations if init_reply is not arrived yet. The patch introduces flag fc->initialized which will serve this purpose. Signed-off-by: Maxim Patlasov Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/fuse/dev.c') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 3b8301f5c0e1..39c2fe3ba93d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2087,6 +2087,7 @@ void fuse_abort_conn(struct fuse_conn *fc) if (fc->connected) { fc->connected = 0; fc->blocked = 0; + fc->initialized = 1; end_io_requests(fc); end_queued_requests(fc); end_polls(fc); -- cgit v1.2.2 From 0aada88476a33690c9569b094191ce92a38e6541 Mon Sep 17 00:00:00 2001 From: Maxim Patlasov Date: Thu, 21 Mar 2013 18:02:28 +0400 Subject: fuse: skip blocking on allocations of synchronous requests A task may have at most one synchronous request allocated. So these requests need not be otherwise limited. The patch re-works fuse_get_req() to follow this idea. Signed-off-by: Maxim Patlasov Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'fs/fuse/dev.c') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 39c2fe3ba93d..d692b85115bd 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -130,21 +130,30 @@ static void fuse_req_init_context(struct fuse_req *req) req->in.h.pid = current->pid; } +static bool fuse_block_alloc(struct fuse_conn *fc, bool for_background) +{ + return !fc->initialized || (for_background && fc->blocked); +} + static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, bool for_background) { struct fuse_req *req; - sigset_t oldset; - int intr; int err; - atomic_inc(&fc->num_waiting); - block_sigs(&oldset); - intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked); - restore_sigs(&oldset); - err = -EINTR; - if (intr) - goto out; + + if (fuse_block_alloc(fc, for_background)) { + sigset_t oldset; + int intr; + + block_sigs(&oldset); + intr = wait_event_interruptible(fc->blocked_waitq, + !fuse_block_alloc(fc, for_background)); + restore_sigs(&oldset); + err = -EINTR; + if (intr) + goto out; + } err = -ENOTCONN; if (!fc->connected) @@ -239,7 +248,7 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc, struct fuse_req *req; atomic_inc(&fc->num_waiting); - wait_event(fc->blocked_waitq, !fc->blocked); + wait_event(fc->blocked_waitq, fc->initialized); req = fuse_request_alloc(0); if (!req) req = get_reserved_req(fc, file); @@ -2106,6 +2115,7 @@ int fuse_dev_release(struct inode *inode, struct file *file) spin_lock(&fc->lock); fc->connected = 0; fc->blocked = 0; + fc->initialized = 1; end_queued_requests(fc); end_polls(fc); wake_up_all(&fc->blocked_waitq); -- cgit v1.2.2 From 722d2bea8c601d0744e4a37170533fdf6214a678 Mon Sep 17 00:00:00 2001 From: Maxim Patlasov Date: Thu, 21 Mar 2013 18:02:36 +0400 Subject: fuse: implement exclusive wakeup for blocked_waitq The patch solves thundering herd problem. So far as previous patches ensured that only allocations for background may block, it's safe to wake up one waiter. Whoever it is, it will wake up another one in request_end() afterwards. Signed-off-by: Maxim Patlasov Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'fs/fuse/dev.c') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index d692b85115bd..367310588962 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -147,7 +147,7 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, int intr; block_sigs(&oldset); - intr = wait_event_interruptible(fc->blocked_waitq, + intr = wait_event_interruptible_exclusive(fc->blocked_waitq, !fuse_block_alloc(fc, for_background)); restore_sigs(&oldset); err = -EINTR; @@ -161,8 +161,11 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, req = fuse_request_alloc(npages); err = -ENOMEM; - if (!req) + if (!req) { + if (for_background) + wake_up(&fc->blocked_waitq); goto out; + } fuse_req_init_context(req); req->waiting = 1; @@ -262,6 +265,17 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc, void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) { if (atomic_dec_and_test(&req->count)) { + if (unlikely(req->background)) { + /* + * We get here in the unlikely case that a background + * request was allocated but not sent + */ + spin_lock(&fc->lock); + if (!fc->blocked) + wake_up(&fc->blocked_waitq); + spin_unlock(&fc->lock); + } + if (req->waiting) atomic_dec(&fc->num_waiting); @@ -359,10 +373,15 @@ __releases(fc->lock) list_del(&req->intr_entry); req->state = FUSE_REQ_FINISHED; if (req->background) { - if (fc->num_background == fc->max_background) { + req->background = 0; + + if (fc->num_background == fc->max_background) fc->blocked = 0; - wake_up_all(&fc->blocked_waitq); - } + + /* Wake up next waiter, if any */ + if (!fc->blocked) + wake_up(&fc->blocked_waitq); + if (fc->num_background == fc->congestion_threshold && fc->connected && fc->bdi_initialized) { clear_bdi_congested(&fc->bdi, BLK_RW_SYNC); -- cgit v1.2.2 From 3c18ef8117f0515b1e455713dfc2e18b06db9bb5 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 17 Apr 2013 21:50:58 +0200 Subject: fuse: optimize wake_up Normally blocked_waitq will be inactive, so optimize this case. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/fuse/dev.c') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 367310588962..be5c7e13320c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -379,7 +379,7 @@ __releases(fc->lock) fc->blocked = 0; /* Wake up next waiter, if any */ - if (!fc->blocked) + if (!fc->blocked && waitqueue_active(&fc->blocked_waitq)) wake_up(&fc->blocked_waitq); if (fc->num_background == fc->congestion_threshold && -- cgit v1.2.2 From 36cf66ed9f871fc0d0911921fba5873df3ddb2dc Mon Sep 17 00:00:00 2001 From: Maxim Patlasov Date: Fri, 14 Dec 2012 19:20:51 +0400 Subject: fuse: make fuse_direct_io() aware about AIO The patch implements passing "struct fuse_io_priv *io" down the stack up to fuse_send_read/write where it is used to submit request asynchronously. io->async==0 designates synchronous processing. Non-trivial part of the patch is changes in fuse_direct_io(): resources like fuse requests and user pages cannot be released immediately in async case. Signed-off-by: Maxim Patlasov Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/fuse/dev.c') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index be5c7e13320c..07bdb14ae7a3 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -111,7 +111,7 @@ static void restore_sigs(sigset_t *oldset) sigprocmask(SIG_SETMASK, oldset, NULL); } -static void __fuse_get_request(struct fuse_req *req) +void __fuse_get_request(struct fuse_req *req) { atomic_inc(&req->count); } -- cgit v1.2.2