mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-12 12:25:18 +02:00
fs: better handle deep ancestor chains in is_subdir()
[ Upstream commit 391b59b045
]
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>
This commit is contained in:
parent
f13c96e0e3
commit
c09e07857c
31
fs/dcache.c
31
fs/dcache.c
|
@ -3208,28 +3208,25 @@ EXPORT_SYMBOL(d_splice_alias);
|
||||||
|
|
||||||
bool is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
|
bool is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
|
||||||
{
|
{
|
||||||
bool result;
|
bool subdir;
|
||||||
unsigned seq;
|
unsigned seq;
|
||||||
|
|
||||||
if (new_dentry == old_dentry)
|
if (new_dentry == old_dentry)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
do {
|
/* Access d_parent under rcu as d_move() may change it. */
|
||||||
/* for restarting inner loop in case of seq retry */
|
rcu_read_lock();
|
||||||
seq = read_seqbegin(&rename_lock);
|
seq = read_seqbegin(&rename_lock);
|
||||||
/*
|
subdir = d_ancestor(old_dentry, new_dentry);
|
||||||
* Need rcu_readlock to protect against the d_parent trashing
|
/* Try lockless once... */
|
||||||
* due to d_move
|
if (read_seqretry(&rename_lock, seq)) {
|
||||||
*/
|
/* ...else acquire lock for progress even on deep chains. */
|
||||||
rcu_read_lock();
|
read_seqlock_excl(&rename_lock);
|
||||||
if (d_ancestor(old_dentry, new_dentry))
|
subdir = d_ancestor(old_dentry, new_dentry);
|
||||||
result = true;
|
read_sequnlock_excl(&rename_lock);
|
||||||
else
|
}
|
||||||
result = false;
|
rcu_read_unlock();
|
||||||
rcu_read_unlock();
|
return subdir;
|
||||||
} while (read_seqretry(&rename_lock, seq));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(is_subdir);
|
EXPORT_SYMBOL(is_subdir);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user