b312fc
From 0c0bf47e3e77ebff691abe1a5e133e43515e5205 Mon Sep 17 00:00:00 2001
b312fc
From: "T.kabe" <kabe@>
b312fc
Date: Mon, 6 Mar 2017 15:21:16 +0900
b312fc
Subject: [PATCH 3/4] vfs: Add a function to lazily unmount all mounts from any dentry.
b312fc
b312fc
[upstream commit 80b5dce8c59b0de1ed6e403b8298e02dcb4db64b]
b312fc
Author: Eric W. Biederman <ebiederman@twitter.com>
b312fc
Date:   Thu Oct 3 01:31:18 2013 -0700
b312fc
b312fc
    vfs: Add a function to lazily unmount all mounts from any dentry.
b312fc
b312fc
    The new function detach_mounts comes in two pieces.  The first piece
b312fc
    is a static inline test of d_mounpoint that returns immediately
b312fc
    without taking any locks if d_mounpoint is not set.  In the common
b312fc
    case when mountpoints are absent this allows the vfs to continue
b312fc
    running with it's same cacheline foot print.
b312fc
b312fc
    The second piece of detach_mounts __detach_mounts actually does the
b312fc
    work and it assumes that a mountpoint is present so it is slow and
b312fc
    takes namespace_sem for write, and then locks the mount hash (aka
b312fc
    mount_lock) after a struct mountpoint has been found.
b312fc
b312fc
    With those two locks held each entry on the list of mounts on a
b312fc
    mountpoint is selected and lazily unmounted until all of the mount
b312fc
    have been lazily unmounted.
b312fc
b312fc
    v7: Wrote a proper change description and removed the changelog
b312fc
        documenting deleted wrong turns.
b312fc
b312fc
    Signed-off-by: Eric W. Biederman <ebiederman@twitter.com>
b312fc
    Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
b312fc
---
b312fc
 fs/mount.h     |  9 +++++++++
b312fc
 fs/namespace.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
b312fc
 2 files changed, 57 insertions(+)
b312fc
b312fc
diff --git a/fs/mount.h b/fs/mount.h
b312fc
index 92fecbe..9959119 100644
b312fc
--- a/fs/mount.h
b312fc
+++ b/fs/mount.h
b312fc
@@ -80,6 +80,15 @@ static inline int is_mounted(struct vfsmount *mnt)
b312fc
 
b312fc
 extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *, int);
b312fc
 
b312fc
+extern void __detach_mounts(struct dentry *dentry);
b312fc
+
b312fc
+static inline void detach_mounts(struct dentry *dentry)
b312fc
+{
b312fc
+	if (!d_mountpoint(dentry))
b312fc
+		return;
b312fc
+	__detach_mounts(dentry);
b312fc
+}
b312fc
+
b312fc
 static inline void get_mnt_ns(struct mnt_namespace *ns)
b312fc
 {
b312fc
 	atomic_inc(&ns->count);
b312fc
diff --git a/fs/namespace.c b/fs/namespace.c
b312fc
index c238481..e48fed3 100644
b312fc
--- a/fs/namespace.c
b312fc
+++ b/fs/namespace.c
b312fc
@@ -608,6 +608,23 @@ struct vfsmount *lookup_mnt(struct path *path)
b312fc
 	}
b312fc
 }
b312fc
 
b312fc
+static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
b312fc
+{
b312fc
+	struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry);
b312fc
+	struct mountpoint *mp;
b312fc
+
b312fc
+	list_for_each_entry(mp, chain, m_hash) {
b312fc
+		if (mp->m_dentry == dentry) {
b312fc
+			/* might be worth a WARN_ON() */
b312fc
+			if (d_unlinked(dentry))
b312fc
+				return ERR_PTR(-ENOENT);
b312fc
+			mp->m_count++;
b312fc
+			return mp;
b312fc
+		}
b312fc
+	}
b312fc
+	return NULL;
b312fc
+}
b312fc
+
b312fc
 static struct mountpoint *new_mountpoint(struct dentry *dentry)
b312fc
 {
b312fc
 	struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry);
b312fc
@@ -1312,6 +1329,37 @@ static int do_umount(struct mount *mnt, int flags)
b312fc
 	return retval;
b312fc
 }
b312fc
 
b312fc
+/*
b312fc
+ * __detach_mounts - lazily unmount all mounts on the specified dentry
b312fc
+ *
b312fc
+ * During unlink, rmdir, and d_drop it is possible to loose the path
b312fc
+ * to an existing mountpoint, and wind up leaking the mount.
b312fc
+ * detach_mounts allows lazily unmounting those mounts instead of
b312fc
+ * leaking them.
b312fc
+ *
b312fc
+ * The caller may hold dentry->d_inode->i_mutex.
b312fc
+ */
b312fc
+void __detach_mounts(struct dentry *dentry)
b312fc
+{
b312fc
+	struct mountpoint *mp;
b312fc
+	struct mount *mnt;
b312fc
+
b312fc
+	namespace_lock();
b312fc
+	mp = lookup_mountpoint(dentry);
b312fc
+	if (!mp)
b312fc
+		goto out_unlock;
b312fc
+
b312fc
+	br_write_lock(&vfsmount_lock);
b312fc
+	while (!hlist_empty(&mp->m_list)) {
b312fc
+		mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
b312fc
+		umount_tree(mnt, 2);
b312fc
+	}
b312fc
+	br_write_unlock(&vfsmount_lock);
b312fc
+	put_mountpoint(mp);
b312fc
+out_unlock:
b312fc
+	namespace_unlock();
b312fc
+}
b312fc
+
b312fc
 /* 
b312fc
  * Is the caller allowed to modify his namespace?
b312fc
  */
b312fc
-- 
b312fc
1.8.3.1
b312fc