diff --git a/fs/dcache.c b/fs/dcache.c index 2c289487a40730c5120589bae7837e8893c34ab5..a94b9fdf20516352968a8b61257afd66c93b0d14 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2558,7 +2558,7 @@ global_root: if (!slash) error = prepend(buffer, buflen, "/", 1); if (!error) - error = is_mounted(vfsmnt) ? 1 : 2; + error = real_mount(vfsmnt)->mnt_ns ? 1 : 2; goto out; } diff --git a/fs/fs_struct.c b/fs/fs_struct.c index 5df4775fea03638f14d1408cfe16408e8a0ed6d7..e159e682ad4c68b16b82ef7eeafa889c05877580 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -6,6 +6,18 @@ #include <linux/fs_struct.h> #include "internal.h" +static inline void path_get_longterm(struct path *path) +{ + path_get(path); + mnt_make_longterm(path->mnt); +} + +static inline void path_put_longterm(struct path *path) +{ + mnt_make_shortterm(path->mnt); + path_put(path); +} + /* * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values. * It can block. @@ -14,7 +26,7 @@ void set_fs_root(struct fs_struct *fs, struct path *path) { struct path old_root; - path_get(path); + path_get_longterm(path); spin_lock(&fs->lock); write_seqcount_begin(&fs->seq); old_root = fs->root; @@ -22,7 +34,7 @@ void set_fs_root(struct fs_struct *fs, struct path *path) write_seqcount_end(&fs->seq); spin_unlock(&fs->lock); if (old_root.dentry) - path_put(&old_root); + path_put_longterm(&old_root); } /* @@ -33,7 +45,7 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path) { struct path old_pwd; - path_get(path); + path_get_longterm(path); spin_lock(&fs->lock); write_seqcount_begin(&fs->seq); old_pwd = fs->pwd; @@ -42,7 +54,7 @@ void set_fs_pwd(struct fs_struct *fs, struct path *path) spin_unlock(&fs->lock); if (old_pwd.dentry) - path_put(&old_pwd); + path_put_longterm(&old_pwd); } static inline int replace_path(struct path *p, const struct path *old, const struct path *new) @@ -72,7 +84,7 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root) write_seqcount_end(&fs->seq); while (hits--) { count++; - path_get(new_root); + path_get_longterm(new_root); } spin_unlock(&fs->lock); } @@ -80,13 +92,13 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root) } while_each_thread(g, p); read_unlock(&tasklist_lock); while (count--) - path_put(old_root); + path_put_longterm(old_root); } void free_fs_struct(struct fs_struct *fs) { - path_put(&fs->root); - path_put(&fs->pwd); + path_put_longterm(&fs->root); + path_put_longterm(&fs->pwd); kmem_cache_free(fs_cachep, fs); } @@ -120,9 +132,9 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) spin_lock(&old->lock); fs->root = old->root; - path_get(&fs->root); + path_get_longterm(&fs->root); fs->pwd = old->pwd; - path_get(&fs->pwd); + path_get_longterm(&fs->pwd); spin_unlock(&old->lock); } return fs; diff --git a/fs/internal.h b/fs/internal.h index faf568012d99664977d9c1a20f0bb89bf4a4cd53..9962c59ba280b1c75d78adc55b8491733075a5e0 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -50,6 +50,8 @@ extern int copy_mount_string(const void __user *, char **); extern struct vfsmount *lookup_mnt(struct path *); extern int finish_automount(struct vfsmount *, struct path *); +extern void mnt_make_longterm(struct vfsmount *); +extern void mnt_make_shortterm(struct vfsmount *); extern int sb_prepare_remount_readonly(struct super_block *); extern void __init mnt_init(void); diff --git a/fs/mount.h b/fs/mount.h index 05a2a1185efcf2e560012c33042ceb8c406dc119..4ef36d93e5a241822e10c21f93dc72d7137d32ec 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -22,6 +22,7 @@ struct mount { struct vfsmount mnt; #ifdef CONFIG_SMP struct mnt_pcp __percpu *mnt_pcp; + atomic_t mnt_longterm; /* how many of the refs are longterm */ #else int mnt_count; int mnt_writers; @@ -48,8 +49,6 @@ struct mount { int mnt_ghosts; }; -#define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */ - static inline struct mount *real_mount(struct vfsmount *mnt) { return container_of(mnt, struct mount, mnt); @@ -60,12 +59,6 @@ static inline int mnt_has_parent(struct mount *mnt) return mnt != mnt->mnt_parent; } -static inline int is_mounted(struct vfsmount *mnt) -{ - /* neither detached nor internal? */ - return !IS_ERR_OR_NULL(real_mount(mnt)); -} - extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *, int); static inline void get_mnt_ns(struct mnt_namespace *ns) diff --git a/fs/namespace.c b/fs/namespace.c index 324194eabeff38b5f5594764eb4ccaa9efe2e7c2..4e465397e45609f6cca3532d2cc15f434b8841c5 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -621,6 +621,21 @@ static void attach_mnt(struct mount *mnt, struct path *path) list_add_tail(&mnt->mnt_child, &real_mount(path->mnt)->mnt_mounts); } +static inline void __mnt_make_longterm(struct mount *mnt) +{ +#ifdef CONFIG_SMP + atomic_inc(&mnt->mnt_longterm); +#endif +} + +/* needs vfsmount lock for write */ +static inline void __mnt_make_shortterm(struct mount *mnt) +{ +#ifdef CONFIG_SMP + atomic_dec(&mnt->mnt_longterm); +#endif +} + /* * vfsmount lock must be held for write */ @@ -634,8 +649,10 @@ static void commit_tree(struct mount *mnt) BUG_ON(parent == mnt); list_add_tail(&head, &mnt->mnt_list); - list_for_each_entry(m, &head, mnt_list) + list_for_each_entry(m, &head, mnt_list) { m->mnt_ns = n; + __mnt_make_longterm(m); + } list_splice(&head, n->list.prev); @@ -787,8 +804,7 @@ static void mntput_no_expire(struct mount *mnt) put_again: #ifdef CONFIG_SMP br_read_lock(vfsmount_lock); - if (likely(mnt->mnt_ns)) { - /* shouldn't be the last one */ + if (likely(atomic_read(&mnt->mnt_longterm))) { mnt_add_count(mnt, -1); br_read_unlock(vfsmount_lock); return; @@ -1057,6 +1073,8 @@ void umount_tree(struct mount *mnt, int propagate, struct list_head *kill) list_del_init(&p->mnt_expire); list_del_init(&p->mnt_list); __touch_mnt_namespace(p->mnt_ns); + if (p->mnt_ns) + __mnt_make_shortterm(p); p->mnt_ns = NULL; list_del_init(&p->mnt_child); if (mnt_has_parent(p)) { @@ -2190,6 +2208,23 @@ static struct mnt_namespace *alloc_mnt_ns(void) return new_ns; } +void mnt_make_longterm(struct vfsmount *mnt) +{ + __mnt_make_longterm(real_mount(mnt)); +} + +void mnt_make_shortterm(struct vfsmount *m) +{ +#ifdef CONFIG_SMP + struct mount *mnt = real_mount(m); + if (atomic_add_unless(&mnt->mnt_longterm, -1, 1)) + return; + br_write_lock(vfsmount_lock); + atomic_dec(&mnt->mnt_longterm); + br_write_unlock(vfsmount_lock); +#endif +} + /* * Allocate a new namespace structure and populate it with contents * copied from the namespace of the passed in task structure. @@ -2229,13 +2264,18 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, q = new; while (p) { q->mnt_ns = new_ns; + __mnt_make_longterm(q); if (fs) { if (&p->mnt == fs->root.mnt) { fs->root.mnt = mntget(&q->mnt); + __mnt_make_longterm(q); + mnt_make_shortterm(&p->mnt); rootmnt = &p->mnt; } if (&p->mnt == fs->pwd.mnt) { fs->pwd.mnt = mntget(&q->mnt); + __mnt_make_longterm(q); + mnt_make_shortterm(&p->mnt); pwdmnt = &p->mnt; } } @@ -2279,6 +2319,7 @@ static struct mnt_namespace *create_mnt_ns(struct vfsmount *m) if (!IS_ERR(new_ns)) { struct mount *mnt = real_mount(m); mnt->mnt_ns = new_ns; + __mnt_make_longterm(mnt); new_ns->root = mnt; list_add(&new_ns->list, &mnt->mnt_list); } else { @@ -2573,7 +2614,7 @@ struct vfsmount *kern_mount_data(struct file_system_type *type, void *data) * it is a longterm mount, don't release mnt until * we unmount before file sys is unregistered */ - real_mount(mnt)->mnt_ns = MNT_NS_INTERNAL; + mnt_make_longterm(mnt); } return mnt; } @@ -2583,9 +2624,7 @@ void kern_unmount(struct vfsmount *mnt) { /* release long term mount so mount point can be released */ if (!IS_ERR_OR_NULL(mnt)) { - br_write_lock(vfsmount_lock); - real_mount(mnt)->mnt_ns = NULL; - br_write_unlock(vfsmount_lock); + mnt_make_shortterm(mnt); mntput(mnt); } }