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

NeilBrown <[email protected]>
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 <[email protected]>
On Mon, 27 Apr 2026, Amir Goldstein wrote:
> On Mon, Apr 27, 2026 at 6:07 AM NeilBrown <[email protected]> wrote:
> >
> > From: NeilBrown <[email protected]>
> >
> > 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 <[email protected]>
> > ---
> >  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.
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.