diff options
author | Eric W. Biederman <ebiederman@twitter.com> | 2013-10-03 04:31:18 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2014-10-09 02:38:55 -0400 |
commit | 80b5dce8c59b0de1ed6e403b8298e02dcb4db64b (patch) | |
tree | 90b7a4f30c050d37f7ec436b63c8f9d7816199f0 | |
parent | e2dfa935464272395b4f35f4cc74ffcc87418b84 (diff) |
vfs: Add a function to lazily unmount all mounts from any dentry.
The new function detach_mounts comes in two pieces. The first piece
is a static inline test of d_mounpoint that returns immediately
without taking any locks if d_mounpoint is not set. In the common
case when mountpoints are absent this allows the vfs to continue
running with it's same cacheline foot print.
The second piece of detach_mounts __detach_mounts actually does the
work and it assumes that a mountpoint is present so it is slow and
takes namespace_sem for write, and then locks the mount hash (aka
mount_lock) after a struct mountpoint has been found.
With those two locks held each entry on the list of mounts on a
mountpoint is selected and lazily unmounted until all of the mount
have been lazily unmounted.
v7: Wrote a proper change description and removed the changelog
documenting deleted wrong turns.
Signed-off-by: Eric W. Biederman <ebiederman@twitter.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/mount.h | 9 | ||||
-rw-r--r-- | fs/namespace.c | 31 |
2 files changed, 40 insertions, 0 deletions
diff --git a/fs/mount.h b/fs/mount.h index 68bb03ed7f19..f82c62840905 100644 --- a/fs/mount.h +++ b/fs/mount.h | |||
@@ -87,6 +87,15 @@ extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *); | |||
87 | 87 | ||
88 | extern bool legitimize_mnt(struct vfsmount *, unsigned); | 88 | extern bool legitimize_mnt(struct vfsmount *, unsigned); |
89 | 89 | ||
90 | extern void __detach_mounts(struct dentry *dentry); | ||
91 | |||
92 | static inline void detach_mounts(struct dentry *dentry) | ||
93 | { | ||
94 | if (!d_mountpoint(dentry)) | ||
95 | return; | ||
96 | __detach_mounts(dentry); | ||
97 | } | ||
98 | |||
90 | static inline void get_mnt_ns(struct mnt_namespace *ns) | 99 | static inline void get_mnt_ns(struct mnt_namespace *ns) |
91 | { | 100 | { |
92 | atomic_inc(&ns->count); | 101 | atomic_inc(&ns->count); |
diff --git a/fs/namespace.c b/fs/namespace.c index 88fc3f4d77ed..00e5b1efa591 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -1468,6 +1468,37 @@ static int do_umount(struct mount *mnt, int flags) | |||
1468 | return retval; | 1468 | return retval; |
1469 | } | 1469 | } |
1470 | 1470 | ||
1471 | /* | ||
1472 | * __detach_mounts - lazily unmount all mounts on the specified dentry | ||
1473 | * | ||
1474 | * During unlink, rmdir, and d_drop it is possible to loose the path | ||
1475 | * to an existing mountpoint, and wind up leaking the mount. | ||
1476 | * detach_mounts allows lazily unmounting those mounts instead of | ||
1477 | * leaking them. | ||
1478 | * | ||
1479 | * The caller may hold dentry->d_inode->i_mutex. | ||
1480 | */ | ||
1481 | void __detach_mounts(struct dentry *dentry) | ||
1482 | { | ||
1483 | struct mountpoint *mp; | ||
1484 | struct mount *mnt; | ||
1485 | |||
1486 | namespace_lock(); | ||
1487 | mp = lookup_mountpoint(dentry); | ||
1488 | if (!mp) | ||
1489 | goto out_unlock; | ||
1490 | |||
1491 | lock_mount_hash(); | ||
1492 | while (!hlist_empty(&mp->m_list)) { | ||
1493 | mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list); | ||
1494 | umount_tree(mnt, 2); | ||
1495 | } | ||
1496 | unlock_mount_hash(); | ||
1497 | put_mountpoint(mp); | ||
1498 | out_unlock: | ||
1499 | namespace_unlock(); | ||
1500 | } | ||
1501 | |||
1471 | /* | 1502 | /* |
1472 | * Is the caller allowed to modify his namespace? | 1503 | * Is the caller allowed to modify his namespace? |
1473 | */ | 1504 | */ |