aboutsummaryrefslogtreecommitdiffstats
path: root/fs/namespace.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-01-14 22:30:21 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2011-01-16 13:47:07 -0500
commitf03c65993b98eeb909a4012ce7833c5857d74755 (patch)
treea6dd5e353889b7fe4ab87c54170d09443d788fec /fs/namespace.c
parent7b8a53fd815deb39542085897743fa0063f9fe06 (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/namespace.c')
-rw-r--r--fs/namespace.c116
1 files changed, 43 insertions, 73 deletions
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)
183unsigned int mnt_get_count(struct vfsmount *mnt) 183unsigned 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 740static void mntput_no_expire(struct vfsmount *mnt)
738static inline void __mntput(struct vfsmount *mnt, int longrefs)
739{ 741{
740 if (!longrefs) {
741put_again: 742put_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
775static inline void __mntput(struct vfsmount *mnt, int longrefs)
776{
777put_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
794static void mntput_no_expire(struct vfsmount *mnt)
795{
796 __mntput(mnt, 0);
797}
798 774
799void mntput(struct vfsmount *mnt) 775void 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}
808EXPORT_SYMBOL(mntput); 784EXPORT_SYMBOL(mntput);
@@ -815,33 +791,6 @@ struct vfsmount *mntget(struct vfsmount *mnt)
815} 791}
816EXPORT_SYMBOL(mntget); 792EXPORT_SYMBOL(mntget);
817 793
818void 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}
831EXPORT_SYMBOL(mntput_long);
832
833struct 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}
843EXPORT_SYMBOL(mntget_long);
844
845void mnt_pin(struct vfsmount *mnt) 794void 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
1970unlock: 1920unlock:
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
2244void mnt_make_longterm(struct vfsmount *mnt)
2245{
2246 atomic_inc(&mnt->mnt_longterm);
2247}
2248
2249void 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 }