summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-04-14 09:11:34 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-04-14 09:11:34 -0700
commit9a7e9f1c60bada782014b2f422f6c68c4d5388f3 (patch)
tree7e874364f448e050edd6058c50e1e70346b99696
parent9ca686626c012935b3b8219815e9027facc6fe6e (diff)
parent56cf34ff0795692327234963dcdcc2cdeec2bb3d (diff)
Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/mszeredi/fuse
* 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/mszeredi/fuse: [fuse] Direct I/O should not use fuse_reset_request [fuse] Don't init request twice [fuse] Fix accounting the number of waiting requests [fuse] fix deadlock between fuse_put_super() and request_end()
-rw-r--r--fs/fuse/dev.c54
-rw-r--r--fs/fuse/file.c10
-rw-r--r--fs/fuse/fuse_i.h15
-rw-r--r--fs/fuse/inode.c27
4 files changed, 65 insertions, 41 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 6c740f860665..cc750c68fe70 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -92,48 +92,50 @@ 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);
}
}
-void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_remove_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);
+ list_del_init(&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);
}
/*
@@ -163,17 +165,27 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
wake_up(&req->waitq);
fuse_put_request(fc, req);
} else {
+ struct inode *inode = req->inode;
+ struct inode *inode2 = req->inode2;
+ struct file *file = req->file;
void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
req->end = NULL;
+ req->inode = NULL;
+ req->inode2 = NULL;
+ req->file = NULL;
+ if (!list_empty(&req->bg_entry))
+ fuse_remove_background(fc, req);
spin_unlock(&fc->lock);
- down_read(&fc->sbput_sem);
- if (fc->mounted)
- fuse_release_background(fc, req);
- up_read(&fc->sbput_sem);
+
if (end)
end(fc, req);
else
fuse_put_request(fc, req);
+
+ if (file)
+ fput(file);
+ iput(inode);
+ iput(inode2);
}
}
@@ -277,6 +289,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 e4f041a11bb5..fc342cf7c2cc 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 19c7185a7546..59661c481d9d 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;
@@ -255,15 +258,9 @@ struct fuse_conn {
/** waitq for blocked connection */
wait_queue_head_t blocked_waitq;
- /** RW semaphore for exclusion with fuse_put_super() */
- struct rw_semaphore sbput_sem;
-
/** The next unique request id */
u64 reqctr;
- /** Mount is active */
- unsigned mounted;
-
/** Connection established, cleared on umount, connection
abort and device release */
unsigned connected;
@@ -474,11 +471,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
/**
- * Release inodes and file associated with background request
+ * Remove request from the the background list
*/
-void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req);
+void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req);
-/* Abort all requests */
+/** Abort all requests */
void fuse_abort_conn(struct fuse_conn *fc);
/**
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index fd34037b0588..43a6fc0db8a7 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -204,17 +204,26 @@ static void fuse_put_super(struct super_block *sb)
{
struct fuse_conn *fc = get_fuse_conn_super(sb);
- down_write(&fc->sbput_sem);
- while (!list_empty(&fc->background))
- fuse_release_background(fc,
- list_entry(fc->background.next,
- struct fuse_req, bg_entry));
-
spin_lock(&fc->lock);
- fc->mounted = 0;
fc->connected = 0;
+ while (!list_empty(&fc->background)) {
+ struct fuse_req *req = list_entry(fc->background.next,
+ struct fuse_req, bg_entry);
+ struct inode *inode = req->inode;
+ struct inode *inode2 = req->inode2;
+
+ /* File would hold a reference to vfsmount */
+ BUG_ON(req->file);
+ req->inode = NULL;
+ req->inode2 = NULL;
+ fuse_remove_background(fc, req);
+
+ spin_unlock(&fc->lock);
+ iput(inode);
+ iput(inode2);
+ spin_lock(&fc->lock);
+ }
spin_unlock(&fc->lock);
- up_write(&fc->sbput_sem);
/* Flush all readers on this fs */
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
wake_up_all(&fc->waitq);
@@ -386,7 +395,6 @@ static struct fuse_conn *new_conn(void)
INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->io);
INIT_LIST_HEAD(&fc->background);
- init_rwsem(&fc->sbput_sem);
kobj_set_kset_s(fc, connections_subsys);
kobject_init(&fc->kobj);
atomic_set(&fc->num_waiting, 0);
@@ -541,7 +549,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
goto err_free_req;
sb->s_root = root_dentry;
- fc->mounted = 1;
fc->connected = 1;
kobject_get(&fc->kobj);
file->private_data = fc;