summaryrefslogtreecommitdiff
path: root/fs/fuse/dev.c
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-04-10 22:54:55 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-04-11 06:18:48 -0700
commit0720b315976447cba3f0c3e211223b8cb82b0f93 (patch)
treeb8013f53bca7a72670961ea6f439612d1c631283 /fs/fuse/dev.c
parente5ac1d1e70a8c19a65a959d73650203df7a2e168 (diff)
downloadlwn-0720b315976447cba3f0c3e211223b8cb82b0f93.tar.gz
lwn-0720b315976447cba3f0c3e211223b8cb82b0f93.zip
[PATCH] fuse: simplify locking
This is in preparation for removing the global spinlock in favor of a per-mount one. The only critical part is the interaction between fuse_dev_release() and fuse_fill_super(): fuse_dev_release() must see the assignment to file->private_data, otherwise it will leak the reference to fuse_conn. This is ensured by the fput() operation, which will synchronize the assignment with other CPU's that may do a final fput() soon after this. Also redundant locking is removed from fuse_fill_super(), where exclusion is already ensured by the BKL held for this function by the VFS. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r--fs/fuse/dev.c31
1 files changed, 11 insertions, 20 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 75c6e9166c39..c510533c6849 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -23,13 +23,11 @@ static kmem_cache_t *fuse_req_cachep;
static struct fuse_conn *fuse_get_conn(struct file *file)
{
- struct fuse_conn *fc;
- spin_lock(&fuse_lock);
- fc = file->private_data;
- if (fc && !fc->connected)
- fc = NULL;
- spin_unlock(&fuse_lock);
- return fc;
+ /*
+ * Lockless access is OK, because file->private data is set
+ * once during mount and is valid until the file is released.
+ */
+ return file->private_data;
}
static void fuse_request_init(struct fuse_req *req)
@@ -607,19 +605,16 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *off)
{
int err;
- struct fuse_conn *fc;
struct fuse_req *req;
struct fuse_in *in;
struct fuse_copy_state cs;
unsigned reqsize;
+ struct fuse_conn *fc = fuse_get_conn(file);
+ if (!fc)
+ return -EPERM;
restart:
spin_lock(&fuse_lock);
- fc = file->private_data;
- err = -EPERM;
- if (!fc)
- goto err_unlock;
-
err = -EAGAIN;
if ((file->f_flags & O_NONBLOCK) && fc->connected &&
list_empty(&fc->pending))
@@ -915,17 +910,13 @@ void fuse_abort_conn(struct fuse_conn *fc)
static int fuse_dev_release(struct inode *inode, struct file *file)
{
- struct fuse_conn *fc;
-
- spin_lock(&fuse_lock);
- fc = file->private_data;
+ struct fuse_conn *fc = fuse_get_conn(file);
if (fc) {
+ spin_lock(&fuse_lock);
fc->connected = 0;
end_requests(fc, &fc->pending);
end_requests(fc, &fc->processing);
- }
- spin_unlock(&fuse_lock);
- if (fc) {
+ spin_unlock(&fuse_lock);
fasync_helper(-1, file, 0, &fc->fasync);
kobject_put(&fc->kobj);
}