aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRam Pai <linuxram@us.ibm.com>2005-11-07 17:20:17 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-11-07 21:18:11 -0500
commita05964f3917c7c55368c229d7985f8e7c9977e97 (patch)
tree7ec25550267ef050572c00ba2f37d6a4ff9c469e /fs
parent2144440327fa01b2f3f65e355120a78211685702 (diff)
[PATCH] shared mounts handling: umount
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 <linuxram@us.ibm.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/namespace.c56
-rw-r--r--fs/pnode.c87
-rw-r--r--fs/pnode.h2
3 files changed, 126 insertions, 19 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 1487982dbc24..4b1af01c2fb4 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -86,31 +86,44 @@ void free_vfsmnt(struct vfsmount *mnt)
86} 86}
87 87
88/* 88/*
89 * Now, lookup_mnt increments the ref count before returning 89 * find the first or last mount at @dentry on vfsmount @mnt depending on
90 * the vfsmount struct. 90 * @dir. If @dir is set return the first mount else return the last mount.
91 */ 91 */
92struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) 92struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry,
93 int dir)
93{ 94{
94 struct list_head *head = mount_hashtable + hash(mnt, dentry); 95 struct list_head *head = mount_hashtable + hash(mnt, dentry);
95 struct list_head *tmp = head; 96 struct list_head *tmp = head;
96 struct vfsmount *p, *found = NULL; 97 struct vfsmount *p, *found = NULL;
97 98
98 spin_lock(&vfsmount_lock);
99 for (;;) { 99 for (;;) {
100 tmp = tmp->next; 100 tmp = dir ? tmp->next : tmp->prev;
101 p = NULL; 101 p = NULL;
102 if (tmp == head) 102 if (tmp == head)
103 break; 103 break;
104 p = list_entry(tmp, struct vfsmount, mnt_hash); 104 p = list_entry(tmp, struct vfsmount, mnt_hash);
105 if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { 105 if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) {
106 found = mntget(p); 106 found = p;
107 break; 107 break;
108 } 108 }
109 } 109 }
110 spin_unlock(&vfsmount_lock);
111 return found; 110 return found;
112} 111}
113 112
113/*
114 * lookup_mnt increments the ref count before returning
115 * the vfsmount struct.
116 */
117struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
118{
119 struct vfsmount *child_mnt;
120 spin_lock(&vfsmount_lock);
121 if ((child_mnt = __lookup_mnt(mnt, dentry, 1)))
122 mntget(child_mnt);
123 spin_unlock(&vfsmount_lock);
124 return child_mnt;
125}
126
114static inline int check_mnt(struct vfsmount *mnt) 127static inline int check_mnt(struct vfsmount *mnt)
115{ 128{
116 return mnt->mnt_namespace == current->namespace; 129 return mnt->mnt_namespace == current->namespace;
@@ -404,9 +417,12 @@ EXPORT_SYMBOL(may_umount_tree);
404 */ 417 */
405int may_umount(struct vfsmount *mnt) 418int may_umount(struct vfsmount *mnt)
406{ 419{
407 if (atomic_read(&mnt->mnt_count) > 2) 420 int ret = 0;
408 return -EBUSY; 421 spin_lock(&vfsmount_lock);
409 return 0; 422 if (propagate_mount_busy(mnt, 2))
423 ret = -EBUSY;
424 spin_unlock(&vfsmount_lock);
425 return ret;
410} 426}
411 427
412EXPORT_SYMBOL(may_umount); 428EXPORT_SYMBOL(may_umount);
@@ -433,7 +449,7 @@ void release_mounts(struct list_head *head)
433 } 449 }
434} 450}
435 451
436void umount_tree(struct vfsmount *mnt, struct list_head *kill) 452void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
437{ 453{
438 struct vfsmount *p; 454 struct vfsmount *p;
439 455
@@ -442,6 +458,9 @@ void umount_tree(struct vfsmount *mnt, struct list_head *kill)
442 list_add(&p->mnt_hash, kill); 458 list_add(&p->mnt_hash, kill);
443 } 459 }
444 460
461 if (propagate)
462 propagate_umount(kill);
463
445 list_for_each_entry(p, kill, mnt_hash) { 464 list_for_each_entry(p, kill, mnt_hash) {
446 list_del_init(&p->mnt_expire); 465 list_del_init(&p->mnt_expire);
447 list_del_init(&p->mnt_list); 466 list_del_init(&p->mnt_list);
@@ -450,6 +469,7 @@ void umount_tree(struct vfsmount *mnt, struct list_head *kill)
450 list_del_init(&p->mnt_child); 469 list_del_init(&p->mnt_child);
451 if (p->mnt_parent != p) 470 if (p->mnt_parent != p)
452 mnt->mnt_mountpoint->d_mounted--; 471 mnt->mnt_mountpoint->d_mounted--;
472 change_mnt_propagation(p, MS_PRIVATE);
453 } 473 }
454} 474}
455 475
@@ -526,9 +546,9 @@ static int do_umount(struct vfsmount *mnt, int flags)
526 event++; 546 event++;
527 547
528 retval = -EBUSY; 548 retval = -EBUSY;
529 if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { 549 if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
530 if (!list_empty(&mnt->mnt_list)) 550 if (!list_empty(&mnt->mnt_list))
531 umount_tree(mnt, &umount_list); 551 umount_tree(mnt, 1, &umount_list);
532 retval = 0; 552 retval = 0;
533 } 553 }
534 spin_unlock(&vfsmount_lock); 554 spin_unlock(&vfsmount_lock);
@@ -651,7 +671,7 @@ Enomem:
651 if (res) { 671 if (res) {
652 LIST_HEAD(umount_list); 672 LIST_HEAD(umount_list);
653 spin_lock(&vfsmount_lock); 673 spin_lock(&vfsmount_lock);
654 umount_tree(res, &umount_list); 674 umount_tree(res, 0, &umount_list);
655 spin_unlock(&vfsmount_lock); 675 spin_unlock(&vfsmount_lock);
656 release_mounts(&umount_list); 676 release_mounts(&umount_list);
657 } 677 }
@@ -827,7 +847,7 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse)
827 if (err) { 847 if (err) {
828 LIST_HEAD(umount_list); 848 LIST_HEAD(umount_list);
829 spin_lock(&vfsmount_lock); 849 spin_lock(&vfsmount_lock);
830 umount_tree(mnt, &umount_list); 850 umount_tree(mnt, 0, &umount_list);
831 spin_unlock(&vfsmount_lock); 851 spin_unlock(&vfsmount_lock);
832 release_mounts(&umount_list); 852 release_mounts(&umount_list);
833 } 853 }
@@ -1023,12 +1043,12 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
1023 * Check that it is still dead: the count should now be 2 - as 1043 * Check that it is still dead: the count should now be 2 - as
1024 * contributed by the vfsmount parent and the mntget above 1044 * contributed by the vfsmount parent and the mntget above
1025 */ 1045 */
1026 if (atomic_read(&mnt->mnt_count) == 2) { 1046 if (!propagate_mount_busy(mnt, 2)) {
1027 /* delete from the namespace */ 1047 /* delete from the namespace */
1028 touch_namespace(mnt->mnt_namespace); 1048 touch_namespace(mnt->mnt_namespace);
1029 list_del_init(&mnt->mnt_list); 1049 list_del_init(&mnt->mnt_list);
1030 mnt->mnt_namespace = NULL; 1050 mnt->mnt_namespace = NULL;
1031 umount_tree(mnt, umounts); 1051 umount_tree(mnt, 1, umounts);
1032 spin_unlock(&vfsmount_lock); 1052 spin_unlock(&vfsmount_lock);
1033 } else { 1053 } else {
1034 /* 1054 /*
@@ -1647,7 +1667,7 @@ void __put_namespace(struct namespace *namespace)
1647 spin_unlock(&vfsmount_lock); 1667 spin_unlock(&vfsmount_lock);
1648 down_write(&namespace_sem); 1668 down_write(&namespace_sem);
1649 spin_lock(&vfsmount_lock); 1669 spin_lock(&vfsmount_lock);
1650 umount_tree(root, &umount_list); 1670 umount_tree(root, 0, &umount_list);
1651 spin_unlock(&vfsmount_lock); 1671 spin_unlock(&vfsmount_lock);
1652 up_write(&namespace_sem); 1672 up_write(&namespace_sem);
1653 release_mounts(&umount_list); 1673 release_mounts(&umount_list);
diff --git a/fs/pnode.c b/fs/pnode.c
index 2d572b88e6f6..7bc942d047cd 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -99,9 +99,94 @@ out:
99 while (!list_empty(&tmp_list)) { 99 while (!list_empty(&tmp_list)) {
100 child = list_entry(tmp_list.next, struct vfsmount, mnt_hash); 100 child = list_entry(tmp_list.next, struct vfsmount, mnt_hash);
101 list_del_init(&child->mnt_hash); 101 list_del_init(&child->mnt_hash);
102 umount_tree(child, &umount_list); 102 umount_tree(child, 0, &umount_list);
103 } 103 }
104 spin_unlock(&vfsmount_lock); 104 spin_unlock(&vfsmount_lock);
105 release_mounts(&umount_list); 105 release_mounts(&umount_list);
106 return ret; 106 return ret;
107} 107}
108
109/*
110 * return true if the refcount is greater than count
111 */
112static inline int do_refcount_check(struct vfsmount *mnt, int count)
113{
114 int mycount = atomic_read(&mnt->mnt_count);
115 return (mycount > count);
116}
117
118/*
119 * check if the mount 'mnt' can be unmounted successfully.
120 * @mnt: the mount to be checked for unmount
121 * NOTE: unmounting 'mnt' would naturally propagate to all
122 * other mounts its parent propagates to.
123 * Check if any of these mounts that **do not have submounts**
124 * have more references than 'refcnt'. If so return busy.
125 */
126int propagate_mount_busy(struct vfsmount *mnt, int refcnt)
127{
128 struct vfsmount *m, *child;
129 struct vfsmount *parent = mnt->mnt_parent;
130 int ret = 0;
131
132 if (mnt == parent)
133 return do_refcount_check(mnt, refcnt);
134
135 /*
136 * quickly check if the current mount can be unmounted.
137 * If not, we don't have to go checking for all other
138 * mounts
139 */
140 if (!list_empty(&mnt->mnt_mounts) || do_refcount_check(mnt, refcnt))
141 return 1;
142
143 for (m = propagation_next(parent, parent); m;
144 m = propagation_next(m, parent)) {
145 child = __lookup_mnt(m, mnt->mnt_mountpoint, 0);
146 if (child && list_empty(&child->mnt_mounts) &&
147 (ret = do_refcount_check(child, 1)))
148 break;
149 }
150 return ret;
151}
152
153/*
154 * NOTE: unmounting 'mnt' naturally propagates to all other mounts its
155 * parent propagates to.
156 */
157static void __propagate_umount(struct vfsmount *mnt)
158{
159 struct vfsmount *parent = mnt->mnt_parent;
160 struct vfsmount *m;
161
162 BUG_ON(parent == mnt);
163
164 for (m = propagation_next(parent, parent); m;
165 m = propagation_next(m, parent)) {
166
167 struct vfsmount *child = __lookup_mnt(m,
168 mnt->mnt_mountpoint, 0);
169 /*
170 * umount the child only if the child has no
171 * other children
172 */
173 if (child && list_empty(&child->mnt_mounts)) {
174 list_del(&child->mnt_hash);
175 list_add_tail(&child->mnt_hash, &mnt->mnt_hash);
176 }
177 }
178}
179
180/*
181 * collect all mounts that receive propagation from the mount in @list,
182 * and return these additional mounts in the same list.
183 * @list: the list of mounts to be unmounted.
184 */
185int propagate_umount(struct list_head *list)
186{
187 struct vfsmount *mnt;
188
189 list_for_each_entry(mnt, list, mnt_hash)
190 __propagate_umount(mnt);
191 return 0;
192}
diff --git a/fs/pnode.h b/fs/pnode.h
index c62c72fad212..9b88ba06794a 100644
--- a/fs/pnode.h
+++ b/fs/pnode.h
@@ -29,4 +29,6 @@ static inline void set_mnt_shared(struct vfsmount *mnt)
29void change_mnt_propagation(struct vfsmount *, int); 29void change_mnt_propagation(struct vfsmount *, int);
30int propagate_mnt(struct vfsmount *, struct dentry *, struct vfsmount *, 30int propagate_mnt(struct vfsmount *, struct dentry *, struct vfsmount *,
31 struct list_head *); 31 struct list_head *);
32int propagate_umount(struct list_head *);
33int propagate_mount_busy(struct vfsmount *, int);
32#endif /* _LINUX_PNODE_H */ 34#endif /* _LINUX_PNODE_H */