| From 0c0bf47e3e77ebff691abe1a5e133e43515e5205 Mon Sep 17 00:00:00 2001 |
| From: "T.kabe" <kabe@> |
| Date: Mon, 6 Mar 2017 15:21:16 +0900 |
| Subject: [PATCH 3/4] vfs: Add a function to lazily unmount all mounts from any dentry. |
| |
| [upstream commit 80b5dce8c59b0de1ed6e403b8298e02dcb4db64b] |
| Author: Eric W. Biederman <ebiederman@twitter.com> |
| Date: Thu Oct 3 01:31:18 2013 -0700 |
| |
| 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> |
| |
| fs/mount.h | 9 +++++++++ |
| fs/namespace.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 57 insertions(+) |
| |
| diff --git a/fs/mount.h b/fs/mount.h |
| index 92fecbe..9959119 100644 |
| |
| |
| @@ -80,6 +80,15 @@ static inline int is_mounted(struct vfsmount *mnt) |
| |
| extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *, int); |
| |
| +extern void __detach_mounts(struct dentry *dentry); |
| + |
| +static inline void detach_mounts(struct dentry *dentry) |
| +{ |
| + if (!d_mountpoint(dentry)) |
| + return; |
| + __detach_mounts(dentry); |
| +} |
| + |
| static inline void get_mnt_ns(struct mnt_namespace *ns) |
| { |
| atomic_inc(&ns->count); |
| diff --git a/fs/namespace.c b/fs/namespace.c |
| index c238481..e48fed3 100644 |
| |
| |
| @@ -608,6 +608,23 @@ struct vfsmount *lookup_mnt(struct path *path) |
| } |
| } |
| |
| +static struct mountpoint *lookup_mountpoint(struct dentry *dentry) |
| +{ |
| + struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry); |
| + struct mountpoint *mp; |
| + |
| + list_for_each_entry(mp, chain, m_hash) { |
| + if (mp->m_dentry == dentry) { |
| + /* might be worth a WARN_ON() */ |
| + if (d_unlinked(dentry)) |
| + return ERR_PTR(-ENOENT); |
| + mp->m_count++; |
| + return mp; |
| + } |
| + } |
| + return NULL; |
| +} |
| + |
| static struct mountpoint *new_mountpoint(struct dentry *dentry) |
| { |
| struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry); |
| @@ -1312,6 +1329,37 @@ static int do_umount(struct mount *mnt, int flags) |
| return retval; |
| } |
| |
| +/* |
| + * __detach_mounts - lazily unmount all mounts on the specified dentry |
| + * |
| + * During unlink, rmdir, and d_drop it is possible to loose the path |
| + * to an existing mountpoint, and wind up leaking the mount. |
| + * detach_mounts allows lazily unmounting those mounts instead of |
| + * leaking them. |
| + * |
| + * The caller may hold dentry->d_inode->i_mutex. |
| + */ |
| +void __detach_mounts(struct dentry *dentry) |
| +{ |
| + struct mountpoint *mp; |
| + struct mount *mnt; |
| + |
| + namespace_lock(); |
| + mp = lookup_mountpoint(dentry); |
| + if (!mp) |
| + goto out_unlock; |
| + |
| + br_write_lock(&vfsmount_lock); |
| + while (!hlist_empty(&mp->m_list)) { |
| + mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list); |
| + umount_tree(mnt, 2); |
| + } |
| + br_write_unlock(&vfsmount_lock); |
| + put_mountpoint(mp); |
| +out_unlock: |
| + namespace_unlock(); |
| +} |
| + |
| /* |
| * Is the caller allowed to modify his namespace? |
| */ |
| -- |
| 1.8.3.1 |
| |