Skip to content

Commit 955336e

Browse files
author
Al Viro
committed
do_make_slave(): choose new master sanely
When mount changes propagation type so that it doesn't propagate events any more (MS_PRIVATE, MS_SLAVE, MS_UNBINDABLE), we need to make sure that event propagation between other mounts is unaffected. We need to make sure that events from peers and master of that mount (if any) still reach everything that used to be on its ->mnt_slave_list. If mount has neither peers nor master, we simply need to dissolve its ->mnt_slave_list and clear ->mnt_master of everything in there. If mount has peers, we transfer everything in ->mnt_slave_list of this mount into that of some of those peers (and adjust ->mnt_master accordingly). If mount has a master but no peers, we transfer everything in ->mnt_slave_list of this mount into that of its master (adjusting ->mnt_master, etc.). There are two problems with the current implementation: * there's a long-obsolete logics in choosing the peer - once upon a time it made sense to prefer the peer that had the same ->mnt_root as our mount, but that had been pointless since 2014 ("smarter propagate_mnt()") * the most common caller of that thing is umount_tree() taking the mounts out of propagation graph. In that case it's possible to have ->mnt_slave_list contents moved many times, since the replacement master is likely to be taken out by the same umount_tree(), etc. Take the choice of replacement master into a separate function (propagation_source()) and teach it to skip the candidates that are going to be taken out. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent ef86251 commit 955336e

File tree

1 file changed

+31
-31
lines changed

1 file changed

+31
-31
lines changed

fs/pnode.c

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -65,40 +65,45 @@ int get_dominating_id(struct mount *mnt, const struct path *root)
6565
return 0;
6666
}
6767

68+
static inline bool will_be_unmounted(struct mount *m)
69+
{
70+
return m->mnt.mnt_flags & MNT_UMOUNT;
71+
}
72+
73+
static struct mount *propagation_source(struct mount *mnt)
74+
{
75+
do {
76+
struct mount *m;
77+
for (m = next_peer(mnt); m != mnt; m = next_peer(m)) {
78+
if (!will_be_unmounted(m))
79+
return m;
80+
}
81+
mnt = mnt->mnt_master;
82+
} while (mnt && will_be_unmounted(mnt));
83+
return mnt;
84+
}
85+
6886
static int do_make_slave(struct mount *mnt)
6987
{
70-
struct mount *master, *slave_mnt;
88+
struct mount *master = propagation_source(mnt);
89+
struct mount *slave_mnt;
7190

7291
if (list_empty(&mnt->mnt_share)) {
7392
mnt_release_group_id(mnt);
74-
CLEAR_MNT_SHARED(mnt);
75-
master = mnt->mnt_master;
76-
if (!master) {
77-
struct list_head *p = &mnt->mnt_slave_list;
78-
while (!list_empty(p)) {
79-
slave_mnt = list_first_entry(p,
80-
struct mount, mnt_slave);
81-
list_del_init(&slave_mnt->mnt_slave);
82-
slave_mnt->mnt_master = NULL;
83-
}
84-
return 0;
85-
}
8693
} else {
87-
struct mount *m;
88-
/*
89-
* slave 'mnt' to a peer mount that has the
90-
* same root dentry. If none is available then
91-
* slave it to anything that is available.
92-
*/
93-
for (m = master = next_peer(mnt); m != mnt; m = next_peer(m)) {
94-
if (m->mnt.mnt_root == mnt->mnt.mnt_root) {
95-
master = m;
96-
break;
97-
}
98-
}
9994
list_del_init(&mnt->mnt_share);
10095
mnt->mnt_group_id = 0;
101-
CLEAR_MNT_SHARED(mnt);
96+
}
97+
CLEAR_MNT_SHARED(mnt);
98+
if (!master) {
99+
struct list_head *p = &mnt->mnt_slave_list;
100+
while (!list_empty(p)) {
101+
slave_mnt = list_first_entry(p,
102+
struct mount, mnt_slave);
103+
list_del_init(&slave_mnt->mnt_slave);
104+
slave_mnt->mnt_master = NULL;
105+
}
106+
return 0;
102107
}
103108
list_for_each_entry(slave_mnt, &mnt->mnt_slave_list, mnt_slave)
104109
slave_mnt->mnt_master = master;
@@ -443,11 +448,6 @@ static inline bool is_candidate(struct mount *m)
443448
return m->mnt_t_flags & T_UMOUNT_CANDIDATE;
444449
}
445450

446-
static inline bool will_be_unmounted(struct mount *m)
447-
{
448-
return m->mnt.mnt_flags & MNT_UMOUNT;
449-
}
450-
451451
static void umount_one(struct mount *m, struct list_head *to_umount)
452452
{
453453
m->mnt.mnt_flags |= MNT_UMOUNT;

0 commit comments

Comments
 (0)