Skip to content

Commit

Permalink
[PATCH] fuse: clean up request accounting
Browse files Browse the repository at this point in the history
FUSE allocated most requests from a fixed size pool filled at mount time.
However in some cases (release/forget) non-pool requests were used.  File
locking operations aren't well served by the request pool, since they may
block indefinetly thus exhausting the pool.

This patch removes the request pool and always allocates requests on demand.

Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
Miklos Szeredi authored and Linus Torvalds committed Apr 11, 2006
1 parent a87046d commit ce1d5a4
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 208 deletions.
73 changes: 9 additions & 64 deletions fs/fuse/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,8 @@ static void restore_sigs(sigset_t *oldset)
*/
void fuse_reset_request(struct fuse_req *req)
{
int preallocated = req->preallocated;
BUG_ON(atomic_read(&req->count) != 1);
fuse_request_init(req);
req->preallocated = preallocated;
}

static void __fuse_get_request(struct fuse_req *req)
Expand All @@ -90,71 +88,28 @@ static void __fuse_put_request(struct fuse_req *req)
atomic_dec(&req->count);
}

static struct fuse_req *do_get_request(struct fuse_conn *fc)
struct fuse_req *fuse_get_req(struct fuse_conn *fc)
{
struct fuse_req *req;
struct fuse_req *req = fuse_request_alloc();
if (!req)
return ERR_PTR(-ENOMEM);

spin_lock(&fc->lock);
BUG_ON(list_empty(&fc->unused_list));
req = list_entry(fc->unused_list.next, struct fuse_req, list);
list_del_init(&req->list);
spin_unlock(&fc->lock);
atomic_inc(&fc->num_waiting);
fuse_request_init(req);
req->preallocated = 1;
req->in.h.uid = current->fsuid;
req->in.h.gid = current->fsgid;
req->in.h.pid = current->pid;
return req;
}

/* This can return NULL, but only in case it's interrupted by a SIGKILL */
struct fuse_req *fuse_get_request(struct fuse_conn *fc)
{
int intr;
sigset_t oldset;

atomic_inc(&fc->num_waiting);
block_sigs(&oldset);
intr = down_interruptible(&fc->outstanding_sem);
restore_sigs(&oldset);
if (intr) {
atomic_dec(&fc->num_waiting);
return NULL;
}
return do_get_request(fc);
}

/* Must be called with fc->lock held */
static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (req->preallocated) {
atomic_dec(&fc->num_waiting);
list_add(&req->list, &fc->unused_list);
} else
fuse_request_free(req);

/* If we are in debt decrease that first */
if (fc->outstanding_debt)
fc->outstanding_debt--;
else
up(&fc->outstanding_sem);
}

void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (atomic_dec_and_test(&req->count)) {
spin_lock(&fc->lock);
fuse_putback_request(fc, req);
spin_unlock(&fc->lock);
atomic_dec(&fc->num_waiting);
fuse_request_free(req);
}
}

static void fuse_put_request_locked(struct fuse_conn *fc, struct fuse_req *req)
{
if (atomic_dec_and_test(&req->count))
fuse_putback_request(fc, req);
}

