diff options
Diffstat (limited to 'fs/dcache.c')
| -rw-r--r-- | fs/dcache.c | 21 | 
1 files changed, 16 insertions, 5 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 7c38f39958bc..8945e6cabd93 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -647,11 +647,16 @@ again:  		spin_unlock(&parent->d_lock);  		goto again;  	} -	rcu_read_unlock(); -	if (parent != dentry) +	if (parent != dentry) {  		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); -	else +		if (unlikely(dentry->d_lockref.count < 0)) { +			spin_unlock(&parent->d_lock); +			parent = NULL; +		} +	} else {  		parent = NULL; +	} +	rcu_read_unlock();  	return parent;  } @@ -2474,7 +2479,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent,  retry:  	rcu_read_lock(); -	seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1; +	seq = smp_load_acquire(&parent->d_inode->i_dir_seq);  	r_seq = read_seqbegin(&rename_lock);  	dentry = __d_lookup_rcu(parent, name, &d_seq);  	if (unlikely(dentry)) { @@ -2495,8 +2500,14 @@ retry:  		rcu_read_unlock();  		goto retry;  	} + +	if (unlikely(seq & 1)) { +		rcu_read_unlock(); +		goto retry; +	} +  	hlist_bl_lock(b); -	if (unlikely(parent->d_inode->i_dir_seq != seq)) { +	if (unlikely(READ_ONCE(parent->d_inode->i_dir_seq) != seq)) {  		hlist_bl_unlock(b);  		rcu_read_unlock();  		goto retry;  |