diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2006-04-11 01:54:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-11 09:18:48 -0400 |
commit | 0720b315976447cba3f0c3e211223b8cb82b0f93 (patch) | |
tree | b8013f53bca7a72670961ea6f439612d1c631283 /fs/fuse | |
parent | e5ac1d1e70a8c19a65a959d73650203df7a2e168 (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/fuse')
-rw-r--r-- | fs/fuse/dev.c | 31 | ||||
-rw-r--r-- | fs/fuse/inode.c | 64 |
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 | ||
24 | static struct fuse_conn *fuse_get_conn(struct file *file) | 24 | static 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 | ||
35 | static void fuse_request_init(struct fuse_req *req) | 33 | 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, | |||
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 | ||
916 | static int fuse_dev_release(struct inode *inode, struct file *file) | 911 | static 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 | ||
417 | static 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 | |||
448 | static struct inode *get_root_inode(struct super_block *sb, unsigned mode) | 417 | static 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 | ||
527 | static unsigned long long conn_id(void) | 496 | static 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 | ||
537 | static int fuse_fill_super(struct super_block *sb, void *data, int silent) | 503 | static 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 | } |