Commit d12def1b authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Linus Torvalds

fuse: limit queued background requests

Libfuse basically creates a new thread for each new request.  This is fine for
synchronous requests, which are naturally limited.  However background
requests (especially writepage) can cause a thread creation storm.

To avoid this, limit the number of background requests available to userspace.

This is done by introducing another queue for background requests, and a
counter for the number of "active" requests, which are currently available for
userspace.
Signed-off-by: default avatarMiklos Szeredi <mszeredi@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b57d4264
...@@ -201,6 +201,55 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) ...@@ -201,6 +201,55 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
} }
} }
static unsigned len_args(unsigned numargs, struct fuse_arg *args)
{
unsigned nbytes = 0;
unsigned i;
for (i = 0; i < numargs; i++)
nbytes += args[i].size;
return nbytes;
}
static u64 fuse_get_unique(struct fuse_conn *fc)
{
fc->reqctr++;
/* zero is special */
if (fc->reqctr == 0)
fc->reqctr = 1;
return fc->reqctr;
}
static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
{
req->in.h.unique = fuse_get_unique(fc);
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;
if (!req->waiting) {
req->waiting = 1;
atomic_inc(&fc->num_waiting);
}
wake_up(&fc->waitq);
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
}
static void flush_bg_queue(struct fuse_conn *fc)
{
while (fc->active_background < FUSE_MAX_BACKGROUND &&
!list_empty(&fc->bg_queue)) {
struct fuse_req *req;
req = list_entry(fc->bg_queue.next, struct fuse_req, list);
list_del(&req->list);
fc->active_background++;
queue_request(fc, req);
}
}
/* /*
* This function is called when a request is finished. Either a reply * This function is called when a request is finished. Either a reply
* has arrived or it was aborted (and not yet sent) or some error * has arrived or it was aborted (and not yet sent) or some error
...@@ -229,6 +278,8 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) ...@@ -229,6 +278,8 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
clear_bdi_congested(&fc->bdi, WRITE); clear_bdi_congested(&fc->bdi, WRITE);
} }
fc->num_background--; fc->num_background--;
fc->active_background--;
flush_bg_queue(fc);
} }
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
wake_up(&req->waitq); wake_up(&req->waitq);
...@@ -320,42 +371,6 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) ...@@ -320,42 +371,6 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
} }
} }
static unsigned len_args(unsigned numargs, struct fuse_arg *args)
{
unsigned nbytes = 0;
unsigned i;
for (i = 0; i < numargs; i++)
nbytes += args[i].size;
return nbytes;
}
static u64 fuse_get_unique(struct fuse_conn *fc)
{
fc->reqctr++;
/* zero is special */
if (fc->reqctr == 0)
fc->reqctr = 1;
return fc->reqctr;
}
static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
{
req->in.h.unique = fuse_get_unique(fc);
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;
if (!req->waiting) {
req->waiting = 1;
atomic_inc(&fc->num_waiting);
}
wake_up(&fc->waitq);
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
}
void request_send(struct fuse_conn *fc, struct fuse_req *req) void request_send(struct fuse_conn *fc, struct fuse_req *req)
{ {
req->isreply = 1; req->isreply = 1;
...@@ -375,20 +390,26 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req) ...@@ -375,20 +390,26 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req)
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
} }
static void request_send_nowait_locked(struct fuse_conn *fc,
struct fuse_req *req)
{
req->background = 1;
fc->num_background++;
if (fc->num_background == FUSE_MAX_BACKGROUND)
fc->blocked = 1;
if (fc->num_background == FUSE_CONGESTION_THRESHOLD) {
set_bdi_congested(&fc->bdi, READ);
set_bdi_congested(&fc->bdi, WRITE);
}
list_add_tail(&req->list, &fc->bg_queue);
flush_bg_queue(fc);
}
static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{ {
spin_lock(&fc->lock); spin_lock(&fc->lock);
if (fc->connected) { if (fc->connected) {
req->background = 1; request_send_nowait_locked(fc, req);
fc->num_background++;
if (fc->num_background == FUSE_MAX_BACKGROUND)
fc->blocked = 1;
if (fc->num_background == FUSE_CONGESTION_THRESHOLD) {
set_bdi_congested(&fc->bdi, READ);
set_bdi_congested(&fc->bdi, WRITE);
}
queue_request(fc, req);
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
} else { } else {
req->out.h.error = -ENOTCONN; req->out.h.error = -ENOTCONN;
......
...@@ -296,6 +296,12 @@ struct fuse_conn { ...@@ -296,6 +296,12 @@ struct fuse_conn {
/** Number of requests currently in the background */ /** Number of requests currently in the background */
unsigned num_background; unsigned num_background;
/** Number of background requests currently queued for userspace */
unsigned active_background;
/** The list of background requests set aside for later queuing */
struct list_head bg_queue;
/** Pending interrupts */ /** Pending interrupts */
struct list_head interrupts; struct list_head interrupts;
......
...@@ -465,6 +465,7 @@ static struct fuse_conn *new_conn(void) ...@@ -465,6 +465,7 @@ static struct fuse_conn *new_conn(void)
INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->io); INIT_LIST_HEAD(&fc->io);
INIT_LIST_HEAD(&fc->interrupts); INIT_LIST_HEAD(&fc->interrupts);
INIT_LIST_HEAD(&fc->bg_queue);
atomic_set(&fc->num_waiting, 0); atomic_set(&fc->num_waiting, 0);
fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
fc->bdi.unplug_io_fn = default_unplug_io_fn; fc->bdi.unplug_io_fn = default_unplug_io_fn;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment