Re: [PATCH v3 08/19] VFS/xfs/ntfs: drop parent lock across d_alloc_parallel() in d_add_ci()

NeilBrown
2026-04-27T08:48:38Z

On Mon, 27 Apr 2026, Amir Goldstein wrote:
> On Mon, Apr 27, 2026 at 6:07 AM NeilBrown <neilb@ownmail.net> wrote:
> >
> > From: NeilBrown <neil@brown.name>
> >
> > A proposed change will invert the lock ordering between
> > d_alloc_parallel() and inode_lock() on the parent.
> > When that happens it will not be safe to call d_alloc_parallel() while
> > holding the parent lock - even shared.
> >
> > We don't need to keep the parent lock held when d_add_ci() is run - the
> > VFS doesn't need it as dentry is exclusively held due to
> > DCACHE_PAR_LOOKUP and the filesystem has finished its work.
> >
> > So drop and reclaim the lock (shared or exclusive as determined by
> > LOOKUP_SHARED) to avoid future deadlock.
> >
> > Signed-off-by: NeilBrown <neil@brown.name>
> > ---
> > fs/dcache.c | 18 +++++++++++++++++-
> > fs/ntfs/namei.c | 4 +++-
> > fs/xfs/xfs_iops.c | 3 ++-
> > include/linux/dcache.h | 3 ++-
> > 4 files changed, 24 insertions(+), 4 deletions(-)
> >
> > diff --git a/fs/dcache.c b/fs/dcache.c
> > index 569a8ddf4c0d..a2ddfe811df3 100644
> > --- a/fs/dcache.c
> > +++ b/fs/dcache.c
> > @@ -2294,6 +2294,7 @@ EXPORT_SYMBOL(d_obtain_root);
> > * @dentry: the negative dentry that was passed to the parent's lookup func
> > * @inode: the inode case-insensitive lookup has found
> > * @name: the case-exact name to be associated with the returned dentry
> > + * @bool: %true if lookup was performed with LOOKUP_SHARED
>
> I do not understand the choice of API.
> Seems better to pass the lookup flags and avoid exposing this very internal
> logic in the call sites...

That is an excellent suggestion, thank. As you say, it cleans up much
messiness. I will definitely do that.

Thanks,
NeilBrown

>
> > *
> > * This is to avoid filling the dcache with case-insensitive names to the
> > * same inode, only the actual correct case is stored in the dcache for
> > @@ -2306,7 +2307,7 @@ EXPORT_SYMBOL(d_obtain_root);
> > * the exact case, and return the spliced entry.
> > */
> > struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
> > - struct qstr *name)
> > + struct qstr *name, bool shared)
> > {
> > struct dentry *found, *res;
> >
> > @@ -2319,6 +2320,17 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
> > iput(inode);
> > return found;
> > }
> > + /*
> > + * We are holding parent lock and so don't want to wait for a
> > + * d_in_lookup() dentry. We can safely drop the parent lock and
> > + * reclaim it as we have exclusive access to dentry as it is
> > + * d_in_lookup() (so ->d_parent is stable) and we are near the
> > + * end ->lookup() and will shortly drop the lock anyway.
> > + */
> > + if (shared)
> > + inode_unlock_shared(d_inode(dentry->d_parent));
> > + else
> > + inode_unlock(d_inode(dentry->d_parent));
> > if (d_in_lookup(dentry)) {
> > found = d_alloc_parallel(dentry->d_parent, name);
> > if (IS_ERR(found) || !d_in_lookup(found)) {
> > @@ -2332,6 +2344,10 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
> > return ERR_PTR(-ENOMEM);
> > }
> > }
> > + if (shared)
> > + inode_lock_shared(d_inode(dentry->d_parent));
> > + else
> > + inode_lock_nested(d_inode(dentry->d_parent), I_MUTEX_PARENT);
> > res = d_splice_alias(inode, found);
> > if (res) {
> > d_lookup_done(found);
> > diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
> > index 10894de519c3..30dddef43300 100644
> > --- a/fs/ntfs/namei.c
> > +++ b/fs/ntfs/namei.c
> > @@ -8,6 +8,7 @@
> >
> > #include <linux/exportfs.h>
> > #include <linux/iversion.h>
> > +#include <linux/namei.h> // for LOOKUP_SHARED
>
> ... this won't be needed
>
> >
> > #include "ntfs.h"
> > #include "time.h"
> > @@ -310,7 +311,8 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
> > }
> > nls_name.hash = full_name_hash(dent, nls_name.name, nls_name.len);
> >
> > - dent = d_add_ci(dent, dent_inode, &nls_name);
> > + dent = d_add_ci(dent, dent_inode, &nls_name,
> > + !!(flags & LOOKUP_SHARED));
> > kfree(nls_name.name);
> > return dent;
> >
> > diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> > index 325c2200c501..f03d832f1468 100644
> > --- a/fs/xfs/xfs_iops.c
> > +++ b/fs/xfs/xfs_iops.c
> > @@ -35,6 +35,7 @@
> > #include <linux/security.h>
> > #include <linux/iversion.h>
> > #include <linux/fiemap.h>
> > +#include <linux/namei.h> // for LOOKUP_SHARED
> >
> > /*
> > * Directories have different lock order w.r.t. mmap_lock compared to regular
> > @@ -369,7 +370,7 @@ xfs_vn_ci_lookup(
> > /* else case-insensitive match... */
> > dname.name = ci_name.name;
> > dname.len = ci_name.len;
> > - dentry = d_add_ci(dentry, VFS_I(ip), &dname);
> > + dentry = d_add_ci(dentry, VFS_I(ip), &dname, !!(flags & LOOKUP_SHARED));
>
>
> ... and this ugliness could be avoided.
>
> Thanks,
> Amir.
>