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