diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-12 14:21:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-12 14:21:17 -0400 |
commit | d6dd6431591b2af8580cd2b57f501352222edb4f (patch) | |
tree | 4f622fd74f38ee0e12b8e4fce1a2b3854ce20690 | |
parent | ec0c96714e7ddeda4eccaa077f5646a0fd6e371f (diff) | |
parent | 4c0d7cd5c8416b1ef41534d19163cb07ffaa03ab (diff) |
Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs fixes from Al Viro:
"A bunch of race fixes, mostly around lazy pathwalk.
All of it is -stable fodder, a large part going back to 2013"
* 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
make sure that __dentry_kill() always invalidates d_seq, unhashed or not
fix __legitimize_mnt()/mntput() race
fix mntput/mntput race
root dentries need RCU-delayed freeing
-rw-r--r-- | fs/dcache.c | 13 | ||||
-rw-r--r-- | fs/namespace.c | 28 |
2 files changed, 32 insertions, 9 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 0e8e5de3c48a..ceb7b491d1b9 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -358,14 +358,11 @@ static void dentry_unlink_inode(struct dentry * dentry) | |||
358 | __releases(dentry->d_inode->i_lock) | 358 | __releases(dentry->d_inode->i_lock) |
359 | { | 359 | { |
360 | struct inode *inode = dentry->d_inode; | 360 | struct inode *inode = dentry->d_inode; |
361 | bool hashed = !d_unhashed(dentry); | ||
362 | 361 | ||
363 | if (hashed) | 362 | raw_write_seqcount_begin(&dentry->d_seq); |
364 | raw_write_seqcount_begin(&dentry->d_seq); | ||
365 | __d_clear_type_and_inode(dentry); | 363 | __d_clear_type_and_inode(dentry); |
366 | hlist_del_init(&dentry->d_u.d_alias); | 364 | hlist_del_init(&dentry->d_u.d_alias); |
367 | if (hashed) | 365 | raw_write_seqcount_end(&dentry->d_seq); |
368 | raw_write_seqcount_end(&dentry->d_seq); | ||
369 | spin_unlock(&dentry->d_lock); | 366 | spin_unlock(&dentry->d_lock); |
370 | spin_unlock(&inode->i_lock); | 367 | spin_unlock(&inode->i_lock); |
371 | if (!inode->i_nlink) | 368 | if (!inode->i_nlink) |
@@ -1932,10 +1929,12 @@ struct dentry *d_make_root(struct inode *root_inode) | |||
1932 | 1929 | ||
1933 | if (root_inode) { | 1930 | if (root_inode) { |
1934 | res = d_alloc_anon(root_inode->i_sb); | 1931 | res = d_alloc_anon(root_inode->i_sb); |
1935 | if (res) | 1932 | if (res) { |
1933 | res->d_flags |= DCACHE_RCUACCESS; | ||
1936 | d_instantiate(res, root_inode); | 1934 | d_instantiate(res, root_inode); |
1937 | else | 1935 | } else { |
1938 | iput(root_inode); | 1936 | iput(root_inode); |
1937 | } | ||
1939 | } | 1938 | } |
1940 | return res; | 1939 | return res; |
1941 | } | 1940 | } |
diff --git a/fs/namespace.c b/fs/namespace.c index 8ddd14806799..bd2f4c68506a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -659,12 +659,21 @@ int __legitimize_mnt(struct vfsmount *bastard, unsigned seq) | |||
659 | return 0; | 659 | return 0; |
660 | mnt = real_mount(bastard); | 660 | mnt = real_mount(bastard); |
661 | mnt_add_count(mnt, 1); | 661 | mnt_add_count(mnt, 1); |
662 | smp_mb(); // see mntput_no_expire() | ||
662 | if (likely(!read_seqretry(&mount_lock, seq))) | 663 | if (likely(!read_seqretry(&mount_lock, seq))) |
663 | return 0; | 664 | return 0; |
664 | if (bastard->mnt_flags & MNT_SYNC_UMOUNT) { | 665 | if (bastard->mnt_flags & MNT_SYNC_UMOUNT) { |
665 | mnt_add_count(mnt, -1); | 666 | mnt_add_count(mnt, -1); |
666 | return 1; | 667 | return 1; |
667 | } | 668 | } |
669 | lock_mount_hash(); | ||
670 | if (unlikely(bastard->mnt_flags & MNT_DOOMED)) { | ||
671 | mnt_add_count(mnt, -1); | ||
672 | unlock_mount_hash(); | ||
673 | return 1; | ||
674 | } | ||
675 | unlock_mount_hash(); | ||
676 | /* caller will mntput() */ | ||
668 | return -1; | 677 | return -1; |
669 | } | 678 | } |
670 | 679 | ||
@@ -1195,12 +1204,27 @@ static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput); | |||
1195 | static void mntput_no_expire(struct mount *mnt) | 1204 | static void mntput_no_expire(struct mount *mnt) |
1196 | { | 1205 | { |
1197 | rcu_read_lock(); | 1206 | rcu_read_lock(); |
1198 | mnt_add_count(mnt, -1); | 1207 | if (likely(READ_ONCE(mnt->mnt_ns))) { |
1199 | if (likely(mnt->mnt_ns)) { /* shouldn't be the last one */ | 1208 | /* |
1209 | * Since we don't do lock_mount_hash() here, | ||
1210 | * ->mnt_ns can change under us. However, if it's | ||
1211 | * non-NULL, then there's a reference that won't | ||
1212 | * be dropped until after an RCU delay done after | ||
1213 | * turning ->mnt_ns NULL. So if we observe it | ||
1214 | * non-NULL under rcu_read_lock(), the reference | ||
1215 | * we are dropping is not the final one. | ||
1216 | */ | ||
1217 | mnt_add_count(mnt, -1); | ||
1200 | rcu_read_unlock(); | 1218 | rcu_read_unlock(); |
1201 | return; | 1219 | return; |
1202 | } | 1220 | } |
1203 | lock_mount_hash(); | 1221 | lock_mount_hash(); |
1222 | /* | ||
1223 | * make sure that if __legitimize_mnt() has not seen us grab | ||
1224 | * mount_lock, we'll see their refcount increment here. | ||
1225 | */ | ||
1226 | smp_mb(); | ||
1227 | mnt_add_count(mnt, -1); | ||
1204 | if (mnt_get_count(mnt)) { | 1228 | if (mnt_get_count(mnt)) { |
1205 | rcu_read_unlock(); | 1229 | rcu_read_unlock(); |
1206 | unlock_mount_hash(); | 1230 | unlock_mount_hash(); |