Commit d6dd6431 authored by Linus Torvalds's avatar Linus Torvalds

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
parents ec0c9671 4c0d7cd5
...@@ -358,13 +358,10 @@ static void dentry_unlink_inode(struct dentry * dentry) ...@@ -358,13 +358,10 @@ static void dentry_unlink_inode(struct dentry * dentry)
__releases(dentry->d_inode->i_lock) __releases(dentry->d_inode->i_lock)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
bool hashed = !d_unhashed(dentry);
if (hashed)
raw_write_seqcount_begin(&dentry->d_seq); raw_write_seqcount_begin(&dentry->d_seq);
__d_clear_type_and_inode(dentry); __d_clear_type_and_inode(dentry);
hlist_del_init(&dentry->d_u.d_alias); hlist_del_init(&dentry->d_u.d_alias);
if (hashed)
raw_write_seqcount_end(&dentry->d_seq); raw_write_seqcount_end(&dentry->d_seq);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
...@@ -1932,11 +1929,13 @@ struct dentry *d_make_root(struct inode *root_inode) ...@@ -1932,11 +1929,13 @@ struct dentry *d_make_root(struct inode *root_inode)
if (root_inode) { if (root_inode) {
res = d_alloc_anon(root_inode->i_sb); res = d_alloc_anon(root_inode->i_sb);
if (res) if (res) {
res->d_flags |= DCACHE_RCUACCESS;
d_instantiate(res, root_inode); d_instantiate(res, root_inode);
else } else {
iput(root_inode); iput(root_inode);
} }
}
return res; return res;
} }
EXPORT_SYMBOL(d_make_root); EXPORT_SYMBOL(d_make_root);
......
...@@ -659,12 +659,21 @@ int __legitimize_mnt(struct vfsmount *bastard, unsigned seq) ...@@ -659,12 +659,21 @@ int __legitimize_mnt(struct vfsmount *bastard, unsigned seq)
return 0; return 0;
mnt = real_mount(bastard); mnt = real_mount(bastard);
mnt_add_count(mnt, 1); mnt_add_count(mnt, 1);
smp_mb(); // see mntput_no_expire()
if (likely(!read_seqretry(&mount_lock, seq))) if (likely(!read_seqretry(&mount_lock, seq)))
return 0; return 0;
if (bastard->mnt_flags & MNT_SYNC_UMOUNT) { if (bastard->mnt_flags & MNT_SYNC_UMOUNT) {
mnt_add_count(mnt, -1); mnt_add_count(mnt, -1);
return 1; return 1;
} }
lock_mount_hash();
if (unlikely(bastard->mnt_flags & MNT_DOOMED)) {
mnt_add_count(mnt, -1);
unlock_mount_hash();
return 1;
}
unlock_mount_hash();
/* caller will mntput() */
return -1; return -1;
} }
...@@ -1195,12 +1204,27 @@ static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput); ...@@ -1195,12 +1204,27 @@ static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput);
static void mntput_no_expire(struct mount *mnt) static void mntput_no_expire(struct mount *mnt)
{ {
rcu_read_lock(); rcu_read_lock();
if (likely(READ_ONCE(mnt->mnt_ns))) {
/*
* Since we don't do lock_mount_hash() here,
* ->mnt_ns can change under us. However, if it's
* non-NULL, then there's a reference that won't
* be dropped until after an RCU delay done after
* turning ->mnt_ns NULL. So if we observe it
* non-NULL under rcu_read_lock(), the reference
* we are dropping is not the final one.
*/
mnt_add_count(mnt, -1); mnt_add_count(mnt, -1);
if (likely(mnt->mnt_ns)) { /* shouldn't be the last one */
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }
lock_mount_hash(); lock_mount_hash();
/*
* make sure that if __legitimize_mnt() has not seen us grab
* mount_lock, we'll see their refcount increment here.
*/
smp_mb();
mnt_add_count(mnt, -1);
if (mnt_get_count(mnt)) { if (mnt_get_count(mnt)) {
rcu_read_unlock(); rcu_read_unlock();
unlock_mount_hash(); unlock_mount_hash();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment