From: Al Viro Date: Mon, 20 Oct 2025 17:28:58 +0000 (-0400) Subject: d_alloc_parallel(): set DCACHE_PAR_LOOKUP earlier X-Git-Tag: v6.12.55~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dc63d878146321f0d736f4cd9f0802283968ab49;p=thirdparty%2Fkernel%2Fstable.git d_alloc_parallel(): set DCACHE_PAR_LOOKUP earlier [ Upstream commit e95db51c81f54dd12ea465b5127e4786f62a1095 ] Do that before new dentry is visible anywhere. It does create a new possible state for dentries present in ->d_children/->d_sib - DCACHE_PAR_LOOKUP present, negative, unhashed, not in in-lookup hash chains, refcount positive. Those are going to be skipped by all tree-walkers (both d_walk() callbacks in fs/dcache.c and explicit loops over children/sibling lists elsewhere) and dput() is fine with those. NOTE: dropping the final reference to a "normal" in-lookup dentry (in in-lookup hash) is a bug - somebody must've forgotten to call d_lookup_done() on it and bad things will happen. With those it's OK; if/when we get around to making __dentry_kill() complain about such breakage, remember that predicate to check should *not* be just d_in_lookup(victim) but rather a combination of that with !hlist_bl_unhashed(&victim->d_u.d_in_lookup_hash). Might be worth considering later... Reviewed-by: Christian Brauner Signed-off-by: Al Viro Stable-dep-of: 56094ad3eaa2 ("vfs: Don't leak disconnected dentries on umount") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- diff --git a/fs/dcache.c b/fs/dcache.c index 0f6b16ba30d08..d81765352cf81 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2475,13 +2475,19 @@ struct dentry *d_alloc_parallel(struct dentry *parent, unsigned int hash = name->hash; struct hlist_bl_head *b = in_lookup_hash(parent, hash); struct hlist_bl_node *node; - struct dentry *new = d_alloc(parent, name); + struct dentry *new = __d_alloc(parent->d_sb, name); struct dentry *dentry; unsigned seq, r_seq, d_seq; if (unlikely(!new)) return ERR_PTR(-ENOMEM); + new->d_flags |= DCACHE_PAR_LOOKUP; + spin_lock(&parent->d_lock); + new->d_parent = dget_dlock(parent); + hlist_add_head(&new->d_sib, &parent->d_children); + spin_unlock(&parent->d_lock); + retry: rcu_read_lock(); seq = smp_load_acquire(&parent->d_inode->i_dir_seq); @@ -2565,8 +2571,6 @@ retry: return dentry; } rcu_read_unlock(); - /* we can't take ->d_lock here; it's OK, though. */ - new->d_flags |= DCACHE_PAR_LOOKUP; new->d_wait = wq; hlist_bl_add_head(&new->d_u.d_in_lookup_hash, b); hlist_bl_unlock(b);