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


Reply via email to