Re: [PATCH v3 04/19] VFS: use wait_var_event for waiting in d_alloc_parallel()

Al Viro <[email protected]> Wed, 29 Apr 2026 06:26:26 +0100
Newsgroups org.kernel.vger.linux-unionfs,org.kernel.vger.linux-efi,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-nfs
Message-ID <20260429052626.GY3518998@ZenIV>
On Wed, Apr 29, 2026 at 09:26:54AM +1000, NeilBrown wrote:

> > More to the point, why not set DCACHE_LOCK_WAITER as soon as we grab ->d_lock
> > there?  Then waiting becomes simply "wait until !d_in_lookup()".  Sure, we
> > might end up setting DCACHE_LOCK_WAITER on a dentry that has just dropped
> > DCACHE_PAR_LOOKUP - who cares?
> > 
> > While we are at it, do we need to drop it when we clear PAR_LOOKUP?  Because
> > if we do not, the whole "what do we return from __d_lookup_unhash()" thing
> > disappears - we simply pass the dentry to end_dir_add() and have it check
> > ->d_flags & DCACHE_LOCK_WAITER to decide whether to bother with wakeup.
> > 
> 
> Yes, your are right.
> 
> I've been thinking of this mostly in the context of locking the dentry for
> directory ops, for which lookup is just one special case.
> In that context the dentry can be locked and unlocked multiple time so
> we really want to clear DCACHE_LOCK_WAITERS on each unlock.
> 
> However in the current code it is only used for lookup which only
> happens once on a given dentry so we never need to clear
> DCACHE_LOCK_WAITERS.
> 
> On the basis that we shouldn't add complexity until we actually need it,
> I'll rename DCACHE_LOCK_WAITERS to DCACHE_LOOKUP_WAITERS and never clear
> it, as you suggest.

Alternative variant (and I'm pretty sure that it will generate good code)
would be this:

static inline void d_wait_lookup(struct dentry *dentry)
{
        if (likely(d_in_lookup(dentry)) {
		dentry->d_flags |= DCACHE_LOOKUP_WAITERS;
		wait_var_event_spinlock(&dentry->d_flags,
					!d_in_lookup(dentry),
					&dentry->d_lock);
	}
}

In __d_lookup_unhash(): just don't return anything and lose the parts
related to ->d_wait (including ->d_lru initialization - same as in your
patch, for the same reason).  Similar to your variant, except that
DCACHE_LOOKUP_WAITERS is *not* cleared.  Or checked, for that matter -
you only do that to find the return value.

In d_alloc_parallel(): lose the 'wq' argument, along with the store
to ->d_wait.

Add
// must be in the same ->d_lock scope as __d_lookup_unhash()
static inline void __d_wake_in_lookup_waiters(struct dentry *dentry)
{
	if (dentry->d_flags & DCACHE_LOOKUP_WAITERS) {
		wake_up_var_locked(&dentry->d_flags, &dentry->d_lock);
		dentry->d_flags &= ~DCACHE_LOOKUP_WAITERS;
	}
}

and have
void __d_lookup_unhash_wake(struct dentry *dentry)
{
        spin_lock(&dentry->d_lock);
	__d_lookup_unhash(dentry);
	__d_wake_in_lookup_waiters(dentry);
	spin_unlock(&dentry->d_lock);
}

static inline void end_dir_add(struct inode *dir, unsigned int n,
                               struct dentry *dentry)
{
	smp_store_release(&dir->i_dir_seq, n + 2);
	preempt_enable_nested();
	__d_wake_in_lookup_waiters(dentry);
}

with obvious adjustments in end_dir_add().  That's it.  Outside of fs/dcache.c,
same as in the patch you've posted, modulo renaming you've suggested for new flag.

That yields the same semantics for flags as your variant does (DCACHE_LOOKUP_WAITERS
may be present only along with DCACHE_PAR_LOOKUP), and fs/dcache.c part is more
straightforward that way, IMO.