Skip to content

Commit c7ce21b

Browse files
Trond Myklebustgregkh
authored andcommitted
NFS: Avoid flushing data while holding directory locks in nfs_rename()
[ Upstream commit dcd21b6 ] 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 <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 87129b9 commit c7ce21b

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

fs/nfs/client.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,8 @@ struct nfs_server *nfs_create_server(struct fs_context *fc)
10961096
if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
10971097
server->namelen = NFS2_MAXNAMLEN;
10981098
}
1099+
/* Linux 'subtree_check' borkenness mandates this setting */
1100+
server->fh_expire_type = NFS_FH_VOL_RENAME;
10991101

11001102
if (!(fattr->valid & NFS_ATTR_FATTR)) {
11011103
error = ctx->nfs_mod->rpc_ops->getattr(server, ctx->mntfh,

fs/nfs/dir.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,18 @@ nfs_unblock_rename(struct rpc_task *task, struct nfs_renamedata *data)
26902690
unblock_revalidate(new_dentry);
26912691
}
26922692

2693+
static bool nfs_rename_is_unsafe_cross_dir(struct dentry *old_dentry,
2694+
struct dentry *new_dentry)
2695+
{
2696+
struct nfs_server *server = NFS_SB(old_dentry->d_sb);
2697+
2698+
if (old_dentry->d_parent != new_dentry->d_parent)
2699+
return false;
2700+
if (server->fh_expire_type & NFS_FH_RENAME_UNSAFE)
2701+
return !(server->fh_expire_type & NFS_FH_NOEXPIRE_WITH_OPEN);
2702+
return true;
2703+
}
2704+
26932705
/*
26942706
* RENAME
26952707
* FIXME: Some nfsds, like the Linux user space nfsd, may generate a
@@ -2777,7 +2789,8 @@ int nfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
27772789

27782790
}
27792791

2780-
if (S_ISREG(old_inode->i_mode))
2792+
if (S_ISREG(old_inode->i_mode) &&
2793+
nfs_rename_is_unsafe_cross_dir(old_dentry, new_dentry))
27812794
nfs_sync_inode(old_inode);
27822795
task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry,
27832796
must_unblock ? nfs_unblock_rename : NULL);

include/linux/nfs_fs_sb.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ struct nfs_server {
211211
char *fscache_uniq; /* Uniquifier (or NULL) */
212212
#endif
213213

214+
/* The following #defines numerically match the NFSv4 equivalents */
215+
#define NFS_FH_NOEXPIRE_WITH_OPEN (0x1)
216+
#define NFS_FH_VOLATILE_ANY (0x2)
217+
#define NFS_FH_VOL_MIGRATION (0x4)
218+
#define NFS_FH_VOL_RENAME (0x8)
219+
#define NFS_FH_RENAME_UNSAFE (NFS_FH_VOLATILE_ANY | NFS_FH_VOL_RENAME)
220+
u32 fh_expire_type; /* V4 bitmask representing file
221+
handle volatility type for
222+
this filesystem */
214223
u32 pnfs_blksize; /* layout_blksize attr */
215224
#if IS_ENABLED(CONFIG_NFS_V4)
216225
u32 attr_bitmask[3];/* V4 bitmask representing the set
@@ -234,9 +243,6 @@ struct nfs_server {
234243
u32 acl_bitmask; /* V4 bitmask representing the ACEs
235244
that are supported on this
236245
filesystem */
237-
u32 fh_expire_type; /* V4 bitmask representing file
238-
handle volatility type for
239-
this filesystem */
240246
struct pnfs_layoutdriver_type *pnfs_curr_ld; /* Active layout driver */
241247
struct rpc_wait_queue roc_rpcwaitq;
242248
void *pnfs_ld_data; /* per mount point data */

0 commit comments

Comments
 (0)