diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2019-06-30 10:39:08 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2019-07-16 22:43:40 -0400 |
commit | 4edbe133f851c9e3a2f2a1db367e826b01e72594 (patch) | |
tree | efd25d386cbb1f6dcf85519f0ea318b7f2d04e87 | |
parent | 9bdebc2bd1c4abfbf44dc154cc152ec333e004de (diff) |
make struct mountpoint bear the dentry reference to mountpoint, not struct mount
Using dput_to_list() to shift the contributing reference from ->mnt_mountpoint
to ->mnt_mp->m_dentry. Dentries are dropped (with dput_to_list()) as soon
as struct mountpoint is destroyed; in cases where we are under namespace_sem
we use the global list, shrinking it in namespace_unlock(). In case of
detaching stuck MNT_LOCKed children at final mntput_no_expire() we use a local
list and shrink it ourselves. ->mnt_ex_mountpoint crap is gone.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/mount.h | 1 | ||||
-rw-r--r-- | fs/namespace.c | 52 |
2 files changed, 25 insertions, 28 deletions
diff --git a/fs/mount.h b/fs/mount.h index 6250de544760..84aa8cdf4971 100644 --- a/fs/mount.h +++ b/fs/mount.h | |||
@@ -69,7 +69,6 @@ struct mount { | |||
69 | int mnt_expiry_mark; /* true if marked for expiry */ | 69 | int mnt_expiry_mark; /* true if marked for expiry */ |
70 | struct hlist_head mnt_pins; | 70 | struct hlist_head mnt_pins; |
71 | struct fs_pin mnt_umount; | 71 | struct fs_pin mnt_umount; |
72 | struct dentry *mnt_ex_mountpoint; | ||
73 | } __randomize_layout; | 72 | } __randomize_layout; |
74 | 73 | ||
75 | #define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */ | 74 | #define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */ |
diff --git a/fs/namespace.c b/fs/namespace.c index b7059a4f07e3..e0902fda6f07 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -69,6 +69,8 @@ static struct hlist_head *mount_hashtable __read_mostly; | |||
69 | static struct hlist_head *mountpoint_hashtable __read_mostly; | 69 | static struct hlist_head *mountpoint_hashtable __read_mostly; |
70 | static struct kmem_cache *mnt_cache __read_mostly; | 70 | static struct kmem_cache *mnt_cache __read_mostly; |
71 | static DECLARE_RWSEM(namespace_sem); | 71 | static DECLARE_RWSEM(namespace_sem); |
72 | static HLIST_HEAD(unmounted); /* protected by namespace_sem */ | ||
73 | static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */ | ||
72 | 74 | ||
73 | /* /sys/fs */ | 75 | /* /sys/fs */ |
74 | struct kobject *fs_kobj; | 76 | struct kobject *fs_kobj; |
@@ -172,7 +174,6 @@ unsigned int mnt_get_count(struct mount *mnt) | |||
172 | static void drop_mountpoint(struct fs_pin *p) | 174 | static void drop_mountpoint(struct fs_pin *p) |
173 | { | 175 | { |
174 | struct mount *m = container_of(p, struct mount, mnt_umount); | 176 | struct mount *m = container_of(p, struct mount, mnt_umount); |
175 | dput(m->mnt_ex_mountpoint); | ||
176 | pin_remove(p); | 177 | pin_remove(p); |
177 | mntput(&m->mnt); | 178 | mntput(&m->mnt); |
178 | } | 179 | } |
@@ -739,7 +740,7 @@ mountpoint: | |||
739 | 740 | ||
740 | /* Add the new mountpoint to the hash table */ | 741 | /* Add the new mountpoint to the hash table */ |
741 | read_seqlock_excl(&mount_lock); | 742 | read_seqlock_excl(&mount_lock); |
742 | new->m_dentry = dentry; | 743 | new->m_dentry = dget(dentry); |
743 | new->m_count = 1; | 744 | new->m_count = 1; |
744 | hlist_add_head(&new->m_hash, mp_hash(dentry)); | 745 | hlist_add_head(&new->m_hash, mp_hash(dentry)); |
745 | INIT_HLIST_HEAD(&new->m_list); | 746 | INIT_HLIST_HEAD(&new->m_list); |
@@ -752,7 +753,11 @@ done: | |||
752 | return mp; | 753 | return mp; |
753 | } | 754 | } |
754 | 755 | ||
755 | static void put_mountpoint(struct mountpoint *mp) | 756 | /* |
757 | * vfsmount lock must be held. Additionally, the caller is responsible | ||
758 | * for serializing calls for given disposal list. | ||
759 | */ | ||
760 | static void __put_mountpoint(struct mountpoint *mp, struct list_head *list) | ||
756 | { | 761 | { |
757 | if (!--mp->m_count) { | 762 | if (!--mp->m_count) { |
758 | struct dentry *dentry = mp->m_dentry; | 763 | struct dentry *dentry = mp->m_dentry; |
@@ -760,11 +765,18 @@ static void put_mountpoint(struct mountpoint *mp) | |||
760 | spin_lock(&dentry->d_lock); | 765 | spin_lock(&dentry->d_lock); |
761 | dentry->d_flags &= ~DCACHE_MOUNTED; | 766 | dentry->d_flags &= ~DCACHE_MOUNTED; |
762 | spin_unlock(&dentry->d_lock); | 767 | spin_unlock(&dentry->d_lock); |
768 | dput_to_list(dentry, list); | ||
763 | hlist_del(&mp->m_hash); | 769 | hlist_del(&mp->m_hash); |
764 | kfree(mp); | 770 | kfree(mp); |
765 | } | 771 | } |
766 | } | 772 | } |
767 | 773 | ||
774 | /* called with namespace_lock and vfsmount lock */ | ||
775 | static void put_mountpoint(struct mountpoint *mp) | ||
776 | { | ||
777 | __put_mountpoint(mp, &ex_mountpoints); | ||
778 | } | ||
779 | |||
768 | static inline int check_mnt(struct mount *mnt) | 780 | static inline int check_mnt(struct mount *mnt) |
769 | { | 781 | { |
770 | return mnt->mnt_ns == current->nsproxy->mnt_ns; | 782 | return mnt->mnt_ns == current->nsproxy->mnt_ns; |
@@ -813,7 +825,7 @@ static struct mountpoint *unhash_mnt(struct mount *mnt) | |||
813 | */ | 825 | */ |
814 | static void detach_mnt(struct mount *mnt, struct path *old_path) | 826 | static void detach_mnt(struct mount *mnt, struct path *old_path) |
815 | { | 827 | { |
816 | old_path->dentry = mnt->mnt_mountpoint; | 828 | old_path->dentry = dget(mnt->mnt_mountpoint); |
817 | old_path->mnt = &mnt->mnt_parent->mnt; | 829 | old_path->mnt = &mnt->mnt_parent->mnt; |
818 | put_mountpoint(unhash_mnt(mnt)); | 830 | put_mountpoint(unhash_mnt(mnt)); |
819 | } | 831 | } |
@@ -823,8 +835,6 @@ static void detach_mnt(struct mount *mnt, struct path *old_path) | |||
823 | */ | 835 | */ |
824 | static void umount_mnt(struct mount *mnt) | 836 | static void umount_mnt(struct mount *mnt) |
825 | { | 837 | { |
826 | /* old mountpoint will be dropped when we can do that */ | ||
827 | mnt->mnt_ex_mountpoint = mnt->mnt_mountpoint; | ||
828 | put_mountpoint(unhash_mnt(mnt)); | 838 | put_mountpoint(unhash_mnt(mnt)); |
829 | } | 839 | } |
830 | 840 | ||
@@ -837,7 +847,7 @@ void mnt_set_mountpoint(struct mount *mnt, | |||
837 | { | 847 | { |
838 | mp->m_count++; | 848 | mp->m_count++; |
839 | mnt_add_count(mnt, 1); /* essentially, that's mntget */ | 849 | mnt_add_count(mnt, 1); /* essentially, that's mntget */ |
840 | child_mnt->mnt_mountpoint = dget(mp->m_dentry); | 850 | child_mnt->mnt_mountpoint = mp->m_dentry; |
841 | child_mnt->mnt_parent = mnt; | 851 | child_mnt->mnt_parent = mnt; |
842 | child_mnt->mnt_mp = mp; | 852 | child_mnt->mnt_mp = mp; |
843 | hlist_add_head(&child_mnt->mnt_mp_list, &mp->m_list); | 853 | hlist_add_head(&child_mnt->mnt_mp_list, &mp->m_list); |
@@ -864,7 +874,6 @@ static void attach_mnt(struct mount *mnt, | |||
864 | void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct mount *mnt) | 874 | void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct mount *mnt) |
865 | { | 875 | { |
866 | struct mountpoint *old_mp = mnt->mnt_mp; | 876 | struct mountpoint *old_mp = mnt->mnt_mp; |
867 | struct dentry *old_mountpoint = mnt->mnt_mountpoint; | ||
868 | struct mount *old_parent = mnt->mnt_parent; | 877 | struct mount *old_parent = mnt->mnt_parent; |
869 | 878 | ||
870 | list_del_init(&mnt->mnt_child); | 879 | list_del_init(&mnt->mnt_child); |
@@ -874,22 +883,6 @@ void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct m | |||
874 | attach_mnt(mnt, parent, mp); | 883 | attach_mnt(mnt, parent, mp); |
875 | 884 | ||
876 | put_mountpoint(old_mp); | 885 | put_mountpoint(old_mp); |
877 | |||
878 | /* | ||
879 | * Safely avoid even the suggestion this code might sleep or | ||
880 | * lock the mount hash by taking advantage of the knowledge that | ||
881 | * mnt_change_mountpoint will not release the final reference | ||
882 | * to a mountpoint. | ||
883 | * | ||
884 | * During mounting, the mount passed in as the parent mount will | ||
885 | * continue to use the old mountpoint and during unmounting, the | ||
886 | * old mountpoint will continue to exist until namespace_unlock, | ||
887 | * which happens well after mnt_change_mountpoint. | ||
888 | */ | ||
889 | spin_lock(&old_mountpoint->d_lock); | ||
890 | old_mountpoint->d_lockref.count--; | ||
891 | spin_unlock(&old_mountpoint->d_lock); | ||
892 | |||
893 | mnt_add_count(old_parent, -1); | 886 | mnt_add_count(old_parent, -1); |
894 | } | 887 | } |
895 | 888 | ||
@@ -1142,6 +1135,8 @@ static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput); | |||
1142 | 1135 | ||
1143 | static void mntput_no_expire(struct mount *mnt) | 1136 | static void mntput_no_expire(struct mount *mnt) |
1144 | { | 1137 | { |
1138 | LIST_HEAD(list); | ||
1139 | |||
1145 | rcu_read_lock(); | 1140 | rcu_read_lock(); |
1146 | if (likely(READ_ONCE(mnt->mnt_ns))) { | 1141 | if (likely(READ_ONCE(mnt->mnt_ns))) { |
1147 | /* | 1142 | /* |
@@ -1182,10 +1177,11 @@ static void mntput_no_expire(struct mount *mnt) | |||
1182 | if (unlikely(!list_empty(&mnt->mnt_mounts))) { | 1177 | if (unlikely(!list_empty(&mnt->mnt_mounts))) { |
1183 | struct mount *p, *tmp; | 1178 | struct mount *p, *tmp; |
1184 | list_for_each_entry_safe(p, tmp, &mnt->mnt_mounts, mnt_child) { | 1179 | list_for_each_entry_safe(p, tmp, &mnt->mnt_mounts, mnt_child) { |
1185 | umount_mnt(p); | 1180 | __put_mountpoint(unhash_mnt(p), &list); |
1186 | } | 1181 | } |
1187 | } | 1182 | } |
1188 | unlock_mount_hash(); | 1183 | unlock_mount_hash(); |
1184 | shrink_dentry_list(&list); | ||
1189 | 1185 | ||
1190 | if (likely(!(mnt->mnt.mnt_flags & MNT_INTERNAL))) { | 1186 | if (likely(!(mnt->mnt.mnt_flags & MNT_INTERNAL))) { |
1191 | struct task_struct *task = current; | 1187 | struct task_struct *task = current; |
@@ -1371,16 +1367,18 @@ int may_umount(struct vfsmount *mnt) | |||
1371 | 1367 | ||
1372 | EXPORT_SYMBOL(may_umount); | 1368 | EXPORT_SYMBOL(may_umount); |
1373 | 1369 | ||
1374 | static HLIST_HEAD(unmounted); /* protected by namespace_sem */ | ||
1375 | |||
1376 | static void namespace_unlock(void) | 1370 | static void namespace_unlock(void) |
1377 | { | 1371 | { |
1378 | struct hlist_head head; | 1372 | struct hlist_head head; |
1373 | LIST_HEAD(list); | ||
1379 | 1374 | ||
1380 | hlist_move_list(&unmounted, &head); | 1375 | hlist_move_list(&unmounted, &head); |
1376 | list_splice_init(&ex_mountpoints, &list); | ||
1381 | 1377 | ||
1382 | up_write(&namespace_sem); | 1378 | up_write(&namespace_sem); |
1383 | 1379 | ||
1380 | shrink_dentry_list(&list); | ||
1381 | |||
1384 | if (likely(hlist_empty(&head))) | 1382 | if (likely(hlist_empty(&head))) |
1385 | return; | 1383 | return; |
1386 | 1384 | ||