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)
|
__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);
|
||||||
if (!inode->i_nlink)
|
if (!inode->i_nlink)
|
||||||
|
@ -1932,10 +1929,12 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
static void mntput_no_expire(struct mount *mnt)
|
static void mntput_no_expire(struct mount *mnt)
|
||||||
{
|
{
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
mnt_add_count(mnt, -1);
|
if (likely(READ_ONCE(mnt->mnt_ns))) {
|
||||||
if (likely(mnt->mnt_ns)) { /* shouldn't be the last one */
|
/*
|
||||||
|
* 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();
|
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();
|
||||||
|
|
Loading…
Reference in New Issue