]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFS: Avoid flushing data while holding directory locks in nfs_rename()
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sun, 27 Apr 2025 22:21:06 +0000 (18:21 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Jun 2025 12:45:10 +0000 (14:45 +0200)
[ Upstream commit dcd21b609d4abc7303f8683bce4f35d78d7d6830 ]

The Linux client assumes that all filehandles are non-volatile for
renames within the same directory (otherwise sillyrename cannot work).
However, the existence of the Linux 'subtree_check' export option has
meant that nfs_rename() has always assumed it needs to flush writes
before attempting to rename.

Since NFSv4 does allow the client to query whether or not the server
exhibits this behaviour, and since knfsd does actually set the
appropriate flag when 'subtree_check' is enabled on an export, it
should be OK to optimise away the write flushing behaviour in the cases
where it is clearly not needed.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/nfs/client.c
fs/nfs/dir.c
include/linux/nfs_fs_sb.h

index 3b0918ade53cd331d76baaa86fd2adec5d945b78..a10d39150abc81b016080abcb748d9872e2b9e9a 100644 (file)
@@ -1100,6 +1100,8 @@ struct nfs_server *nfs_create_server(struct fs_context *fc)
                if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
                        server->namelen = NFS2_MAXNAMLEN;
        }
+       /* Linux 'subtree_check' borkenness mandates this setting */
+       server->fh_expire_type = NFS_FH_VOL_RENAME;
 
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
                error = ctx->nfs_mod->rpc_ops->getattr(server, ctx->mntfh,
index 2b04038b0e40525be67f1ec512fb0db547e5f6ed..34f3471ce813bc833b24a09678af49857e010b20 100644 (file)
@@ -2678,6 +2678,18 @@ nfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data)
        unblock_revalidate(new_dentry);
 }
 
+static bool nfs_rename_is_unsafe_cross_dir(struct dentry *old_dentry,
+                                          struct dentry *new_dentry)
+{
+       struct nfs_server *server = NFS_SB(old_dentry->d_sb);
+
+       if (old_dentry->d_parent != new_dentry->d_parent)
+               return false;
+       if (server->fh_expire_type & NFS_FH_RENAME_UNSAFE)
+               return !(server->fh_expire_type & NFS_FH_NOEXPIRE_WITH_OPEN);
+       return true;
+}
+
 /*
  * RENAME
  * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
@@ -2765,7 +2777,8 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
 
        }
 
-       if (S_ISREG(old_inode->i_mode))
+       if (S_ISREG(old_inode->i_mode) &&
+           nfs_rename_is_unsafe_cross_dir(old_dentry, new_dentry))
                nfs_sync_inode(old_inode);
        task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry,
                                must_unblock ? nfs_unblock_rename : NULL);
index 108862d81b579830a2c2b78f136928d27ad124f0..8baaad2dfbe4057dea05f8213e2c2f9be63e45b5 100644 (file)
@@ -210,6 +210,15 @@ struct nfs_server {
        char                    *fscache_uniq;  /* Uniquifier (or NULL) */
 #endif
 
+       /* The following #defines numerically match the NFSv4 equivalents */
+#define NFS_FH_NOEXPIRE_WITH_OPEN (0x1)
+#define NFS_FH_VOLATILE_ANY (0x2)
+#define NFS_FH_VOL_MIGRATION (0x4)
+#define NFS_FH_VOL_RENAME (0x8)
+#define NFS_FH_RENAME_UNSAFE (NFS_FH_VOLATILE_ANY | NFS_FH_VOL_RENAME)
+       u32                     fh_expire_type; /* V4 bitmask representing file
+                                                  handle volatility type for
+                                                  this filesystem */
        u32                     pnfs_blksize;   /* layout_blksize attr */
 #if IS_ENABLED(CONFIG_NFS_V4)
        u32                     attr_bitmask[3];/* V4 bitmask representing the set
@@ -233,9 +242,6 @@ struct nfs_server {
        u32                     acl_bitmask;    /* V4 bitmask representing the ACEs
                                                   that are supported on this
                                                   filesystem */
-       u32                     fh_expire_type; /* V4 bitmask representing file
-                                                  handle volatility type for
-                                                  this filesystem */
        struct pnfs_layoutdriver_type  *pnfs_curr_ld; /* Active layout driver */
        struct rpc_wait_queue   roc_rpcwaitq;
        void                    *pnfs_ld_data;  /* per mount point data */