Proposed locking changes will require a dentry to remain hashed during all directory operations which are currently protected by i_rwsem, or for there to be a controlled transition from one hashed dentry to another which maintains the lock - which will then be on the dentry.
The current practice of dropping (unhashing) a dentry before calling d_splice_alias() and d_add() defeats this need. This patch changes d_splice_alias() and d_add() to accept a hashed dentry and to only drop it when necessary immediately before an alternate dentry is hashed. These functions will, in a subsequent patch, transfer the dentry locking across so that the name remains locked in the directory. Signed-off-by: NeilBrown <n...@brown.name> --- Documentation/filesystems/vfs.rst | 4 ++-- fs/ceph/file.c | 2 -- fs/ceph/inode.c | 3 --- fs/dcache.c | 8 +++++--- fs/fuse/dir.c | 1 - fs/hostfs/hostfs_kern.c | 1 - fs/nfs/dir.c | 5 +---- fs/nfs/nfs4proc.c | 1 - fs/smb/client/dir.c | 1 - 9 files changed, 8 insertions(+), 18 deletions(-) diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst index 486a91633474..642dd6afb139 100644 --- a/Documentation/filesystems/vfs.rst +++ b/Documentation/filesystems/vfs.rst @@ -580,8 +580,8 @@ otherwise noted. dentry before the first mkdir returns. If there is any chance this could happen, then the new inode - should be d_drop()ed and attached with d_splice_alias(). The - returned dentry (if any) should be returned by ->mkdir(). + should be attached with d_splice_alias(). The returned dentry + (if any) should be returned by ->mkdir(). ``rmdir`` called by the rmdir(2) system call. Only required if you want diff --git a/fs/ceph/file.c b/fs/ceph/file.c index c02f100f8552..27eb1ac06177 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -755,8 +755,6 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode, unlock_new_inode(inode); } if (d_in_lookup(dentry) || d_really_is_negative(dentry)) { - if (!d_unhashed(dentry)) - d_drop(dentry); dn = d_splice_alias(inode, dentry); WARN_ON_ONCE(dn && dn != dentry); } diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index fc543075b827..7acd6ac0d50f 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1480,9 +1480,6 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) } } - /* dn must be unhashed */ - if (!d_unhashed(dn)) - d_drop(dn); realdn = d_splice_alias(in, dn); if (IS_ERR(realdn)) { pr_err_client(cl, "error %ld %p inode %p ino %llx.%llx\n", diff --git a/fs/dcache.c b/fs/dcache.c index 60046ae23d51..0db256098adb 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2709,7 +2709,11 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode, raw_write_seqcount_end(&dentry->d_seq); fsnotify_update_flags(dentry); } - __d_rehash(dentry); + if (d_unhashed(dentry)) + __d_rehash(dentry); + else if (inode && (dentry->d_flags & + (DCACHE_LRU_LIST|DCACHE_SHRINK_LIST)) == DCACHE_LRU_LIST) + this_cpu_dec(nr_dentry_negative); if (dir) end_dir_add(dir, n, d_wait); spin_unlock(&dentry->d_lock); @@ -2990,8 +2994,6 @@ struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry, if (IS_ERR(inode)) return ERR_CAST(inode); - BUG_ON(!d_unhashed(dentry)); - if (!inode) goto out; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 2d817d7cab26..60e7763da8c8 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -834,7 +834,6 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun } kfree(forget); - d_drop(entry); d = d_splice_alias(inode, entry); if (IS_ERR(d)) return d; diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 01e516175bcd..8e51fc623301 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -700,7 +700,6 @@ static struct dentry *hostfs_mkdir(struct mnt_idmap *idmap, struct inode *ino, dentry = ERR_PTR(err); } else { inode = hostfs_iget(dentry->d_sb, file); - d_drop(dentry); dentry = d_splice_alias(inode, dentry); } __putname(file); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index d81217923936..250a826d5480 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2136,7 +2136,6 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, err = PTR_ERR(inode); trace_nfs_atomic_open_exit(dir, ctx, open_flags, err); put_nfs_open_context(ctx); - d_drop(dentry); switch (err) { case -ENOENT: d_splice_alias(NULL, dentry); @@ -2157,6 +2156,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, default: break; } + d_drop(dentry); goto out; } file->f_mode |= FMODE_CAN_ODIRECT; @@ -2304,8 +2304,6 @@ nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle, struct dentry *d; int error; - d_drop(dentry); - if (fhandle->size == 0) { error = NFS_PROTO(dir)->lookup(dir, dentry, &dentry->d_name, fhandle, fattr); @@ -2652,7 +2650,6 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) old_dentry, dentry); trace_nfs_link_enter(inode, dir, dentry); - d_drop(dentry); if (S_ISREG(inode->i_mode)) nfs_sync_inode(inode); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7d2b67e06cc3..d8739c286a99 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3175,7 +3175,6 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, dentry = opendata->dentry; if (d_really_is_negative(dentry)) { struct dentry *alias; - d_drop(dentry); alias = d_splice_alias(igrab(state->inode), dentry); /* d_splice_alias() can't fail here - it's a non-directory */ if (alias) { diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c index 5223edf6d11a..8cbc284c5005 100644 --- a/fs/smb/client/dir.c +++ b/fs/smb/client/dir.c @@ -439,7 +439,6 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned goto out_err; } - d_drop(direntry); d_add(direntry, newinode); out: -- 2.50.0.107.gf914562f5916.dirty