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. >