diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2011-01-14 22:30:21 -0500 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-01-16 13:47:07 -0500 |
| commit | f03c65993b98eeb909a4012ce7833c5857d74755 (patch) | |
| tree | a6dd5e353889b7fe4ab87c54170d09443d788fec | |
| parent | 7b8a53fd815deb39542085897743fa0063f9fe06 (diff) | |
sanitize vfsmount refcounting changes
Instead of splitting refcount between (per-cpu) mnt_count
and (SMP-only) mnt_longrefs, make all references contribute
to mnt_count again and keep track of how many are longterm
ones.
Accounting rules for longterm count:
* 1 for each fs_struct.root.mnt
* 1 for each fs_struct.pwd.mnt
* 1 for having non-NULL ->mnt_ns
* decrement to 0 happens only under vfsmount lock exclusive
That allows nice common case for mntput() - since we can't drop the
final reference until after mnt_longterm has reached 0 due to the rules
above, mntput() can grab vfsmount lock shared and check mnt_longterm.
If it turns out to be non-zero (which is the common case), we know
that this is not the final mntput() and can just blindly decrement
percpu mnt_count. Otherwise we grab vfsmount lock exclusive and
do usual decrement-and-check of percpu mnt_count.
For fs_struct.c we have mnt_make_longterm() and mnt_make_shortterm();
namespace.c uses the latter in places where we don't already hold
vfsmount lock exclusive and opencodes a few remaining spots where
we need to manipulate mnt_longterm.
Note that we mostly revert the code outside of fs/namespace.c back
to what we used to have; in particular, normal code doesn't need
to care about two kinds of references, etc. And we get to keep
the optimization Nick's variant had bought us...
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | drivers/mtd/mtdchar.c | 2 | ||||
| -rw-r--r-- | fs/anon_inodes.c | 2 | ||||
| -rw-r--r-- | fs/fs_struct.c | 35 | ||||
| -rw-r--r-- | fs/internal.h | 3 | ||||
| -rw-r--r-- | fs/namei.c | 24 | ||||
| -rw-r--r-- | fs/namespace.c | 116 | ||||
| -rw-r--r-- | fs/pipe.c | 2 | ||||
| -rw-r--r-- | fs/super.c | 2 | ||||
| -rw-r--r-- | include/linux/mount.h | 4 | ||||
| -rw-r--r-- | include/linux/path.h | 2 |
10 files changed, 75 insertions, 117 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index ee4bb3330bdf..98240575a18d 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
| @@ -1201,7 +1201,7 @@ err_unregister_chdev: | |||
| 1201 | static void __exit cleanup_mtdchar(void) | 1201 | static void __exit cleanup_mtdchar(void) |
| 1202 | { | 1202 | { |
| 1203 | unregister_mtd_user(&mtdchar_notifier); | 1203 | unregister_mtd_user(&mtdchar_notifier); |
| 1204 | mntput_long(mtd_inode_mnt); | 1204 | mntput(mtd_inode_mnt); |
| 1205 | unregister_filesystem(&mtd_inodefs_type); | 1205 | unregister_filesystem(&mtd_inodefs_type); |
| 1206 | __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); | 1206 | __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); |
| 1207 | } | 1207 | } |
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index cbe57f3c4d89..c5567cb78432 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c | |||
| @@ -233,7 +233,7 @@ static int __init anon_inode_init(void) | |||
| 233 | return 0; | 233 | return 0; |
| 234 | 234 | ||
| 235 | err_mntput: | 235 | err_mntput: |
| 236 | mntput_long(anon_inode_mnt); | 236 | mntput(anon_inode_mnt); |
| 237 | err_unregister_filesystem: | 237 | err_unregister_filesystem: |
| 238 | unregister_filesystem(&anon_inode_fs_type); | 238 | unregister_filesystem(&anon_inode_fs_type); |
| 239 | err_exit: | 239 | err_exit: |
diff --git a/fs/fs_struct.c b/fs/fs_struct.c index 68ca487bedb1..78b519c13536 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c | |||
| @@ -4,6 +4,19 @@ | |||
| 4 | #include <linux/path.h> | 4 | #include <linux/path.h> |
| 5 | #include <linux/slab.h> | 5 | #include <linux/slab.h> |
| 6 | #include <linux/fs_struct.h> | 6 | #include <linux/fs_struct.h> |
| 7 | #include "internal.h" | ||
| 8 | |||
| 9 | static inline void path_get_longterm(struct path *path) | ||
| 10 | { | ||
| 11 | path_get(path); | ||
| 12 | mnt_make_longterm(path->mnt); | ||
| 13 | } | ||
| 14 | |||
| 15 | static inline void path_put_longterm(struct path *path) | ||
| 16 | { | ||
| 17 | mnt_make_shortterm(path->mnt); | ||
| 18 | path_put(path); | ||
| 19 | } | ||
| 7 | 20 | ||
| 8 | /* | 21 | /* |
| 9 | * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values. | 22 | * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values. |
| @@ -17,11 +30,11 @@ void set_fs_root(struct fs_struct *fs, struct path *path) | |||
| 17 | write_seqcount_begin(&fs->seq); | 30 | write_seqcount_begin(&fs->seq); |
| 18 | old_root = fs->root; | 31 | old_root = fs->root; |
| 19 | fs->root = *path; | 32 | fs->root = *path; |
| 20 | path_get_long(path); | 33 | path_get_longterm(path); |
| 21 | write_seqcount_end(&fs->seq); | 34 | write_seqcount_end(&fs->seq); |
| 22 | spin_unlock(&fs->lock); | 35 | spin_unlock(&fs->lock); |
| 23 | if (old_root.dentry) | 36 | if (old_root.dentry) |
| 24 | path_put_long(&old_root); | 37 | path_put_longterm(&old_root); |
| 25 | } | 38 | } |
| 26 | 39 | ||
| 27 | /* | 40 | /* |
| @@ -36,12 +49,12 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path) | |||
| 36 | write_seqcount_begin(&fs->seq); | 49 | write_seqcount_begin(&fs->seq); |
| 37 | old_pwd = fs->pwd; | 50 | old_pwd = fs->pwd; |
| 38 | fs->pwd = *path; | 51 | fs->pwd = *path; |
| 39 | path_get_long(path); | 52 | path_get_longterm(path); |
| 40 | write_seqcount_end(&fs->seq); | 53 | write_seqcount_end(&fs->seq); |
| 41 | spin_unlock(&fs->lock); | 54 | spin_unlock(&fs->lock); |
| 42 | 55 | ||
| 43 | if (old_pwd.dentry) | 56 | if (old_pwd.dentry) |
| 44 | path_put_long(&old_pwd); | 57 | path_put_longterm(&old_pwd); |
| 45 | } | 58 | } |
| 46 | 59 | ||
| 47 | void chroot_fs_refs(struct path *old_root, struct path *new_root) | 60 | void chroot_fs_refs(struct path *old_root, struct path *new_root) |
| @@ -59,13 +72,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root) | |||
| 59 | write_seqcount_begin(&fs->seq); | 72 | write_seqcount_begin(&fs->seq); |
| 60 | if (fs->root.dentry == old_root->dentry | 73 | if (fs->root.dentry == old_root->dentry |
| 61 | && fs->root.mnt == old_root->mnt) { | 74 | && fs->root.mnt == old_root->mnt) { |
| 62 | path_get_long(new_root); | 75 | path_get_longterm(new_root); |
| 63 | fs->root = *new_root; | 76 | fs->root = *new_root; |
| 64 | count++; | 77 | count++; |
| 65 | } | 78 | } |
| 66 | if (fs->pwd.dentry == old_root->dentry | 79 | if (fs->pwd.dentry == old_root->dentry |
| 67 | && fs->pwd.mnt == old_root->mnt) { | 80 | && fs->pwd.mnt == old_root->mnt) { |
| 68 | path_get_long(new_root); | 81 | path_get_longterm(new_root); |
| 69 | fs->pwd = *new_root; | 82 | fs->pwd = *new_root; |
| 70 | count++; | 83 | count++; |
| 71 | } | 84 | } |
| @@ -76,13 +89,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root) | |||
| 76 | } while_each_thread(g, p); | 89 | } while_each_thread(g, p); |
| 77 | read_unlock(&tasklist_lock); | 90 | read_unlock(&tasklist_lock); |
| 78 | while (count--) | 91 | while (count--) |
| 79 | path_put_long(old_root); | 92 | path_put_longterm(old_root); |
| 80 | } | 93 | } |
| 81 | 94 | ||
| 82 | void free_fs_struct(struct fs_struct *fs) | 95 | void free_fs_struct(struct fs_struct *fs) |
| 83 | { | 96 | { |
| 84 | path_put_long(&fs->root); | 97 | path_put_longterm(&fs->root); |
| 85 | path_put_long(&fs->pwd); | 98 | path_put_longterm(&fs->pwd); |
| 86 | kmem_cache_free(fs_cachep, fs); | 99 | kmem_cache_free(fs_cachep, fs); |
| 87 | } | 100 | } |
| 88 | 101 | ||
| @@ -118,9 +131,9 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) | |||
| 118 | 131 | ||
| 119 | spin_lock(&old->lock); | 132 | spin_lock(&old->lock); |
| 120 | fs->root = old->root; | 133 | fs->root = old->root; |
| 121 | path_get_long(&fs->root); | 134 | path_get_longterm(&fs->root); |
| 122 | fs->pwd = old->pwd; | 135 | fs->pwd = old->pwd; |
| 123 | path_get_long(&fs->pwd); | 136 | path_get_longterm(&fs->pwd); |
| 124 | spin_unlock(&old->lock); | 137 | spin_unlock(&old->lock); |
| 125 | } | 138 | } |
| 126 | return fs; | 139 | return fs; |
diff --git a/fs/internal.h b/fs/internal.h index 4931060fd089..12ccb86edef7 100644 --- a/fs/internal.h +++ b/fs/internal.h | |||
| @@ -73,6 +73,9 @@ extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); | |||
| 73 | extern int do_add_mount(struct vfsmount *, struct path *, int); | 73 | extern int do_add_mount(struct vfsmount *, struct path *, int); |
| 74 | extern void mnt_clear_expiry(struct vfsmount *); | 74 | extern void mnt_clear_expiry(struct vfsmount *); |
| 75 | 75 | ||
| 76 | extern void mnt_make_longterm(struct vfsmount *); | ||
| 77 | extern void mnt_make_shortterm(struct vfsmount *); | ||
| 78 | |||
| 76 | extern void __init mnt_init(void); | 79 | extern void __init mnt_init(void); |
| 77 | 80 | ||
| 78 | DECLARE_BRLOCK(vfsmount_lock); | 81 | DECLARE_BRLOCK(vfsmount_lock); |
diff --git a/fs/namei.c b/fs/namei.c index c2e37727e3ab..8f7b41a14882 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -368,18 +368,6 @@ void path_get(struct path *path) | |||
| 368 | EXPORT_SYMBOL(path_get); | 368 | EXPORT_SYMBOL(path_get); |
| 369 | 369 | ||
| 370 | /** | 370 | /** |
| 371 | * path_get_long - get a long reference to a path | ||
| 372 | * @path: path to get the reference to | ||
| 373 | * | ||
| 374 | * Given a path increment the reference count to the dentry and the vfsmount. | ||
| 375 | */ | ||
| 376 | void path_get_long(struct path *path) | ||
| 377 | { | ||
| 378 | mntget_long(path->mnt); | ||
| 379 | dget(path->dentry); | ||
| 380 | } | ||
| 381 | |||
| 382 | /** | ||
| 383 | * path_put - put a reference to a path | 371 | * path_put - put a reference to a path |
| 384 | * @path: path to put the reference to | 372 | * @path: path to put the reference to |
| 385 | * | 373 | * |
| @@ -393,18 +381,6 @@ void path_put(struct path *path) | |||
| 393 | EXPORT_SYMBOL(path_put); | 381 | EXPORT_SYMBOL(path_put); |
| 394 | 382 | ||
| 395 | /** | 383 | /** |
| 396 | * path_put_long - put a long reference to a path | ||
| 397 | * @path: path to put the reference to | ||
| 398 | * | ||
| 399 | * Given a path decrement the reference count to the dentry and the vfsmount. | ||
| 400 | */ | ||
| 401 | void path_put_long(struct path *path) | ||
| 402 | { | ||
| 403 | dput(path->dentry); | ||
| 404 | mntput_long(path->mnt); | ||
| 405 | } | ||
| 406 | |||
| 407 | /** | ||
| 408 | * nameidata_drop_rcu - drop this nameidata out of rcu-walk | 384 | * nameidata_drop_rcu - drop this nameidata out of rcu-walk |
| 409 | * @nd: nameidata pathwalk data to drop | 385 | * @nd: nameidata pathwalk data to drop |
| 410 | * Returns: 0 on success, -ECHILD on failure | 386 | * Returns: 0 on success, -ECHILD on failure |
diff --git a/fs/namespace.c b/fs/namespace.c index d7fc05fac753..48809e21f270 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
| @@ -183,7 +183,7 @@ static inline void mnt_dec_count(struct vfsmount *mnt) | |||
| 183 | unsigned int mnt_get_count(struct vfsmount *mnt) | 183 | unsigned int mnt_get_count(struct vfsmount *mnt) |
| 184 | { | 184 | { |
| 185 | #ifdef CONFIG_SMP | 185 | #ifdef CONFIG_SMP |
| 186 | unsigned int count = atomic_read(&mnt->mnt_longrefs); | 186 | unsigned int count = 0; |
| 187 | int cpu; | 187 | int cpu; |
| 188 | 188 | ||
| 189 | for_each_possible_cpu(cpu) { | 189 | for_each_possible_cpu(cpu) { |
| @@ -217,7 +217,7 @@ struct vfsmount *alloc_vfsmnt(const char *name) | |||
| 217 | if (!mnt->mnt_pcp) | 217 | if (!mnt->mnt_pcp) |
| 218 | goto out_free_devname; | 218 | goto out_free_devname; |
| 219 | 219 | ||
| 220 | atomic_set(&mnt->mnt_longrefs, 1); | 220 | this_cpu_add(mnt->mnt_pcp->mnt_count, 1); |
| 221 | #else | 221 | #else |
| 222 | mnt->mnt_count = 1; | 222 | mnt->mnt_count = 1; |
| 223 | mnt->mnt_writers = 0; | 223 | mnt->mnt_writers = 0; |
| @@ -624,8 +624,11 @@ static void commit_tree(struct vfsmount *mnt) | |||
| 624 | BUG_ON(parent == mnt); | 624 | BUG_ON(parent == mnt); |
| 625 | 625 | ||
| 626 | list_add_tail(&head, &mnt->mnt_list); | 626 | list_add_tail(&head, &mnt->mnt_list); |
| 627 | list_for_each_entry(m, &head, mnt_list) | 627 | list_for_each_entry(m, &head, mnt_list) { |
| 628 | m->mnt_ns = n; | 628 | m->mnt_ns = n; |
| 629 | atomic_inc(&m->mnt_longterm); | ||
| 630 | } | ||
| 631 | |||
| 629 | list_splice(&head, n->list.prev); | 632 | list_splice(&head, n->list.prev); |
| 630 | 633 | ||
| 631 | list_add_tail(&mnt->mnt_hash, mount_hashtable + | 634 | list_add_tail(&mnt->mnt_hash, mount_hashtable + |
| @@ -734,51 +737,30 @@ static inline void mntfree(struct vfsmount *mnt) | |||
| 734 | deactivate_super(sb); | 737 | deactivate_super(sb); |
| 735 | } | 738 | } |
| 736 | 739 | ||
| 737 | #ifdef CONFIG_SMP | 740 | static void mntput_no_expire(struct vfsmount *mnt) |
| 738 | static inline void __mntput(struct vfsmount *mnt, int longrefs) | ||
| 739 | { | 741 | { |
| 740 | if (!longrefs) { | ||
| 741 | put_again: | 742 | put_again: |
| 742 | br_read_lock(vfsmount_lock); | 743 | #ifdef CONFIG_SMP |
| 743 | if (likely(atomic_read(&mnt->mnt_longrefs))) { | 744 | br_read_lock(vfsmount_lock); |
| 744 | mnt_dec_count(mnt); | 745 | if (likely(atomic_read(&mnt->mnt_longterm))) { |
| 745 | br_read_unlock(vfsmount_lock); | 746 | mnt_dec_count(mnt); |
| 746 | return; | ||
| 747 | } | ||
| 748 | br_read_unlock(vfsmount_lock); | 747 | br_read_unlock(vfsmount_lock); |
| 749 | } else { | 748 | return; |
| 750 | BUG_ON(!atomic_read(&mnt->mnt_longrefs)); | ||
| 751 | if (atomic_add_unless(&mnt->mnt_longrefs, -1, 1)) | ||
| 752 | return; | ||
| 753 | } | 749 | } |
| 750 | br_read_unlock(vfsmount_lock); | ||
| 754 | 751 | ||
| 755 | br_write_lock(vfsmount_lock); | 752 | br_write_lock(vfsmount_lock); |
| 756 | if (!longrefs) | 753 | mnt_dec_count(mnt); |
| 757 | mnt_dec_count(mnt); | ||
| 758 | else | ||
| 759 | atomic_dec(&mnt->mnt_longrefs); | ||
| 760 | if (mnt_get_count(mnt)) { | 754 | if (mnt_get_count(mnt)) { |
| 761 | br_write_unlock(vfsmount_lock); | 755 | br_write_unlock(vfsmount_lock); |
| 762 | return; | 756 | return; |
| 763 | } | 757 | } |
| 764 | if (unlikely(mnt->mnt_pinned)) { | ||
| 765 | mnt_add_count(mnt, mnt->mnt_pinned + 1); | ||
| 766 | mnt->mnt_pinned = 0; | ||
| 767 | br_write_unlock(vfsmount_lock); | ||
| 768 | acct_auto_close_mnt(mnt); | ||
| 769 | goto put_again; | ||
| 770 | } | ||
| 771 | br_write_unlock(vfsmount_lock); | ||
| 772 | mntfree(mnt); | ||
| 773 | } | ||
| 774 | #else | 758 | #else |
| 775 | static inline void __mntput(struct vfsmount *mnt, int longrefs) | ||
| 776 | { | ||
| 777 | put_again: | ||
| 778 | mnt_dec_count(mnt); | 759 | mnt_dec_count(mnt); |
| 779 | if (likely(mnt_get_count(mnt))) | 760 | if (likely(mnt_get_count(mnt))) |
| 780 | return; | 761 | return; |
| 781 | br_write_lock(vfsmount_lock); | 762 | br_write_lock(vfsmount_lock); |
| 763 | #endif | ||
| 782 | if (unlikely(mnt->mnt_pinned)) { | 764 | if (unlikely(mnt->mnt_pinned)) { |
| 783 | mnt_add_count(mnt, mnt->mnt_pinned + 1); | 765 | mnt_add_count(mnt, mnt->mnt_pinned + 1); |
| 784 | mnt->mnt_pinned = 0; | 766 | mnt->mnt_pinned = 0; |
| @@ -789,12 +771,6 @@ put_again: | |||
| 789 | br_write_unlock(vfsmount_lock); | 771 | br_write_unlock(vfsmount_lock); |
| 790 | mntfree(mnt); | 772 | mntfree(mnt); |
| 791 | } | 773 | } |
| 792 | #endif | ||
| 793 | |||
| 794 | static void mntput_no_expire(struct vfsmount *mnt) | ||
| 795 | { | ||
| 796 | __mntput(mnt, 0); | ||
| 797 | } | ||
| 798 | 774 | ||
| 799 | void mntput(struct vfsmount *mnt) | 775 | void mntput(struct vfsmount *mnt) |
| 800 | { | 776 | { |
| @@ -802,7 +778,7 @@ void mntput(struct vfsmount *mnt) | |||
| 802 | /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ | 778 | /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ |
| 803 | if (unlikely(mnt->mnt_expiry_mark)) | 779 | if (unlikely(mnt->mnt_expiry_mark)) |
| 804 | mnt->mnt_expiry_mark = 0; | 780 | mnt->mnt_expiry_mark = 0; |
| 805 | __mntput(mnt, 0); | 781 | mntput_no_expire(mnt); |
| 806 | } | 782 | } |
| 807 | } | 783 | } |
| 808 | EXPORT_SYMBOL(mntput); | 784 | EXPORT_SYMBOL(mntput); |
| @@ -815,33 +791,6 @@ struct vfsmount *mntget(struct vfsmount *mnt) | |||
| 815 | } | 791 | } |
| 816 | EXPORT_SYMBOL(mntget); | 792 | EXPORT_SYMBOL(mntget); |
| 817 | 793 | ||
| 818 | void mntput_long(struct vfsmount *mnt) | ||
| 819 | { | ||
| 820 | #ifdef CONFIG_SMP | ||
| 821 | if (mnt) { | ||
| 822 | /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ | ||
| 823 | if (unlikely(mnt->mnt_expiry_mark)) | ||
| 824 | mnt->mnt_expiry_mark = 0; | ||
| 825 | __mntput(mnt, 1); | ||
| 826 | } | ||
| 827 | #else | ||
| 828 | mntput(mnt); | ||
| 829 | #endif | ||
| 830 | } | ||
| 831 | EXPORT_SYMBOL(mntput_long); | ||
| 832 | |||
| 833 | struct vfsmount *mntget_long(struct vfsmount *mnt) | ||
| 834 | { | ||
| 835 | #ifdef CONFIG_SMP | ||
| 836 | if (mnt) | ||
| 837 | atomic_inc(&mnt->mnt_longrefs); | ||
| 838 | return mnt; | ||
| 839 | #else | ||
| 840 | return mntget(mnt); | ||
| 841 | #endif | ||
| 842 | } | ||
| 843 | EXPORT_SYMBOL(mntget_long); | ||
| 844 | |||
| 845 | void mnt_pin(struct vfsmount *mnt) | 794 | void mnt_pin(struct vfsmount *mnt) |
| 846 | { | 795 | { |
| 847 | br_write_lock(vfsmount_lock); | 796 | br_write_lock(vfsmount_lock); |
| @@ -1216,7 +1165,7 @@ void release_mounts(struct list_head *head) | |||
| 1216 | dput(dentry); | 1165 | dput(dentry); |
| 1217 | mntput(m); | 1166 | mntput(m); |
| 1218 | } | 1167 | } |
| 1219 | mntput_long(mnt); | 1168 | mntput(mnt); |
| 1220 | } | 1169 | } |
| 1221 | } | 1170 | } |
| 1222 | 1171 | ||
| @@ -1240,6 +1189,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) | |||
| 1240 | list_del_init(&p->mnt_list); | 1189 | list_del_init(&p->mnt_list); |
| 1241 | __touch_mnt_namespace(p->mnt_ns); | 1190 | __touch_mnt_namespace(p->mnt_ns); |
| 1242 | p->mnt_ns = NULL; | 1191 | p->mnt_ns = NULL; |
| 1192 | atomic_dec(&p->mnt_longterm); | ||
| 1243 | list_del_init(&p->mnt_child); | 1193 | list_del_init(&p->mnt_child); |
| 1244 | if (p->mnt_parent != p) { | 1194 | if (p->mnt_parent != p) { |
| 1245 | p->mnt_parent->mnt_ghosts++; | 1195 | p->mnt_parent->mnt_ghosts++; |
| @@ -1969,7 +1919,7 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags) | |||
| 1969 | 1919 | ||
| 1970 | unlock: | 1920 | unlock: |
| 1971 | up_write(&namespace_sem); | 1921 | up_write(&namespace_sem); |
| 1972 | mntput_long(newmnt); | 1922 | mntput(newmnt); |
| 1973 | return err; | 1923 | return err; |
| 1974 | } | 1924 | } |
| 1975 | 1925 | ||
| @@ -2291,6 +2241,20 @@ static struct mnt_namespace *alloc_mnt_ns(void) | |||
| 2291 | return new_ns; | 2241 | return new_ns; |
| 2292 | } | 2242 | } |
| 2293 | 2243 | ||
| 2244 | void mnt_make_longterm(struct vfsmount *mnt) | ||
| 2245 | { | ||
| 2246 | atomic_inc(&mnt->mnt_longterm); | ||
| 2247 | } | ||
| 2248 | |||
| 2249 | void mnt_make_shortterm(struct vfsmount *mnt) | ||
| 2250 | { | ||
| 2251 | if (atomic_add_unless(&mnt->mnt_longterm, -1, 1)) | ||
| 2252 | return; | ||
| 2253 | br_write_lock(vfsmount_lock); | ||
| 2254 | atomic_dec(&mnt->mnt_longterm); | ||
| 2255 | br_write_unlock(vfsmount_lock); | ||
| 2256 | } | ||
| 2257 | |||
| 2294 | /* | 2258 | /* |
| 2295 | * Allocate a new namespace structure and populate it with contents | 2259 | * Allocate a new namespace structure and populate it with contents |
| 2296 | * copied from the namespace of the passed in task structure. | 2260 | * copied from the namespace of the passed in task structure. |
| @@ -2328,14 +2292,19 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, | |||
| 2328 | q = new_ns->root; | 2292 | q = new_ns->root; |
| 2329 | while (p) { | 2293 | while (p) { |
| 2330 | q->mnt_ns = new_ns; | 2294 | q->mnt_ns = new_ns; |
| 2295 | atomic_inc(&q->mnt_longterm); | ||
| 2331 | if (fs) { | 2296 | if (fs) { |
| 2332 | if (p == fs->root.mnt) { | 2297 | if (p == fs->root.mnt) { |
| 2298 | fs->root.mnt = mntget(q); | ||
| 2299 | atomic_inc(&q->mnt_longterm); | ||
| 2300 | mnt_make_shortterm(p); | ||
| 2333 | rootmnt = p; | 2301 | rootmnt = p; |
| 2334 | fs->root.mnt = mntget_long(q); | ||
| 2335 | } | 2302 | } |
| 2336 | if (p == fs->pwd.mnt) { | 2303 | if (p == fs->pwd.mnt) { |
| 2304 | fs->pwd.mnt = mntget(q); | ||
| 2305 | atomic_inc(&q->mnt_longterm); | ||
| 2306 | mnt_make_shortterm(p); | ||
| 2337 | pwdmnt = p; | 2307 | pwdmnt = p; |
| 2338 | fs->pwd.mnt = mntget_long(q); | ||
| 2339 | } | 2308 | } |
| 2340 | } | 2309 | } |
| 2341 | p = next_mnt(p, mnt_ns->root); | 2310 | p = next_mnt(p, mnt_ns->root); |
| @@ -2344,9 +2313,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, | |||
| 2344 | up_write(&namespace_sem); | 2313 | up_write(&namespace_sem); |
| 2345 | 2314 | ||
| 2346 | if (rootmnt) | 2315 | if (rootmnt) |
| 2347 | mntput_long(rootmnt); | 2316 | mntput(rootmnt); |
| 2348 | if (pwdmnt) | 2317 | if (pwdmnt) |
| 2349 | mntput_long(pwdmnt); | 2318 | mntput(pwdmnt); |
| 2350 | 2319 | ||
| 2351 | return new_ns; | 2320 | return new_ns; |
| 2352 | } | 2321 | } |
| @@ -2379,6 +2348,7 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt) | |||
| 2379 | new_ns = alloc_mnt_ns(); | 2348 | new_ns = alloc_mnt_ns(); |
| 2380 | if (!IS_ERR(new_ns)) { | 2349 | if (!IS_ERR(new_ns)) { |
| 2381 | mnt->mnt_ns = new_ns; | 2350 | mnt->mnt_ns = new_ns; |
| 2351 | atomic_inc(&mnt->mnt_longterm); | ||
| 2382 | new_ns->root = mnt; | 2352 | new_ns->root = mnt; |
| 2383 | list_add(&new_ns->list, &new_ns->root->mnt_list); | 2353 | list_add(&new_ns->list, &new_ns->root->mnt_list); |
| 2384 | } | 2354 | } |
| @@ -1292,7 +1292,7 @@ static int __init init_pipe_fs(void) | |||
| 1292 | static void __exit exit_pipe_fs(void) | 1292 | static void __exit exit_pipe_fs(void) |
| 1293 | { | 1293 | { |
| 1294 | unregister_filesystem(&pipe_fs_type); | 1294 | unregister_filesystem(&pipe_fs_type); |
| 1295 | mntput_long(pipe_mnt); | 1295 | mntput(pipe_mnt); |
| 1296 | } | 1296 | } |
| 1297 | 1297 | ||
| 1298 | fs_initcall(init_pipe_fs); | 1298 | fs_initcall(init_pipe_fs); |
diff --git a/fs/super.c b/fs/super.c index 4f6a3571a634..74e149efed81 100644 --- a/fs/super.c +++ b/fs/super.c | |||
| @@ -1141,7 +1141,7 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype) | |||
| 1141 | return mnt; | 1141 | return mnt; |
| 1142 | 1142 | ||
| 1143 | err: | 1143 | err: |
| 1144 | mntput_long(mnt); | 1144 | mntput(mnt); |
| 1145 | return ERR_PTR(err); | 1145 | return ERR_PTR(err); |
| 1146 | } | 1146 | } |
| 1147 | 1147 | ||
diff --git a/include/linux/mount.h b/include/linux/mount.h index af4765ea8fde..604f122a2326 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h | |||
| @@ -60,7 +60,7 @@ struct vfsmount { | |||
| 60 | struct super_block *mnt_sb; /* pointer to superblock */ | 60 | struct super_block *mnt_sb; /* pointer to superblock */ |
| 61 | #ifdef CONFIG_SMP | 61 | #ifdef CONFIG_SMP |
| 62 | struct mnt_pcp __percpu *mnt_pcp; | 62 | struct mnt_pcp __percpu *mnt_pcp; |
| 63 | atomic_t mnt_longrefs; | 63 | atomic_t mnt_longterm; /* how many of the refs are longterm */ |
| 64 | #else | 64 | #else |
| 65 | int mnt_count; | 65 | int mnt_count; |
| 66 | int mnt_writers; | 66 | int mnt_writers; |
| @@ -96,8 +96,6 @@ extern int mnt_clone_write(struct vfsmount *mnt); | |||
| 96 | extern void mnt_drop_write(struct vfsmount *mnt); | 96 | extern void mnt_drop_write(struct vfsmount *mnt); |
| 97 | extern void mntput(struct vfsmount *mnt); | 97 | extern void mntput(struct vfsmount *mnt); |
| 98 | extern struct vfsmount *mntget(struct vfsmount *mnt); | 98 | extern struct vfsmount *mntget(struct vfsmount *mnt); |
| 99 | extern void mntput_long(struct vfsmount *mnt); | ||
| 100 | extern struct vfsmount *mntget_long(struct vfsmount *mnt); | ||
| 101 | extern void mnt_pin(struct vfsmount *mnt); | 99 | extern void mnt_pin(struct vfsmount *mnt); |
| 102 | extern void mnt_unpin(struct vfsmount *mnt); | 100 | extern void mnt_unpin(struct vfsmount *mnt); |
| 103 | extern int __mnt_is_readonly(struct vfsmount *mnt); | 101 | extern int __mnt_is_readonly(struct vfsmount *mnt); |
diff --git a/include/linux/path.h b/include/linux/path.h index a581e8c06533..edc98dec6266 100644 --- a/include/linux/path.h +++ b/include/linux/path.h | |||
| @@ -10,9 +10,7 @@ struct path { | |||
| 10 | }; | 10 | }; |
| 11 | 11 | ||
| 12 | extern void path_get(struct path *); | 12 | extern void path_get(struct path *); |
| 13 | extern void path_get_long(struct path *); | ||
| 14 | extern void path_put(struct path *); | 13 | extern void path_put(struct path *); |
| 15 | extern void path_put_long(struct path *); | ||
| 16 | 14 | ||
| 17 | static inline int path_equal(const struct path *path1, const struct path *path2) | 15 | static inline int path_equal(const struct path *path1, const struct path *path2) |
| 18 | { | 16 | { |
