From: ebiederm@xmission.com (Eric W. Biederman)
To: Andrei Vagin <avagin@virtuozzo.com>
Cc: Al Viro <viro@ZenIV.linux.org.uk>,
<linux-fsdevel@vger.kernel.org>, Ram Pai <linuxram@us.ibm.com>
Subject: [REVIEW][PATCH 2/2] mnt: Make propagate_umount less slow for overlapping mount propagation trees
Date: Wed, 17 May 2017 00:55:16 -0500 [thread overview]
Message-ID: <877f1gdo3f.fsf_-_@xmission.com> (raw)
In-Reply-To: <87efvodo4l.fsf_-_@xmission.com> (Eric W. Biederman's message of "Wed, 17 May 2017 00:54:34 -0500")
Andrei Vagin pointed out that time to executue propagate_umount can go
non-linear (and take a ludicrious amount of time) when the mount
propogation trees of the mounts to be unmunted by a lazy unmount
overlap.
Make the walk of the mount propagation trees nearly linear by
remembering which mounts have already been visited, allowing
subsequent walks to detect when walking a mount propgation tree or a
subtree of a mount propgation tree would be duplicate work and to skip
them entirely.
Walk the list of mounts whose propgatation trees need to be traversed
from the mount highest in the mount tree to mounts lower in the mount
tree so that odds are higher that the code will walk the largest trees
first, allowing later tree walks to be skipped entirely.
Add cleanup_umount_visitation to remover the code's memory of which
mounts have been visited.
Add the functions last_slave and skip_propagation_subtree to allow
skipping appropriate parts of the mount propagation tree without
needing to change the logic of the rest of the code.
A script to generate overlapping mount propagation trees:
$ cat runs.h
set -e
mount -t tmpfs zdtm /mnt
mkdir -p /mnt/1 /mnt/2
mount -t tmpfs zdtm /mnt/1
mount --make-shared /mnt/1
mkdir /mnt/1/1
iteration=10
if [ -n "$1" ] ; then
iteration=$1
fi
for i in $(seq $iteration); do
mount --bind /mnt/1/1 /mnt/1/1
done
mount --rbind /mnt/1 /mnt/2
TIMEFORMAT='%Rs'
nr=$(( ( 2 ** ( $iteration + 1 ) ) + 1 ))
echo -n "umount -l /mnt/1 -> $nr "
time umount -l /mnt/1
nr=$(cat /proc/self/mountinfo | grep zdtm | wc -l )
time umount -l /mnt/2
$ for i in $(seq 9 19); do echo $i; unshare -Urm bash ./run.sh $i; done
Here are the performance numbers with and without the patch:
mhash | 8192 | 8192 | 1048576 | 1048576
mounts | before | after | before | after
------------------------------------------------
1025 | 0.040s | 0.016s | 0.038s | 0.019s
2049 | 0.094s | 0.017s | 0.080s | 0.018s
4097 | 0.243s | 0.019s | 0.206s | 0.023s
8193 | 1.202s | 0.028s | 1.562s | 0.032s
16385 | 9.635s | 0.036s | 9.952s | 0.041s
32769 | 60.928s | 0.063s | 44.321s | 0.064s
65537 | | 0.097s | | 0.097s
131073 | | 0.233s | | 0.176s
262145 | | 0.653s | | 0.344s
524289 | | 2.305s | | 0.735s
1048577 | | 7.107s | | 2.603s
Andrei Vagin reports fixing the performance problem is part of the
work to fix CVE-2016-6213.
Cc: stable@vger.kernel.org
Fixes: a05964f3917c ("[PATCH] shared mounts handling: umount")
Reported-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
fs/pnode.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 62 insertions(+), 1 deletion(-)
diff --git a/fs/pnode.c b/fs/pnode.c
index fbaca7df2eb0..53d411a371ce 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -24,6 +24,11 @@ static inline struct mount *first_slave(struct mount *p)
return list_entry(p->mnt_slave_list.next, struct mount, mnt_slave);
}
+static inline struct mount *last_slave(struct mount *p)
+{
+ return list_entry(p->mnt_slave_list.prev, struct mount, mnt_slave);
+}
+
static inline struct mount *next_slave(struct mount *p)
{
return list_entry(p->mnt_slave.next, struct mount, mnt_slave);
@@ -162,6 +167,19 @@ static struct mount *propagation_next(struct mount *m,
}
}
+static struct mount *skip_propagation_subtree(struct mount *m,
+ struct mount *origin)
+{
+ /*
+ * Advance m such that propagation_next will not return
+ * the slaves of m.
+ */
+ if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
+ m = last_slave(m);
+
+ return m;
+}
+
static struct mount *next_group(struct mount *m, struct mount *origin)
{
while (1) {
@@ -505,6 +523,15 @@ static void restore_mounts(struct list_head *to_restore)
}
}
+static void cleanup_umount_visitations(struct list_head *visited)
+{
+ while (!list_empty(visited)) {
+ struct mount *mnt =
+ list_first_entry(visited, struct mount, mnt_umounting);
+ list_del_init(&mnt->mnt_umounting);
+ }
+}
+
/*
* collect all mounts that receive propagation from the mount in @list,
* and return these additional mounts in the same list.
@@ -517,11 +544,23 @@ int propagate_umount(struct list_head *list)
struct mount *mnt;
LIST_HEAD(to_restore);
LIST_HEAD(to_umount);
+ LIST_HEAD(visited);
- list_for_each_entry(mnt, list, mnt_list) {
+ /* Find candidates for unmounting */
+ list_for_each_entry_reverse(mnt, list, mnt_list) {
struct mount *parent = mnt->mnt_parent;
struct mount *m;
+ /*
+ * If this mount has already been visited it is known that it's
+ * entire peer group and all of their slaves in the propagation
+ * tree for the mountpoint has already been visited and there is
+ * no need to visit them again.
+ */
+ if (!list_empty(&mnt->mnt_umounting))
+ continue;
+
+ list_add_tail(&mnt->mnt_umounting, &visited);
for (m = propagation_next(parent, parent); m;
m = propagation_next(m, parent)) {
struct mount *child = __lookup_mnt(&m->mnt,
@@ -529,6 +568,27 @@ int propagate_umount(struct list_head *list)
if (!child)
continue;
+ if (!list_empty(&child->mnt_umounting)) {
+ /*
+ * If the child has already been visited it is
+ * know that it's entire peer group and all of
+ * their slaves in the propgation tree for the
+ * mountpoint has already been visited and there
+ * is no need to visit this subtree again.
+ */
+ m = skip_propagation_subtree(m, parent);
+ continue;
+ } else if (child->mnt.mnt_flags & MNT_UMOUNT) {
+ /*
+ * We have come accross an partially unmounted
+ * mount in list that has not been visited yet.
+ * Remember it has been visited and continue
+ * about our merry way.
+ */
+ list_add_tail(&child->mnt_umounting, &visited);
+ continue;
+ }
+
/* Check the child and parents while progress is made */
while (__propagate_umount(child,
&to_umount, &to_restore)) {
@@ -542,6 +602,7 @@ int propagate_umount(struct list_head *list)
umount_list(&to_umount, &to_restore);
restore_mounts(&to_restore);
+ cleanup_umount_visitations(&visited);
list_splice_tail(&to_umount, list);
return 0;
--
2.10.1
next prev parent reply other threads:[~2017-05-17 6:01 UTC|newest]
Thread overview: 63+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-31 4:10 [PATCH] Fix a race in put_mountpoint Krister Johansen
2016-12-31 6:17 ` Al Viro
2017-01-03 0:51 ` Eric W. Biederman
2017-01-03 1:48 ` Al Viro
2017-01-03 3:17 ` Eric W. Biederman
2017-01-03 4:00 ` Al Viro
2017-01-04 3:52 ` Eric W. Biederman
2017-01-04 3:53 ` [PATCH] mnt: Protect the mountpoint hashtable with mount_lock Eric W. Biederman
2017-01-04 21:04 ` [REVIEW][PATCH] mnt: Tuck mounts under others instead of creating shadow/side mounts Eric W. Biederman
2017-01-07 5:06 ` Al Viro
2017-01-11 0:10 ` Eric W. Biederman
2017-01-11 4:11 ` Al Viro
2017-01-11 16:03 ` Eric W. Biederman
2017-01-11 16:18 ` [REVIEW][PATCH 1/2] mnt: Fix propagate_mount_busy to notice all cases of busy mounts Eric W. Biederman
2017-01-11 16:19 ` [REVIEW][PATCH 2/2] mnt: Tuck mounts under others instead of creating shadow/side mounts Eric W. Biederman
2017-01-12 5:45 ` Al Viro
2017-01-20 7:20 ` Eric W. Biederman
2017-01-20 7:26 ` [PATCH v5] " Eric W. Biederman
2017-01-21 3:58 ` Ram Pai
2017-01-21 4:15 ` Eric W. Biederman
2017-01-23 19:02 ` Ram Pai
2017-01-24 0:16 ` Eric W. Biederman
2017-02-03 10:54 ` Eric W. Biederman
2017-02-03 17:10 ` Ram Pai
2017-02-03 18:26 ` Eric W. Biederman
2017-02-03 20:28 ` Ram Pai
2017-02-03 20:58 ` Eric W. Biederman
2017-02-06 3:25 ` Andrei Vagin
2017-02-06 21:40 ` Ram Pai
2017-02-07 6:35 ` Andrei Vagin
2017-01-12 5:30 ` [REVIEW][PATCH 1/2] mnt: Fix propagate_mount_busy to notice all cases of busy mounts Al Viro
2017-01-20 7:18 ` Eric W. Biederman
2017-01-13 20:32 ` Andrei Vagin
2017-01-18 19:20 ` Andrei Vagin
2017-01-20 23:18 ` Ram Pai
2017-01-23 8:15 ` Eric W. Biederman
2017-01-23 17:04 ` Ram Pai
2017-01-12 5:03 ` [REVIEW][PATCH] mnt: Tuck mounts under others instead of creating shadow/side mounts Al Viro
2017-05-14 2:15 ` Andrei Vagin
2017-05-14 4:05 ` Eric W. Biederman
2017-05-14 9:26 ` Eric W. Biederman
2017-05-15 18:27 ` Andrei Vagin
2017-05-15 19:42 ` Eric W. Biederman
2017-05-15 20:10 ` [REVIEW][PATCH] mnt: In umount propagation reparent in a separate pass Eric W. Biederman
2017-05-15 23:12 ` Andrei Vagin
2017-05-16 5:42 ` [PATCH] test: check a case when a mount is propagated between exiting mounts Andrei Vagin
2017-05-17 5:54 ` [REVIEW][PATCH 1/2] mnt: In propgate_umount handle visiting mounts in any order Eric W. Biederman
2017-05-17 5:55 ` Eric W. Biederman [this message]
2017-05-17 22:48 ` [REVIEW][PATCH 2/2] mnt: Make propagate_umount less slow for overlapping mount propagation trees Andrei Vagin
2017-05-17 23:26 ` Eric W. Biederman
2017-05-18 0:51 ` Andrei Vagin
2017-05-24 20:42 ` [REVIEW][PATCH 1/2] mnt: In propgate_umount handle visiting mounts in any order Ram Pai
2017-05-24 21:54 ` Eric W. Biederman
2017-05-24 22:35 ` Ram Pai
2017-05-30 6:07 ` Ram Pai
2017-05-30 15:07 ` Eric W. Biederman
2017-06-07 9:54 ` Ram Pai
2017-06-07 13:09 ` Eric W. Biederman
2017-05-22 8:15 ` [REVIEW][PATCH] mnt: In umount propagation reparent in a separate pass Ram Pai
2017-05-22 18:33 ` Eric W. Biederman
2017-05-22 22:34 ` Ram Pai
2017-05-23 13:58 ` Eric W. Biederman
2017-01-06 7:00 ` [PATCH] mnt: Protect the mountpoint hashtable with mount_lock Krister Johansen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=877f1gdo3f.fsf_-_@xmission.com \
--to=ebiederm@xmission.com \
--cc=avagin@virtuozzo.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linuxram@us.ibm.com \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.