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
This commit is contained in:
commit
d6dd643159
13
fs/dcache.c
13
fs/dcache.c
|
@ -358,14 +358,11 @@ static void dentry_unlink_inode(struct dentry * dentry)
|
|||
__releases(dentry->d_inode->i_lock)
|
||||
{
|
||||
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);
|
||||
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(&inode->i_lock);
|
||||
if (!inode->i_nlink)
|
||||
|
@ -1932,10 +1929,12 @@ struct dentry *d_make_root(struct inode *root_inode)
|
|||
|
||||
if (root_inode) {
|
||||
res = d_alloc_anon(root_inode->i_sb);
|
||||
if (res)
|
||||
if (res) {
|
||||
res->d_flags |= DCACHE_RCUACCESS;
|
||||
d_instantiate(res, root_inode);
|
||||
else
|
||||
} else {
|
||||
iput(root_inode);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -659,12 +659,21 @@ int __legitimize_mnt(struct vfsmount *bastard, unsigned seq)
|
|||
return 0;
|
||||
mnt = real_mount(bastard);
|
||||
mnt_add_count(mnt, 1);
|
||||
smp_mb(); // see mntput_no_expire()
|
||||
if (likely(!read_seqretry(&mount_lock, seq)))
|
||||
return 0;
|
||||
if (bastard->mnt_flags & MNT_SYNC_UMOUNT) {
|
||||
mnt_add_count(mnt, -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;
|
||||
}
|
||||
|
||||
|
@ -1195,12 +1204,27 @@ static DECLARE_DELAYED_WORK(delayed_mntput_work, delayed_mntput);
|
|||
static void mntput_no_expire(struct mount *mnt)
|
||||
{
|
||||
rcu_read_lock();
|
||||
mnt_add_count(mnt, -1);
|
||||
if (likely(mnt->mnt_ns)) { /* shouldn't be the last one */
|
||||
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);
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
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)) {
|
||||
rcu_read_unlock();
|
||||
unlock_mount_hash();
|
||||
|
|
Loading…
Reference in New Issue