summaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/dev.c39
-rw-r--r--fs/fuse/file.c10
-rw-r--r--fs/fuse/fuse_i.h3
-rw-r--r--fs/fuse/inode.c13
4 files changed, 48 insertions, 17 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 6c740f8..104a62d 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -92,40 +92,52 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
{
struct fuse_req *req;
sigset_t oldset;
+ int intr;
int err;
+ atomic_inc(&fc->num_waiting);
block_sigs(&oldset);
- err = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
+ intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
restore_sigs(&oldset);
- if (err)
- return ERR_PTR(-EINTR);
+ err = -EINTR;
+ if (intr)
+ goto out;
req = fuse_request_alloc();
+ err = -ENOMEM;
if (!req)
- return ERR_PTR(-ENOMEM);
+ goto out;
- atomic_inc(&fc->num_waiting);
- fuse_request_init(req);
req->in.h.uid = current->fsuid;
req->in.h.gid = current->fsgid;
req->in.h.pid = current->pid;
+ req->waiting = 1;
return req;
+
+ out:
+ atomic_dec(&fc->num_waiting);
+ return ERR_PTR(err);
}
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (atomic_dec_and_test(&req->count)) {
- atomic_dec(&fc->num_waiting);
+ if (req->waiting)
+ atomic_dec(&fc->num_waiting);
fuse_request_free(req);
}
}
+/*
+ * Called with sbput_sem held for read (request_end) or write
+ * (fuse_put_super). By the time fuse_put_super() is finished, all
+ * inodes belonging to background requests must be released, so the
+ * iputs have to be done within the locked region.
+ */
void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
{
iput(req->inode);
iput(req->inode2);
- if (req->file)
- fput(req->file);
spin_lock(&fc->lock);
list_del(&req->bg_entry);
if (fc->num_background == FUSE_MAX_BACKGROUND) {
@@ -170,6 +182,11 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
if (fc->mounted)
fuse_release_background(fc, req);
up_read(&fc->sbput_sem);
+
+ /* fput must go outside sbput_sem, otherwise it can deadlock */
+ if (req->file)
+ fput(req->file);
+
if (end)
end(fc, req);
else
@@ -277,6 +294,10 @@ 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);
}
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index e4f041a..fc342cf 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1,6 +1,6 @@
/*
FUSE: Filesystem in Userspace
- Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
@@ -565,8 +565,12 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
buf += nres;
if (nres != nbytes)
break;
- if (count)
- fuse_reset_request(req);
+ if (count) {
+ fuse_put_request(fc, req);
+ req = fuse_get_req(fc);
+ if (IS_ERR(req))
+ break;
+ }
}
fuse_put_request(fc, req);
if (res > 0) {
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 19c7185..0474202 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -159,6 +159,9 @@ struct fuse_req {
/** Data is being copied to/from the request */
unsigned locked:1;
+ /** Request is counted as "waiting" */
+ unsigned waiting:1;
+
/** State of the request */
enum fuse_req_state state;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index fd34037..7627022 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -500,11 +500,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
if (file->f_op != &fuse_dev_operations)
return -EINVAL;
- /* Setting file->private_data can't race with other mount()
- instances, since BKL is held for ->get_sb() */
- if (file->private_data)
- return -EINVAL;
-
fc = new_conn();
if (!fc)
return -ENOMEM;
@@ -540,6 +535,12 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
if (err)
goto err_free_req;
+ /* Setting file->private_data can't race with other mount()
+ instances, since BKL is held for ->get_sb() */
+ err = -EINVAL;
+ if (file->private_data)
+ goto err_kobject_del;
+
sb->s_root = root_dentry;
fc->mounted = 1;
fc->connected = 1;
@@ -556,6 +557,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
return 0;
+ err_kobject_del:
+ kobject_del(&fc->kobj);
err_free_req:
fuse_request_free(init_req);
err_put_root:
OpenPOWER on IntegriCloud