Re: [PATCH v3 08/19] VFS/xfs/ntfs: drop parent lock across d_alloc_parallel() in d_add_ci()
Amir Goldstein <[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 | <CAOQ4uxjj6iJNnwUqV+cRtqo4qRD4v-f-ct92=ofCZWPDATXM7g@mail.gmail.com> |
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... > * > * 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.