shared mounts handling: umount
PATCH 14/18 shared mounts handling: umount
From: Ram Pai <[email protected]>
Date: 1131402017 -0500
An unmount of a mount creates a umount event on the parent. If the parent is a shared mount, it gets propagated to all mounts in the peer group.
Signed-off-by Ram Pai ([email protected])
Signed-off-by Al Viro <[email protected]>
---
fs/namespace.c | 56 +++++++++++++++++++++---------- fs/pnode.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++- fs/pnode.h | 2 + include/linux/dcache.h | 1 + include/linux/fs.h | 2 + 5 files changed, 128 insertions(+), 20 deletions(-)
9eeda77dfca5c1193fcf9702d2921e3545362c19
fs/namespace.c +38 -18
@@ -86,31 +86,44 @@ void free_vfsmnt(struct vfsmount *mnt) }/* -* Now, lookup_mnt increments the ref count before returning -* the vfsmount struct. +* find the first or last mount at @dentry on vfsmount @mnt depending on +* @dir. If @dir is set return the first mount else return the last mount. */ -struct vfsmount *lookup_mnt (struct vfsmount *mnt,struct dentry *dentry) +struct vfsmount *__lookup_mnt (struct vfsmount *mnt,struct dentry *dentry, +int dir) {struct list_head *head = mount_hashtable +hash (mnt, dentry);struct list_head *tmp = head;struct vfsmount *p, *found =NULL ; -spin_lock (&vfsmount_lock);for (;;) { - tmp = tmp->next; + tmp = dir ? tmp->next : tmp->prev; p =NULL ;if (tmp == head)break ; p = list_entry(tmp, struct vfsmount, mnt_hash);if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { - found =mntget (p); + found = p;break ; } } -spin_unlock (&vfsmount_lock);return found; } +/* +* lookup_mnt increments the ref count before returning +* the vfsmount struct. +*/ +struct vfsmount *lookup_mnt (struct vfsmount *mnt,struct dentry *dentry) +{ +struct vfsmount *child_mnt; +spin_lock (&vfsmount_lock); +if ((child_mnt =__lookup_mnt (mnt, dentry,1 ))) +mntget (child_mnt); +spin_unlock (&vfsmount_lock); +return child_mnt; +} +static inline int check_mnt (struct vfsmount *mnt) {return mnt->mnt_namespace == current->namespace;
@@ -404,9 +417,12 @@ EXPORT_SYMBOL(may_umount_tree);*/ int may_umount (struct vfsmount *mnt) { -if (atomic_read (&mnt->mnt_count) >2 ) -return -EBUSY ; -return 0 ; +int ret =0 ; +spin_lock (&vfsmount_lock); +if (propagate_mount_busy (mnt,2 )) + ret = -EBUSY ; +spin_unlock (&vfsmount_lock); +return ret; }EXPORT_SYMBOL (may_umount);
@@ -433,7 +449,7 @@ void release_mounts(struct list_head *he } } -void umount_tree (struct vfsmount *mnt,struct list_head *kill) +void umount_tree (struct vfsmount *mnt,int propagate,struct list_head *kill) {struct vfsmount *p;
@@ -442,6 +458,9 @@ void umount_tree(struct vfsmount *mnt, slist_add (&p->mnt_hash, kill); } +if (propagate) +propagate_umount (kill); +list_for_each_entry (p, kill, mnt_hash) {list_del_init (&p->mnt_expire);list_del_init (&p->mnt_list);
@@ -450,6 +469,7 @@ void umount_tree(struct vfsmount *mnt, slist_del_init (&p->mnt_child);if (p->mnt_parent != p) mnt->mnt_mountpoint->d_mounted--; +change_mnt_propagation (p,MS_PRIVATE ); } }
@@ -526,9 +546,9 @@ static int do_umount(struct vfsmount *mn event++; retval = -EBUSY ; -if (atomic_read (&mnt->mnt_count) ==2 || flags &MNT_DETACH ) { +if (flags &MNT_DETACH || !propagate_mount_busy (mnt,2 )) {if (!list_empty (&mnt->mnt_list)) -umount_tree (mnt, &umount_list); +umount_tree (mnt,1 , &umount_list); retval =0 ; }spin_unlock (&vfsmount_lock);
@@ -651,7 +671,7 @@ Enomem:if (res) {LIST_HEAD (umount_list);spin_lock (&vfsmount_lock); -umount_tree (res, &umount_list); +umount_tree (res,0 , &umount_list);spin_unlock (&vfsmount_lock);release_mounts (&umount_list); }
@@ -827,7 +847,7 @@ static int do_loopback(struct nameidataif (err) {LIST_HEAD (umount_list);spin_lock (&vfsmount_lock); -umount_tree (mnt, &umount_list); +umount_tree (mnt,0 , &umount_list);spin_unlock (&vfsmount_lock);release_mounts (&umount_list); }
@@ -1023,12 +1043,12 @@ static void expire_mount(struct vfsmount* Check that it is still dead: the count should now be 2 - as * contributed by the vfsmount parent and the mntget above */ -if (atomic_read (&mnt->mnt_count) ==2 ) { +if (!propagate_mount_busy (mnt,2 )) {/* delete from the namespace */ touch_namespace (mnt->mnt_namespace);list_del_init (&mnt->mnt_list); mnt->mnt_namespace =NULL ; -umount_tree (mnt, umounts); +umount_tree (mnt,1 , umounts);spin_unlock (&vfsmount_lock); }else {/*
@@ -1647,7 +1667,7 @@ void __put_namespace(struct namespace *nspin_unlock (&vfsmount_lock);down_write (&namespace_sem);spin_lock (&vfsmount_lock); -umount_tree (root, &umount_list); +umount_tree (root,0 , &umount_list);spin_unlock (&vfsmount_lock);up_write (&namespace_sem);release_mounts (&umount_list);
fs/pnode.c +86 -1
95 lines not on this page · raw message
fs/pnode.h +2 -0
@@ -29,4 +29,6 @@ static inline void set_mnt_shared(structvoid change_mnt_propagation (struct vfsmount *,int );int propagate_mnt (struct vfsmount *,struct dentry *,struct vfsmount *,struct list_head *); +int propagate_umount (struct list_head *); +int propagate_mount_busy (struct vfsmount *,int );#endif /* _LINUX_PNODE_H */
include/linux/dcache.h +1 -0
@@ -329,6 +329,7 @@ static inline int d_mountpoint(struct de }extern struct vfsmount *lookup_mnt (struct vfsmount *,struct dentry *); +extern struct vfsmount *__lookup_mnt (struct vfsmount *,struct dentry *,int );extern struct dentry *lookup_create (struct nameidata *nd,int is_dir);extern int sysctl_vfs_cache_pressure;
include/linux/fs.h +1 -1
@@ -1251,7 +1251,7 @@ extern int unregister_filesystem(structextern struct vfsmount *kern_mount (struct file_system_type *);extern int may_umount_tree (struct vfsmount *);extern int may_umount (struct vfsmount *); -extern void umount_tree (struct vfsmount *,struct list_head *); +extern void umount_tree (struct vfsmount *,int ,struct list_head *);extern void release_mounts (struct list_head *);extern long do_mount (char *,char *,char *,unsigned long ,void *);extern struct vfsmount *copy_tree (struct vfsmount *,struct dentry *,int );