Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

umount_tree(): take all victims out of propagation graph at once

For each removed mount we need to calculate where the slaves will end up.
To avoid duplicating that work, do it for all mounts to be removed
at once, taking the mounts themselves out of propagation graph as
we go, then do all transfers; the duplicate work on finding destinations
is avoided since if we run into a mount that already had destination found,
we don't need to trace the rest of the way. That's guaranteed
O(removed mounts) for finding destinations and removing from propagation
graph and O(surviving mounts that have master removed) for transfers.

Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 75db7fd9 fc9d5efc

+55 -16
+2 -1
fs/namespace.c
··· 1846 1846 if (how & UMOUNT_PROPAGATE) 1847 1847 propagate_umount(&tmp_list); 1848 1848 1849 + bulk_make_private(&tmp_list); 1850 + 1849 1851 while (!list_empty(&tmp_list)) { 1850 1852 struct mnt_namespace *ns; 1851 1853 bool disconnect; ··· 1872 1870 umount_mnt(p); 1873 1871 } 1874 1872 } 1875 - change_mnt_propagation(p, MS_PRIVATE); 1876 1873 if (disconnect) 1877 1874 hlist_add_head(&p->mnt_umount, &unmounted); 1878 1875
+52 -15
fs/pnode.c
··· 71 71 return m->mnt.mnt_flags & MNT_UMOUNT; 72 72 } 73 73 74 - static struct mount *propagation_source(struct mount *mnt) 75 - { 76 - do { 77 - struct mount *m; 78 - for (m = next_peer(mnt); m != mnt; m = next_peer(m)) { 79 - if (!will_be_unmounted(m)) 80 - return m; 81 - } 82 - mnt = mnt->mnt_master; 83 - } while (mnt && will_be_unmounted(mnt)); 84 - return mnt; 85 - } 86 - 87 74 static void transfer_propagation(struct mount *mnt, struct mount *to) 88 75 { 89 76 struct hlist_node *p = NULL, *n; ··· 99 112 return; 100 113 } 101 114 if (IS_MNT_SHARED(mnt)) { 102 - if (type == MS_SLAVE || !hlist_empty(&mnt->mnt_slave_list)) 103 - m = propagation_source(mnt); 104 115 if (list_empty(&mnt->mnt_share)) { 105 116 mnt_release_group_id(mnt); 106 117 } else { 118 + m = next_peer(mnt); 107 119 list_del_init(&mnt->mnt_share); 108 120 mnt->mnt_group_id = 0; 109 121 } ··· 120 134 mnt->mnt_t_flags |= T_UNBINDABLE; 121 135 else 122 136 mnt->mnt_t_flags &= ~T_UNBINDABLE; 137 + } 138 + } 139 + 140 + static struct mount *trace_transfers(struct mount *m) 141 + { 142 + while (1) { 143 + struct mount *next = next_peer(m); 144 + 145 + if (next != m) { 146 + list_del_init(&m->mnt_share); 147 + m->mnt_group_id = 0; 148 + m->mnt_master = next; 149 + } else { 150 + if (IS_MNT_SHARED(m)) 151 + mnt_release_group_id(m); 152 + next = m->mnt_master; 153 + } 154 + hlist_del_init(&m->mnt_slave); 155 + CLEAR_MNT_SHARED(m); 156 + SET_MNT_MARK(m); 157 + 158 + if (!next || !will_be_unmounted(next)) 159 + return next; 160 + if (IS_MNT_MARKED(next)) 161 + return next->mnt_master; 162 + m = next; 163 + } 164 + } 165 + 166 + static void set_destinations(struct mount *m, struct mount *master) 167 + { 168 + struct mount *next; 169 + 170 + while ((next = m->mnt_master) != master) { 171 + m->mnt_master = master; 172 + m = next; 173 + } 174 + } 175 + 176 + void bulk_make_private(struct list_head *set) 177 + { 178 + struct mount *m; 179 + 180 + list_for_each_entry(m, set, mnt_list) 181 + if (!IS_MNT_MARKED(m)) 182 + set_destinations(m, trace_transfers(m)); 183 + 184 + list_for_each_entry(m, set, mnt_list) { 185 + transfer_propagation(m, m->mnt_master); 186 + m->mnt_master = NULL; 187 + CLEAR_MNT_MARK(m); 123 188 } 124 189 } 125 190
+1
fs/pnode.h
··· 42 42 } 43 43 44 44 void change_mnt_propagation(struct mount *, int); 45 + void bulk_make_private(struct list_head *); 45 46 int propagate_mnt(struct mount *, struct mountpoint *, struct mount *, 46 47 struct hlist_head *); 47 48 void propagate_umount(struct list_head *);