aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-04-11 01:54:55 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-04-11 09:18:48 -0400
commit0720b315976447cba3f0c3e211223b8cb82b0f93 (patch)
treeb8013f53bca7a72670961ea6f439612d1c631283 /fs
parente5ac1d1e70a8c19a65a959d73650203df7a2e168 (diff)
[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')
-rw-r--r--fs/fuse/dev.c31
-rw-r--r--fs/fuse/inode.c64
2 files changed, 33 insertions, 62 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;
23 23
24static struct fuse_conn *fuse_get_conn(struct file *file) 24static struct fuse_conn *fuse_get_conn(struct file *file)
25{ 25{
26 struct fuse_conn *fc; 26 /*
27 spin_lock(&fuse_lock); 27 * Lockless access is OK, because file->private data is set
28 fc = file->private_data; 28 * once during mount and is valid until the file is released.
29 if (fc && !fc->connected) 29 */
30 fc = NULL; 30 return file->private_data;
31 spin_unlock(&fuse_lock);
32 return fc;
33} 31}
34 32
35static void fuse_request_init(struct fuse_req *req) 33static 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,
607 unsigned long nr_segs, loff_t *off) 605 unsigned long nr_segs, loff_t *off)
608{ 606{
609 int err; 607 int err;
610 struct fuse_conn *fc;
611 struct fuse_req *req; 608 struct fuse_req *req;
612 struct fuse_in *in; 609 struct fuse_in *in;
613 struct fuse_copy_state cs; 610 struct fuse_copy_state cs;
614 unsigned reqsize; 611 unsigned reqsize;
612 struct fuse_conn *fc = fuse_get_conn(file);
613 if (!fc)
614 return -EPERM;
615 615
616 restart: 616 restart:
617 spin_lock(&fuse_lock); 617 spin_lock(&fuse_lock);
618 fc = file->private_data;
619 err = -EPERM;
620 if (!fc)
621 goto err_unlock;
622
623 err = -EAGAIN; 618 err = -EAGAIN;
624 if ((file->f_flags & O_NONBLOCK) && fc->connected && 619 if ((file->f_flags & O_NONBLOCK) && fc->connected &&
625 list_empty(&fc->pending)) 620 list_empty(&fc->pending))
@@ -915,17 +910,13 @@ void fuse_abort_conn(struct fuse_conn *fc)
915 910
916static int fuse_dev_release(struct inode *inode, struct file *file) 911static int fuse_dev_release(struct inode *inode, struct file *file)
917{ 912{
918 struct fuse_conn *fc; 913 struct fuse_conn *fc = fuse_get_conn(file);
919
920 spin_lock(&fuse_lock);
921 fc = file->private_data;
922 if (fc) { 914 if (fc) {
915 spin_lock(&fuse_lock);
923 fc->connected = 0; 916 fc->connected = 0;
924 end_requests(fc, &fc->pending); 917 end_requests(fc, &fc->pending);
925 end_requests(fc, &fc->processing); 918 end_requests(fc, &fc->processing);
926 } 919 spin_unlock(&fuse_lock);
927 spin_unlock(&fuse_lock);
928 if (fc) {
929 fasync_helper(-1, file, 0, &fc->fasync); 920 fasync_helper(-1, file, 0, &fc->fasync);
930 kobject_put(&fc->kobj); 921 kobject_put(&fc->kobj);
931 } 922 }
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 78700cbb9cdf..620579a69107 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -414,37 +414,6 @@ static struct fuse_conn *new_conn(void)
414 return fc; 414 return fc;
415} 415}
416 416
417static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
418{
419 struct fuse_conn *fc;
420 int err;
421
422 err = -EINVAL;
423 if (file->f_op != &fuse_dev_operations)
424 goto out_err;
425
426 err = -ENOMEM;
427 fc = new_conn();
428 if (!fc)
429 goto out_err;
430
431 spin_lock(&fuse_lock);
432 err = -EINVAL;
433 if (file->private_data)
434 goto out_unlock;
435
436 kobject_get(&fc->kobj);
437 file->private_data = fc;
438 spin_unlock(&fuse_lock);
439 return fc;
440
441 out_unlock:
442 spin_unlock(&fuse_lock);
443 kobject_put(&fc->kobj);
444 out_err:
445 return ERR_PTR(err);
446}
447
448static struct inode *get_root_inode(struct super_block *sb, unsigned mode) 417static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
449{ 418{
450 struct fuse_attr attr; 419 struct fuse_attr attr;
@@ -526,12 +495,9 @@ static void fuse_send_init(struct fuse_conn *fc)
526 495
527static unsigned long long conn_id(void) 496static unsigned long long conn_id(void)
528{ 497{
498 /* BKL is held for ->get_sb() */
529 static unsigned long long ctr = 1; 499 static unsigned long long ctr = 1;
530 unsigned long long val; 500 return ctr++;
531 spin_lock(&fuse_lock);
532 val = ctr++;
533 spin_unlock(&fuse_lock);
534 return val;
535} 501}
536 502
537static int fuse_fill_super(struct super_block *sb, void *data, int silent) 503static int fuse_fill_super(struct super_block *sb, void *data, int silent)
@@ -556,10 +522,17 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
556 if (!file) 522 if (!file)
557 return -EINVAL; 523 return -EINVAL;
558 524
559 fc = get_conn(file, sb); 525 if (file->f_op != &fuse_dev_operations)
560 fput(file); 526 return -EINVAL;
561 if (IS_ERR(fc)) 527
562 return PTR_ERR(fc); 528 /* Setting file->private_data can't race with other mount()
529 instances, since BKL is held for ->get_sb() */
530 if (file->private_data)
531 return -EINVAL;
532
533 fc = new_conn();
534 if (!fc)
535 return -ENOMEM;
563 536
564 fc->flags = d.flags; 537 fc->flags = d.flags;
565 fc->user_id = d.user_id; 538 fc->user_id = d.user_id;
@@ -589,10 +562,16 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
589 goto err_put_root; 562 goto err_put_root;
590 563
591 sb->s_root = root_dentry; 564 sb->s_root = root_dentry;
592 spin_lock(&fuse_lock);
593 fc->mounted = 1; 565 fc->mounted = 1;
594 fc->connected = 1; 566 fc->connected = 1;
595 spin_unlock(&fuse_lock); 567 kobject_get(&fc->kobj);
568 file->private_data = fc;
569 /*
570 * atomic_dec_and_test() in fput() provides the necessary
571 * memory barrier for file->private_data to be visible on all
572 * CPUs after this
573 */
574 fput(file);
596 575
597 fuse_send_init(fc); 576 fuse_send_init(fc);
598 577
@@ -601,6 +580,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
601 err_put_root: 580 err_put_root:
602 dput(root_dentry); 581 dput(root_dentry);
603 err: 582 err:
583 fput(file);
604 kobject_put(&fc->kobj); 584 kobject_put(&fc->kobj);
605 return err; 585 return err;
606} 586}