diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 4dc104c0e95d..6c740f860665 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -90,7 +90,17 @@ static void __fuse_put_request(struct fuse_req *req) struct fuse_req *fuse_get_req(struct fuse_conn *fc) { - struct fuse_req *req = fuse_request_alloc(); + struct fuse_req *req; + sigset_t oldset; + int err; + + block_sigs(&oldset); + err = wait_event_interruptible(fc->blocked_waitq, !fc->blocked); + restore_sigs(&oldset); + if (err) + return ERR_PTR(-EINTR); + + req = fuse_request_alloc(); if (!req) return ERR_PTR(-ENOMEM); @@ -118,6 +128,11 @@ void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req) fput(req->file); spin_lock(&fc->lock); list_del(&req->bg_entry); + if (fc->num_background == FUSE_MAX_BACKGROUND) { + fc->blocked = 0; + wake_up_all(&fc->blocked_waitq); + } + fc->num_background--; spin_unlock(&fc->lock); } @@ -195,6 +210,9 @@ static void background_request(struct fuse_conn *fc, struct fuse_req *req) { req->background = 1; list_add(&req->bg_entry, &fc->background); + fc->num_background++; + if (fc->num_background == FUSE_MAX_BACKGROUND) + fc->blocked = 1; if (req->inode) req->inode = igrab(req->inode); if (req->inode2) @@ -288,6 +306,7 @@ void request_send(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); + background_request(fc, req); if (fc->connected) { queue_request(fc, req); spin_unlock(&fc->lock); @@ -306,9 +325,6 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) void request_send_background(struct fuse_conn *fc, struct fuse_req *req) { req->isreply = 1; - spin_lock(&fc->lock); - background_request(fc, req); - spin_unlock(&fc->lock); request_send_nowait(fc, req); } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 242e69cb1251..19c7185a7546 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -18,6 +18,9 @@ /** Max number of pages that can be used in a single read request */ #define FUSE_MAX_PAGES_PER_REQ 32 +/** Maximum number of outstanding background requests */ +#define FUSE_MAX_BACKGROUND 10 + /** It could be as large as PATH_MAX, but would that have any uses? */ #define FUSE_NAME_MAX 1024 @@ -241,6 +244,17 @@ struct fuse_conn { interrupted request) */ struct list_head background; + /** Number of requests currently in the background */ + unsigned num_background; + + /** Flag indicating if connection is blocked. This will be + the case before the INIT reply is received, and if there + are too many outstading backgrounds requests */ + int blocked; + + /** waitq for blocked connection */ + wait_queue_head_t blocked_waitq; + /** RW semaphore for exclusion with fuse_put_super() */ struct rw_semaphore sbput_sem; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 824ebbc428ed..fd34037b0588 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -381,6 +381,7 @@ static struct fuse_conn *new_conn(void) if (fc) { spin_lock_init(&fc->lock); init_waitqueue_head(&fc->waitq); + init_waitqueue_head(&fc->blocked_waitq); INIT_LIST_HEAD(&fc->pending); INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->io); @@ -392,6 +393,7 @@ static struct fuse_conn *new_conn(void) fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; fc->bdi.unplug_io_fn = default_unplug_io_fn; fc->reqctr = 0; + fc->blocked = 1; } return fc; } @@ -438,6 +440,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; } fuse_put_request(fc, req); + fc->blocked = 0; + wake_up_all(&fc->blocked_waitq); } static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)