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 /fs/fs_struct.c | |
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>
Diffstat (limited to 'fs/fs_struct.c')
-rw-r--r-- | fs/fs_struct.c | 35 |
1 files changed, 24 insertions, 11 deletions
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; |