aboutsummaryrefslogtreecommitdiffstats
path: root/fs/namespace.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-02-27 09:35:45 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2014-04-01 23:19:08 -0400
commitf2ebb3a921c1ca1e2ddd9242e95a1989a50c4c68 (patch)
tree0a14b8ec08d94aff427a5446b8b5c4e68b4e032e /fs/namespace.c
parent38129a13e6e71f666e0468e99fdd932a687b4d7e (diff)
smarter propagate_mnt()
The current mainline has copies propagated to *all* nodes, then tears down the copies we made for nodes that do not contain counterparts of the desired mountpoint. That sets the right propagation graph for the copies (at teardown time we move the slaves of removed node to a surviving peer or directly to master), but we end up paying a fairly steep price in useless allocations. It's fairly easy to create a situation where N calls of mount(2) create exactly N bindings, with O(N^2) vfsmounts allocated and freed in process. Fortunately, it is possible to avoid those allocations/freeings. The trick is to create copies in the right order and find which one would've eventually become a master with the current algorithm. It turns out to be possible in O(nodes getting propagation) time and with no extra allocations at all. One part is that we need to make sure that eventual master will be created before its slaves, so we need to walk the propagation tree in a different order - by peer groups. And iterate through the peers before dealing with the next group. Another thing is finding the (earlier) copy that will be a master of one we are about to create; to do that we are (temporary) marking the masters of mountpoints we are attaching the copies to. Either we are in a peer of the last mountpoint we'd dealt with, or we have the following situation: we are attaching to mountpoint M, the last copy S_0 had been attached to M_0 and there are sequences S_0...S_n, M_0...M_n such that S_{i+1} is a master of S_{i}, S_{i} mounted on M{i} and we need to create a slave of the first S_{k} such that M is getting propagation from M_{k}. It means that the master of M_{k} will be among the sequence of masters of M. On the other hand, the nearest marked node in that sequence will either be the master of M_{k} or the master of M_{k-1} (the latter - in the case if M_{k-1} is a slave of something M gets propagation from, but in a wrong peer group). So we go through the sequence of masters of M until we find a marked one (P). Let N be the one before it. Then we go through the sequence of masters of S_0 until we find one (say, S) mounted on a node D that has P as master and check if D is a peer of N. If it is, S will be the master of new copy, if not - the master of S will be. That's it for the hard part; the rest is fairly simple. Iterator is in next_group(), handling of one prospective mountpoint is propagate_one(). It seems to survive all tests and gives a noticably better performance than the current mainline for setups that are seriously using shared subtrees. Cc: stable@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namespace.c')
-rw-r--r--fs/namespace.c11
1 files changed, 8 insertions, 3 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 2ffc5a2905d4..65233a5f390a 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -885,7 +885,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
885 goto out_free; 885 goto out_free;
886 } 886 }
887 887
888 mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD; 888 mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED);
889 /* Don't allow unprivileged users to change mount flags */ 889 /* Don't allow unprivileged users to change mount flags */
890 if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) 890 if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
891 mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; 891 mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
@@ -1661,9 +1661,9 @@ static int attach_recursive_mnt(struct mount *source_mnt,
1661 if (err) 1661 if (err)
1662 goto out; 1662 goto out;
1663 err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list); 1663 err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list);
1664 lock_mount_hash();
1664 if (err) 1665 if (err)
1665 goto out_cleanup_ids; 1666 goto out_cleanup_ids;
1666 lock_mount_hash();
1667 for (p = source_mnt; p; p = next_mnt(p, source_mnt)) 1667 for (p = source_mnt; p; p = next_mnt(p, source_mnt))
1668 set_mnt_shared(p); 1668 set_mnt_shared(p);
1669 } else { 1669 } else {
@@ -1690,6 +1690,11 @@ static int attach_recursive_mnt(struct mount *source_mnt,
1690 return 0; 1690 return 0;
1691 1691
1692 out_cleanup_ids: 1692 out_cleanup_ids:
1693 while (!hlist_empty(&tree_list)) {
1694 child = hlist_entry(tree_list.first, struct mount, mnt_hash);
1695 umount_tree(child, 0);
1696 }
1697 unlock_mount_hash();
1693 cleanup_group_ids(source_mnt, NULL); 1698 cleanup_group_ids(source_mnt, NULL);
1694 out: 1699 out:
1695 return err; 1700 return err;
@@ -2044,7 +2049,7 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
2044 struct mount *parent; 2049 struct mount *parent;
2045 int err; 2050 int err;
2046 2051
2047 mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | MNT_DOOMED | MNT_SYNC_UMOUNT); 2052 mnt_flags &= ~MNT_INTERNAL_FLAGS;
2048 2053
2049 mp = lock_mount(path); 2054 mp = lock_mount(path);
2050 if (IS_ERR(mp)) 2055 if (IS_ERR(mp))