summaryrefslogtreecommitdiffstats
path: root/fs/namespace.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2014-12-23 22:37:03 -0500
committerEric W. Biederman <ebiederm@xmission.com>2015-04-09 12:39:55 -0400
commitce07d891a0891d3c0d0c2d73d577490486b809e1 (patch)
treeb27cd472257302edfd6689e98b9991fa7d648ae0 /fs/namespace.c
parent820f9f147dcce2602eefd9b575bbbd9ea14f0953 (diff)
mnt: Honor MNT_LOCKED when detaching mounts
Modify umount(MNT_DETACH) to keep mounts in the hash table that are locked to their parent mounts, when the parent is lazily unmounted. In mntput_no_expire detach the children from the hash table, depending on mnt_pin_kill in cleanup_mnt to decrement the mnt_count of the children. In __detach_mounts if there are any mounts that have been unmounted but still are on the list of mounts of a mountpoint, remove their children from the mount hash table and those children to the unmounted list so they won't linger potentially indefinitely waiting for their final mntput, now that the mounts serve no purpose. Cc: stable@vger.kernel.org Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'fs/namespace.c')
-rw-r--r--fs/namespace.c29
1 files changed, 26 insertions, 3 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 010d5bebcb7e..1894d1878dbc 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1099,6 +1099,13 @@ static void mntput_no_expire(struct mount *mnt)
1099 rcu_read_unlock(); 1099 rcu_read_unlock();
1100 1100
1101 list_del(&mnt->mnt_instance); 1101 list_del(&mnt->mnt_instance);
1102
1103 if (unlikely(!list_empty(&mnt->mnt_mounts))) {
1104 struct mount *p, *tmp;
1105 list_for_each_entry_safe(p, tmp, &mnt->mnt_mounts, mnt_child) {
1106 umount_mnt(p);
1107 }
1108 }
1102 unlock_mount_hash(); 1109 unlock_mount_hash();
1103 1110
1104 if (likely(!(mnt->mnt.mnt_flags & MNT_INTERNAL))) { 1111 if (likely(!(mnt->mnt.mnt_flags & MNT_INTERNAL))) {
@@ -1370,6 +1377,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
1370 propagate_umount(&tmp_list); 1377 propagate_umount(&tmp_list);
1371 1378
1372 while (!list_empty(&tmp_list)) { 1379 while (!list_empty(&tmp_list)) {
1380 bool disconnect;
1373 p = list_first_entry(&tmp_list, struct mount, mnt_list); 1381 p = list_first_entry(&tmp_list, struct mount, mnt_list);
1374 list_del_init(&p->mnt_expire); 1382 list_del_init(&p->mnt_expire);
1375 list_del_init(&p->mnt_list); 1383 list_del_init(&p->mnt_list);
@@ -1378,10 +1386,18 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
1378 if (how & UMOUNT_SYNC) 1386 if (how & UMOUNT_SYNC)
1379 p->mnt.mnt_flags |= MNT_SYNC_UMOUNT; 1387 p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
1380 1388
1381 pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt, &unmounted); 1389 disconnect = !IS_MNT_LOCKED_AND_LAZY(p);
1390
1391 pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt,
1392 disconnect ? &unmounted : NULL);
1382 if (mnt_has_parent(p)) { 1393 if (mnt_has_parent(p)) {
1383 mnt_add_count(p->mnt_parent, -1); 1394 mnt_add_count(p->mnt_parent, -1);
1384 umount_mnt(p); 1395 if (!disconnect) {
1396 /* Don't forget about p */
1397 list_add_tail(&p->mnt_child, &p->mnt_parent->mnt_mounts);
1398 } else {
1399 umount_mnt(p);
1400 }
1385 } 1401 }
1386 change_mnt_propagation(p, MS_PRIVATE); 1402 change_mnt_propagation(p, MS_PRIVATE);
1387 } 1403 }
@@ -1506,7 +1522,14 @@ void __detach_mounts(struct dentry *dentry)
1506 lock_mount_hash(); 1522 lock_mount_hash();
1507 while (!hlist_empty(&mp->m_list)) { 1523 while (!hlist_empty(&mp->m_list)) {
1508 mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list); 1524 mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
1509 umount_tree(mnt, 0); 1525 if (mnt->mnt.mnt_flags & MNT_UMOUNT) {
1526 struct mount *p, *tmp;
1527 list_for_each_entry_safe(p, tmp, &mnt->mnt_mounts, mnt_child) {
1528 hlist_add_head(&p->mnt_umount.s_list, &unmounted);
1529 umount_mnt(p);
1530 }
1531 }
1532 else umount_tree(mnt, 0);
1510 } 1533 }
1511 unlock_mount_hash(); 1534 unlock_mount_hash();
1512 put_mountpoint(mp); 1535 put_mountpoint(mp);