void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
{
iput(req->inode);
Expand Down Expand Up @@ -189,9 +144,9 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
list_del(&req->list);
req->state = FUSE_REQ_FINISHED;
if (!req->background) {
wake_up(&req->waitq);
fuse_put_request_locked(fc, req);
spin_unlock(&fc->lock);
wake_up(&req->waitq);
fuse_put_request(fc, req);
} else {
void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
req->end = NULL;
Expand Down Expand Up @@ -302,16 +257,6 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
req->in.h.unique = fc->reqctr;
req->in.h.len = sizeof(struct fuse_in_header) +
len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
if (!req->preallocated) {
/* If request is not preallocated (either FORGET or
RELEASE), then still decrease outstanding_sem, so
user can't open infinite number of files while not
processing the RELEASE requests. However for
efficiency do it without blocking, so if down()
would block, just increase the debt instead */
if (down_trylock(&fc->outstanding_sem))
fc->outstanding_debt++;
}
list_add_tail(&req->list, &fc->pending);
req->state = FUSE_REQ_PENDING;
wake_up(&fc->waitq);
Expand Down
118 changes: 58 additions & 60 deletions fs/fuse/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
return 0;

fc = get_fuse_conn(inode);
req = fuse_get_request(fc);
if (!req)
req = fuse_get_req(fc);
if (IS_ERR(req))
return 0;

fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
Expand Down Expand Up @@ -188,9 +188,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
if (entry->d_name.len > FUSE_NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);

req = fuse_get_request(fc);
if (!req)
return ERR_PTR(-EINTR);
req = fuse_get_req(fc);
if (IS_ERR(req))
return ERR_PTR(PTR_ERR(req));

fuse_lookup_init(req, dir, entry, &outarg);
request_send(fc, req);
Expand Down Expand Up @@ -244,15 +244,14 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
struct file *file;
int flags = nd->intent.open.flags - 1;

err = -ENOSYS;
if (fc->no_create)
goto out;
return -ENOSYS;

err = -EINTR;
req = fuse_get_request(fc);
if (!req)
goto out;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

err = -ENOMEM;
ff = fuse_file_alloc();
if (!ff)
goto out_put_request;
Expand Down Expand Up @@ -314,7 +313,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
fuse_file_free(ff);
out_put_request:
fuse_put_request(fc, req);
out:
return err;
}

Expand Down Expand Up @@ -375,9 +373,9 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
{
struct fuse_mknod_in inarg;
struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
Expand Down Expand Up @@ -407,9 +405,9 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
{
struct fuse_mkdir_in inarg;
struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.mode = mode;
Expand All @@ -427,9 +425,9 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
{
struct fuse_conn *fc = get_fuse_conn(dir);
unsigned len = strlen(link) + 1;
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

req->in.h.opcode = FUSE_SYMLINK;
req->in.numargs = 2;
Expand All @@ -444,9 +442,9 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
{
int err;
struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

req->in.h.opcode = FUSE_UNLINK;
req->in.h.nodeid = get_node_id(dir);
Expand Down Expand Up @@ -476,9 +474,9 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
{
int err;
struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

req->in.h.opcode = FUSE_RMDIR;
req->in.h.nodeid = get_node_id(dir);
Expand All @@ -504,9 +502,9 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
int err;
struct fuse_rename_in inarg;
struct fuse_conn *fc = get_fuse_conn(olddir);
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.newdir = get_node_id(newdir);
Expand Down Expand Up @@ -553,9 +551,9 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
struct fuse_link_in inarg;
struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.oldnodeid = get_node_id(inode);
Expand Down Expand Up @@ -583,9 +581,9 @@ int fuse_do_getattr(struct inode *inode)
int err;
struct fuse_attr_out arg;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req = fuse_get_request(fc);
if (!req)
return -EINTR;
struct fuse_req *req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

req->in.h.opcode = FUSE_GETATTR;
req->in.h.nodeid = get_node_id(inode);
Expand Down Expand Up @@ -673,9 +671,9 @@ static int fuse_access(struct inode *inode, int mask)
if (fc->no_access)
return 0;

req = fuse_get_request(fc);
if (!req)
return -EINTR;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.mask = mask;
Expand Down Expand Up @@ -780,9 +778,9 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
if (is_bad_inode(inode))
return -EIO;

req = fuse_get_request(fc);
if (!req)
return -EINTR;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

page = alloc_page(GFP_KERNEL);
if (!page) {
Expand All @@ -809,11 +807,11 @@ static char *read_link(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req = fuse_get_request(fc);
struct fuse_req *req = fuse_get_req(fc);
char *link;

if (!req)
return ERR_PTR(-EINTR);
if (IS_ERR(req))
return ERR_PTR(PTR_ERR(req));

link = (char *) __get_free_page(GFP_KERNEL);
if (!link) {
Expand Down Expand Up @@ -933,9 +931,9 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
}
}

req = fuse_get_request(fc);
if (!req)
return -EINTR;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
iattr_to_fattr(attr, &inarg);
Expand Down Expand Up @@ -995,9 +993,9 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
if (fc->no_setxattr)
return -EOPNOTSUPP;

req = fuse_get_request(fc);
if (!req)
return -EINTR;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
Expand Down Expand Up @@ -1035,9 +1033,9 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
if (fc->no_getxattr)
return -EOPNOTSUPP;

req = fuse_get_request(fc);
if (!req)
return -EINTR;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
Expand Down Expand Up @@ -1085,9 +1083,9 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
if (fc->no_listxattr)
return -EOPNOTSUPP;

req = fuse_get_request(fc);
if (!req)
return -EINTR;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

memset(&inarg, 0, sizeof(inarg));
inarg.size = size;
Expand Down Expand Up @@ -1131,9 +1129,9 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
if (fc->no_removexattr)
return -EOPNOTSUPP;

req = fuse_get_request(fc);
if (!req)
return -EINTR;
req = fuse_get_req(fc);
if (IS_ERR(req))
return PTR_ERR(req);

req->in.h.opcode = FUSE_REMOVEXATTR;
req->in.h.nodeid = get_node_id(inode);
Expand Down
Loading

0 comments on commit ce1d5a4

Please sign in to comment.