Skip to content

Commit eb85581

Browse files
braunergregkh
authored andcommitted
fs: better handle deep ancestor chains in is_subdir()
[ Upstream commit 391b59b ] Jan reported that 'cd ..' may take a long time in deep directory hierarchies under a bind-mount. If concurrent renames happen it is possible to livelock in is_subdir() because it will keep retrying. Change is_subdir() from simply retrying over and over to retry once and then acquire the rename lock to handle deep ancestor chains better. The list of alternatives to this approach were less then pleasant. Change the scope of rcu lock to cover the whole walk while at it. A big thanks to Jan and Linus. Both Jan and Linus had proposed effectively the same thing just that one version ended up being slightly more elegant. Reported-by: Jan Kara <jack@suse.cz> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 437c043 commit eb85581

1 file changed

Lines changed: 14 additions & 17 deletions

File tree

fs/dcache.c

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3035,28 +3035,25 @@ EXPORT_SYMBOL(d_splice_alias);
30353035

30363036
bool is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
30373037
{
3038-
bool result;
3038+
bool subdir;
30393039
unsigned seq;
30403040

30413041
if (new_dentry == old_dentry)
30423042
return true;
30433043

3044-
do {
3045-
/* for restarting inner loop in case of seq retry */
3046-
seq = read_seqbegin(&rename_lock);
3047-
/*
3048-
* Need rcu_readlock to protect against the d_parent trashing
3049-
* due to d_move
3050-
*/
3051-
rcu_read_lock();
3052-
if (d_ancestor(old_dentry, new_dentry))
3053-
result = true;
3054-
else
3055-
result = false;
3056-
rcu_read_unlock();
3057-
} while (read_seqretry(&rename_lock, seq));
3058-
3059-
return result;
3044+
/* Access d_parent under rcu as d_move() may change it. */
3045+
rcu_read_lock();
3046+
seq = read_seqbegin(&rename_lock);
3047+
subdir = d_ancestor(old_dentry, new_dentry);
3048+
/* Try lockless once... */
3049+
if (read_seqretry(&rename_lock, seq)) {
3050+
/* ...else acquire lock for progress even on deep chains. */
3051+
read_seqlock_excl(&rename_lock);
3052+
subdir = d_ancestor(old_dentry, new_dentry);
3053+
read_sequnlock_excl(&rename_lock);
3054+
}
3055+
rcu_read_unlock();
3056+
return subdir;
30603057
}
30613058
EXPORT_SYMBOL(is_subdir);
30623059

0 commit comments

Comments
 (0)