* [PATCH 0/5] NFS: Client Side Directory Delegations
@ 2025-11-04 15:06 Anna Schumaker
2025-11-04 15:06 ` [PATCH 1/5] NFS: Add support for sending GDD_GETATTR Anna Schumaker
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Anna Schumaker @ 2025-11-04 15:06 UTC (permalink / raw)
To: linux-nfs, trond.myklebust; +Cc: anna
From: Anna Schumaker <anna.schumaker@oracle.com>
This patchset adds support for recallable directory delegations in the
NFS client, letting us shortcut some directory revalidation steps since
we know the directory hasn't changed from underneath us.
I tried to limit requesting a dir delegation to when we think the user
is doing work within a directory, so I look for ACCESS, CREATE, UNLINK,
and (same directory) RENAME calls. I'm open to suggestions for other
times I should be requesting a dir delegation too.
Finally, I add a new "directory_delegations" module parameter for
controlling the usage of directory delegations since they are still a
new feature. I (optimistically) have them enabled by default, and
setting this option to false will disable requesting delegations and
cause the client to return any existing delegations on the next use.
I'm happy to hear any suggestions for improvements!
Anna
Anna Schumaker (5):
NFS: Add support for sending GDD_GETATTR
NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK
NFS: Request a directory delegation during RENAME
NFS: Shortcut lookup revalidations if we have a directory delegation
NFS: Add a module option to disable directory delegations
fs/nfs/delegation.c | 8 +++
fs/nfs/delegation.h | 13 +++++
fs/nfs/dir.c | 19 +++++++
fs/nfs/inode.c | 3 ++
fs/nfs/nfs3proc.c | 3 +-
fs/nfs/nfs4proc.c | 62 ++++++++++++++++++++--
fs/nfs/nfs4xdr.c | 106 ++++++++++++++++++++++++++++++++++++++
fs/nfs/proc.c | 3 +-
fs/nfs/unlink.c | 3 +-
include/linux/nfs_fs.h | 1 +
include/linux/nfs_fs_sb.h | 1 +
include/linux/nfs_xdr.h | 10 +++-
12 files changed, 223 insertions(+), 9 deletions(-)
--
2.51.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/5] NFS: Add support for sending GDD_GETATTR
2025-11-04 15:06 [PATCH 0/5] NFS: Client Side Directory Delegations Anna Schumaker
@ 2025-11-04 15:06 ` Anna Schumaker
2025-11-04 15:06 ` [PATCH 2/5] NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK Anna Schumaker
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Anna Schumaker @ 2025-11-04 15:06 UTC (permalink / raw)
To: linux-nfs, trond.myklebust; +Cc: anna
From: Anna Schumaker <anna.schumaker@oracle.com>
I add this to the existing GETATTR compound as an option extra step that
we can send if the "dir_deleg" flag is set to 'true'. Actually enabling
this value will happen in a later patch.
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
---
fs/nfs/nfs4xdr.c | 106 ++++++++++++++++++++++++++++++++++++++++
include/linux/nfs_xdr.h | 7 +++
2 files changed, 113 insertions(+)
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 1d0e6c10f921..b6fe30577fab 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -393,6 +393,20 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5)
#define encode_reclaim_complete_maxsz (op_encode_hdr_maxsz + 4)
#define decode_reclaim_complete_maxsz (op_decode_hdr_maxsz + 4)
+#define encode_get_dir_deleg_maxsz (op_encode_hdr_maxsz + \
+ 4 /* gdda_signal_deleg_avail */ + \
+ 8 /* gdda_notification_types */ + \
+ nfstime4_maxsz /* gdda_child_attr_delay */ + \
+ nfstime4_maxsz /* gdda_dir_attr_delay */ + \
+ nfs4_fattr_bitmap_maxsz /* gdda_child_attributes */ + \
+ nfs4_fattr_bitmap_maxsz /* gdda_dir_attributes */)
+#define decode_get_dir_deleg_maxsz (op_decode_hdr_maxsz + \
+ 4 /* gddrnf_status */ + \
+ encode_verifier_maxsz /* gddr_cookieverf */ + \
+ encode_stateid_maxsz /* gddr_stateid */ + \
+ 8 /* gddr_notification */ + \
+ nfs4_fattr_maxsz /* gddr_child_attributes */ + \
+ nfs4_fattr_maxsz /* gddr_dir_attributes */)
#define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + \
XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
1 /* layout type */ + \
@@ -444,6 +458,8 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#else /* CONFIG_NFS_V4_1 */
#define encode_sequence_maxsz 0
#define decode_sequence_maxsz 0
+#define encode_get_dir_deleg_maxsz 0
+#define decode_get_dir_deleg_maxsz 0
#define encode_layoutreturn_maxsz 0
#define decode_layoutreturn_maxsz 0
#define encode_layoutget_maxsz 0
@@ -631,11 +647,13 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
#define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
+ encode_get_dir_deleg_maxsz + \
encode_getattr_maxsz + \
encode_renew_maxsz)
#define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
+ decode_get_dir_deleg_maxsz + \
decode_getattr_maxsz + \
decode_renew_maxsz)
#define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \
@@ -2007,6 +2025,33 @@ static void encode_sequence(struct xdr_stream *xdr,
}
#ifdef CONFIG_NFS_V4_1
+static void
+encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
+{
+ struct timespec64 ts = { 0, 0 };
+ u32 notifications[1] = { 0 };
+ u32 attributes[1] = { 0 };
+ __be32 *p;
+
+ encode_op_hdr(xdr, OP_GET_DIR_DELEGATION, decode_get_dir_deleg_maxsz, hdr);
+
+ /* We don't handle CB_RECALLABLE_OBJ_AVAIL yet. */
+ xdr_stream_encode_bool(xdr, false);
+
+ xdr_encode_bitmap4(xdr, notifications, ARRAY_SIZE(notifications));
+
+ /* Request no delay on attribute updates */
+ p = reserve_space(xdr, 12 + 12);
+ p = xdr_encode_nfstime4(p, &ts);
+ xdr_encode_nfstime4(p, &ts);
+
+ /* Requested child attributes */
+ xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+
+ /* Requested dir attributes */
+ xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+}
+
static void
encode_getdeviceinfo(struct xdr_stream *xdr,
const struct nfs4_getdeviceinfo_args *args,
@@ -2142,6 +2187,11 @@ static void encode_free_stateid(struct xdr_stream *xdr,
encode_nfs4_stateid(xdr, &args->stateid);
}
#else
+static inline void
+encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
+{
+}
+
static inline void
encode_layoutreturn(struct xdr_stream *xdr,
const struct nfs4_layoutreturn_args *args,
@@ -2356,6 +2406,8 @@ static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
+ if (args->get_dir_deleg)
+ encode_get_dir_delegation(xdr, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
}
@@ -5994,6 +6046,49 @@ static int decode_layout_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
return decode_stateid(xdr, stateid);
}
+static int decode_get_dir_delegation(struct xdr_stream *xdr,
+ struct nfs4_getattr_res *res)
+{
+ struct nfs4_gdd_res *gdd_res = res->gdd_res;
+ nfs4_verifier cookieverf;
+ u32 bitmap[1];
+ int status;
+
+ status = decode_op_hdr(xdr, OP_GET_DIR_DELEGATION);
+ if (status)
+ return status;
+
+ if (xdr_stream_decode_u32(xdr, &gdd_res->status))
+ return -EIO;
+
+ if (gdd_res->status == GDD4_UNAVAIL)
+ return xdr_inline_decode(xdr, 4) ? 0 : -EIO;
+
+ status = decode_verifier(xdr, &cookieverf);
+ if (status)
+ return status;
+
+ status = decode_delegation_stateid(xdr, &gdd_res->deleg);
+ if (status)
+ return status;
+
+ /* Decode supported notification types. */
+ status = decode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
+ if (status < 0)
+ return status;
+
+ /* Decode supported child attributes. */
+ status = decode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
+ if (status < 0)
+ return status;
+
+ /* Decode supported attributes. */
+ status = decode_bitmap4(xdr, bitmap, ARRAY_SIZE(bitmap));
+ if (status < 0)
+ return status;
+ return 0;
+}
+
static int decode_getdeviceinfo(struct xdr_stream *xdr,
struct nfs4_getdeviceinfo_res *res)
{
@@ -6208,6 +6303,12 @@ static int decode_free_stateid(struct xdr_stream *xdr,
return res->status;
}
#else
+static int decode_get_dir_delegation(struct xdr_stream *xdr,
+ struct nfs4_getattr_res *res)
+{
+ return 0;
+}
+
static inline
int decode_layoutreturn(struct xdr_stream *xdr,
struct nfs4_layoutreturn_res *res)
@@ -6525,6 +6626,11 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
status = decode_putfh(xdr);
if (status)
goto out;
+ if (res->gdd_res) {
+ status = decode_get_dir_delegation(xdr, res);
+ if (status)
+ goto out;
+ }
status = decode_getfattr(xdr, res->fattr, res->server);
out:
return status;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 31463286402f..8bf6cba96c46 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1092,12 +1092,19 @@ struct nfs4_getattr_arg {
struct nfs4_sequence_args seq_args;
const struct nfs_fh * fh;
const u32 * bitmask;
+ bool get_dir_deleg;
+};
+
+struct nfs4_gdd_res {
+ u32 status;
+ nfs4_stateid deleg;
};
struct nfs4_getattr_res {
struct nfs4_sequence_res seq_res;
const struct nfs_server * server;
struct nfs_fattr * fattr;
+ struct nfs4_gdd_res * gdd_res;
};
struct nfs4_link_arg {
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/5] NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK
2025-11-04 15:06 [PATCH 0/5] NFS: Client Side Directory Delegations Anna Schumaker
2025-11-04 15:06 ` [PATCH 1/5] NFS: Add support for sending GDD_GETATTR Anna Schumaker
@ 2025-11-04 15:06 ` Anna Schumaker
2025-11-04 15:06 ` [PATCH 3/5] NFS: Request a directory delegation during RENAME Anna Schumaker
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Anna Schumaker @ 2025-11-04 15:06 UTC (permalink / raw)
To: linux-nfs, trond.myklebust; +Cc: anna
From: Anna Schumaker <anna.schumaker@oracle.com>
This patch adds a new flag: NFS_INO_REQ_DIR_DELEG to signal that a
directory wants to request a directory delegation the next time it does
a GETATTR. I have the client request a directory delegation when doing
an access, create, or unlink call since these calls indicate that a user
is working with a directory.
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
---
fs/nfs/delegation.c | 1 +
fs/nfs/delegation.h | 6 +++++
fs/nfs/nfs4proc.c | 55 ++++++++++++++++++++++++++++++++++++---
include/linux/nfs_fs.h | 1 +
include/linux/nfs_fs_sb.h | 1 +
5 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 9d3a5f29f17f..b4c192f00e94 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -379,6 +379,7 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
delegation->inode = NULL;
rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock);
+ clear_bit(NFS_INO_REQ_DIR_DELEG, &nfsi->flags);
return delegation;
}
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 08ec2e9c68a4..def50e8a83bf 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -124,6 +124,12 @@ static inline int nfs_have_delegated_mtime(struct inode *inode)
NFS_DELEGATION_FLAG_TIME);
}
+static inline void nfs_request_directory_delegation(struct inode *inode)
+{
+ if (S_ISDIR(inode->i_mode))
+ set_bit(NFS_INO_REQ_DIR_DELEG, &NFS_I(inode)->flags);
+}
+
int nfs4_delegation_hash_alloc(struct nfs_server *server);
#endif
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 411776718494..bd718a270e72 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4460,6 +4460,28 @@ static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
return status;
}
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+static bool should_request_dir_deleg(struct inode *inode)
+{
+ if (!inode)
+ return false;
+ if (!S_ISDIR(inode->i_mode))
+ return false;
+ if (!nfs_server_capable(inode, NFS_CAP_DIR_DELEG))
+ return false;
+ if (!test_and_clear_bit(NFS_INO_REQ_DIR_DELEG, &(NFS_I(inode)->flags)))
+ return false;
+ if (nfs4_have_delegation(inode, FMODE_READ, 0))
+ return false;
+ return true;
+}
+#else
+static bool should_request_dir_deleg(struct inode *inode)
+{
+ return false;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr, struct inode *inode)
{
@@ -4477,7 +4499,9 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_argp = &args,
.rpc_resp = &res,
};
+ struct nfs4_gdd_res gdd_res;
unsigned short task_flags = 0;
+ int status;
if (nfs4_has_session(server->nfs_client))
task_flags = RPC_TASK_MOVEABLE;
@@ -4486,11 +4510,26 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
if (inode && (server->flags & NFS_MOUNT_SOFTREVAL))
task_flags |= RPC_TASK_TIMEOUT;
+ args.get_dir_deleg = should_request_dir_deleg(inode);
+ if (args.get_dir_deleg)
+ res.gdd_res = &gdd_res;
+
nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, fattr->label), inode, 0);
nfs_fattr_init(fattr);
nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
- return nfs4_do_call_sync(server->client, server, &msg,
- &args.seq_args, &res.seq_res, task_flags);
+
+ status = nfs4_do_call_sync(server->client, server, &msg,
+ &args.seq_args, &res.seq_res, task_flags);
+ if (args.get_dir_deleg) {
+ if (status == -EOPNOTSUPP) {
+ server->caps &= ~NFS_CAP_DIR_DELEG;
+ } else if (status == 0 && gdd_res.status == GDD4_OK) {
+ status = nfs_inode_set_delegation(inode, current_cred(),
+ FMODE_READ, &gdd_res.deleg,
+ 0, NFS4_OPEN_DELEGATE_READ);
+ }
+ }
+ return status;
}
int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -4503,8 +4542,10 @@ int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
do {
err = _nfs4_proc_getattr(server, fhandle, fattr, inode);
trace_nfs4_getattr(server, fhandle, fattr, err);
- err = nfs4_handle_exception(server, err,
- &exception);
+ if (err == -EOPNOTSUPP)
+ exception.retry = true;
+ else
+ err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
@@ -4765,6 +4806,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
int status = 0;
if (!nfs4_have_delegation(inode, FMODE_READ, 0)) {
+ nfs_request_directory_delegation(inode);
res.fattr = nfs_alloc_fattr();
if (res.fattr == NULL)
return -ENOMEM;
@@ -4872,6 +4914,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
+ nfs_request_directory_delegation(dir);
+
if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
sattr->ia_mode &= ~current_umask();
state = nfs4_do_open(dir, ctx, flags, sattr, ilabel, NULL);
@@ -4968,6 +5012,7 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg,
nfs4_init_sequence(&args->seq_args, &res->seq_res, 1, 0);
nfs_fattr_init(res->dir_attr);
+ nfs_request_directory_delegation(d_inode(dentry->d_parent));
if (inode) {
nfs4_inode_return_delegation(inode);
@@ -10819,6 +10864,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
.minor_version = 1,
.init_caps = NFS_CAP_READDIRPLUS
| NFS_CAP_ATOMIC_OPEN
+ | NFS_CAP_DIR_DELEG
| NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41
| NFS_CAP_ATOMIC_OPEN_V1
@@ -10845,6 +10891,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
.minor_version = 2,
.init_caps = NFS_CAP_READDIRPLUS
| NFS_CAP_ATOMIC_OPEN
+ | NFS_CAP_DIR_DELEG
| NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41
| NFS_CAP_ATOMIC_OPEN_V1
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index c585939b6cd6..a6624edb7226 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -344,6 +344,7 @@ struct nfs4_copy_state {
#define NFS_INO_LAYOUTCOMMITTING (10) /* layoutcommit inflight */
#define NFS_INO_LAYOUTSTATS (11) /* layoutstats inflight */
#define NFS_INO_ODIRECT (12) /* I/O setting is O_DIRECT */
+#define NFS_INO_REQ_DIR_DELEG (13) /* Request a directory delegation */
static inline struct nfs_inode *NFS_I(const struct inode *inode)
{
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index d30c0245031c..4ba04de6b1ca 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -305,6 +305,7 @@ struct nfs_server {
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
#define NFS_CAP_ZERO_RANGE (1U << 10)
+#define NFS_CAP_DIR_DELEG (1U << 11)
#define NFS_CAP_OPEN_XOR (1U << 12)
#define NFS_CAP_DELEGTIME (1U << 13)
#define NFS_CAP_POSIX_LOCK (1U << 14)
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/5] NFS: Request a directory delegation during RENAME
2025-11-04 15:06 [PATCH 0/5] NFS: Client Side Directory Delegations Anna Schumaker
2025-11-04 15:06 ` [PATCH 1/5] NFS: Add support for sending GDD_GETATTR Anna Schumaker
2025-11-04 15:06 ` [PATCH 2/5] NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK Anna Schumaker
@ 2025-11-04 15:06 ` Anna Schumaker
2025-11-04 15:06 ` [PATCH 4/5] NFS: Shortcut lookup revalidations if we have a directory delegation Anna Schumaker
2025-11-04 15:06 ` [PATCH 5/5] NFS: Add a module option to disable directory delegations Anna Schumaker
4 siblings, 0 replies; 6+ messages in thread
From: Anna Schumaker @ 2025-11-04 15:06 UTC (permalink / raw)
To: linux-nfs, trond.myklebust; +Cc: anna
From: Anna Schumaker <anna.schumaker@oracle.com>
If we notice that we're renaming a file within a directory then we take
that as a sign that the user is working with the current directory and
may want a delegation to avoid extra revalidations when possible.
The nfs_request_directory_delegation() function exists within the NFS v4
module, so I add an extra flag to rename_setup() to indicate if a dentry
is being renamed within the same parent directory.
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
---
fs/nfs/nfs3proc.c | 3 ++-
fs/nfs/nfs4proc.c | 5 ++++-
fs/nfs/proc.c | 3 ++-
fs/nfs/unlink.c | 3 ++-
include/linux/nfs_xdr.h | 3 ++-
5 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index a4cb67573aa7..1181f9cc6dbd 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -483,7 +483,8 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void
nfs3_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct inode *same_parent)
{
msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME];
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index bd718a270e72..fa176db362c7 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -5047,7 +5047,8 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void nfs4_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct inode *same_parent)
{
struct nfs_renameargs *arg = msg->rpc_argp;
struct nfs_renameres *res = msg->rpc_resp;
@@ -5058,6 +5059,8 @@ static void nfs4_proc_rename_setup(struct rpc_message *msg,
nfs4_inode_make_writeable(old_inode);
if (new_inode)
nfs4_inode_return_delegation(new_inode);
+ if (same_parent)
+ nfs_request_directory_delegation(same_parent);
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
res->server = NFS_SB(old_dentry->d_sb);
nfs4_init_sequence(&arg->seq_args, &res->seq_res, 1, 0);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 63e71310b9f6..39df80e4ae6f 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -353,7 +353,8 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static void
nfs_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct inode *same_parent)
{
msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME];
}
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index b55467911648..4db818c0f9dd 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -390,7 +390,8 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
nfs_sb_active(old_dir->i_sb);
- NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dentry, new_dentry);
+ NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dentry, new_dentry,
+ old_dir == new_dir ? old_dir : NULL);
return rpc_run_task(&task_setup_data);
}
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 8bf6cba96c46..79fe2dfb470f 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1808,7 +1808,8 @@ struct nfs_rpc_ops {
int (*unlink_done) (struct rpc_task *, struct inode *);
void (*rename_setup) (struct rpc_message *msg,
struct dentry *old_dentry,
- struct dentry *new_dentry);
+ struct dentry *new_dentry,
+ struct inode *same_parent);
void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *);
int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir);
int (*link) (struct inode *, struct inode *, const struct qstr *);
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/5] NFS: Shortcut lookup revalidations if we have a directory delegation
2025-11-04 15:06 [PATCH 0/5] NFS: Client Side Directory Delegations Anna Schumaker
` (2 preceding siblings ...)
2025-11-04 15:06 ` [PATCH 3/5] NFS: Request a directory delegation during RENAME Anna Schumaker
@ 2025-11-04 15:06 ` Anna Schumaker
2025-11-04 15:06 ` [PATCH 5/5] NFS: Add a module option to disable directory delegations Anna Schumaker
4 siblings, 0 replies; 6+ messages in thread
From: Anna Schumaker @ 2025-11-04 15:06 UTC (permalink / raw)
To: linux-nfs, trond.myklebust; +Cc: anna
From: Anna Schumaker <anna.schumaker@oracle.com>
Holding a directory delegation means we know that nobody else has
modified the directory on the server, so we can take a few revalidation
shortcuts.
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
---
fs/nfs/delegation.h | 5 +++++
fs/nfs/dir.c | 19 +++++++++++++++++++
fs/nfs/inode.c | 3 +++
3 files changed, 27 insertions(+)
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index def50e8a83bf..8968f62bf438 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -130,6 +130,11 @@ static inline void nfs_request_directory_delegation(struct inode *inode)
set_bit(NFS_INO_REQ_DIR_DELEG, &NFS_I(inode)->flags);
}
+static inline bool nfs_have_directory_delegation(struct inode *inode)
+{
+ return S_ISDIR(inode->i_mode) && nfs_have_delegated_attributes(inode);
+}
+
int nfs4_delegation_hash_alloc(struct nfs_server *server);
#endif
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ea9f6ca8f30f..2cc784ae0581 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1514,6 +1514,15 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
return 0;
if (!nfs_dentry_verify_change(dir, dentry))
return 0;
+
+ /*
+ * If we have a directory delegation then we don't need to revalidate
+ * the directory. The delegation will either get recalled or we will
+ * receive a notification when it changes.
+ */
+ if (nfs_have_directory_delegation(dir))
+ return 0;
+
/* Revalidate nfsi->cache_change_attribute before we declare a match */
if (nfs_mapping_need_revalidate_inode(dir)) {
if (rcu_walk)
@@ -2202,6 +2211,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
}
EXPORT_SYMBOL_GPL(nfs_atomic_open);
+static int
+nfs_lookup_revalidate_delegated_parent(struct inode *dir, struct dentry *dentry,
+ struct inode *inode)
+{
+ return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
+}
+
static int
nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *dentry, unsigned int flags)
@@ -2229,6 +2245,9 @@ nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
if (nfs_verifier_is_delegated(dentry))
return nfs_lookup_revalidate_delegated(dir, dentry, inode);
+ if (nfs_have_directory_delegation(dir))
+ return nfs_lookup_revalidate_delegated_parent(dir, dentry, inode);
+
/* NFS only supports OPEN on regular files */
if (!S_ISREG(inode->i_mode))
goto full_reval;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 18b57c7c2f97..6c92211835e7 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1383,6 +1383,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
status = pnfs_sync_inode(inode, false);
if (status)
goto out;
+ } else if (nfs_have_directory_delegation(inode)) {
+ status = 0;
+ goto out;
}
status = -ENOMEM;
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 5/5] NFS: Add a module option to disable directory delegations
2025-11-04 15:06 [PATCH 0/5] NFS: Client Side Directory Delegations Anna Schumaker
` (3 preceding siblings ...)
2025-11-04 15:06 ` [PATCH 4/5] NFS: Shortcut lookup revalidations if we have a directory delegation Anna Schumaker
@ 2025-11-04 15:06 ` Anna Schumaker
4 siblings, 0 replies; 6+ messages in thread
From: Anna Schumaker @ 2025-11-04 15:06 UTC (permalink / raw)
To: linux-nfs, trond.myklebust; +Cc: anna
From: Anna Schumaker <anna.schumaker@oracle.com>
When this option is disabled then the client will not request directory
delegations or check if we have one during the revalidation paths.
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
---
fs/nfs/delegation.c | 7 +++++++
fs/nfs/delegation.h | 2 ++
fs/nfs/nfs4proc.c | 2 ++
3 files changed, 11 insertions(+)
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index b4c192f00e94..2248e3ad089a 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -30,6 +30,11 @@
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;
module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
+bool directory_delegations = true;
+module_param(directory_delegations, bool, 0644);
+MODULE_PARM_DESC(directory_delegations,
+ "Enable the use of directory delegations, defaults to on.");
+
static struct hlist_head *nfs_delegation_hash(struct nfs_server *server,
const struct nfs_fh *fhandle)
{
@@ -143,6 +148,8 @@ static int nfs4_do_check_delegation(struct inode *inode, fmode_t type,
*/
int nfs4_have_delegation(struct inode *inode, fmode_t type, int flags)
{
+ if (S_ISDIR(inode->i_mode) && !directory_delegations)
+ nfs_inode_evict_delegation(inode);
return nfs4_do_check_delegation(inode, type, flags, true);
}
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 8968f62bf438..46d866adb5c2 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -124,6 +124,8 @@ static inline int nfs_have_delegated_mtime(struct inode *inode)
NFS_DELEGATION_FLAG_TIME);
}
+extern bool directory_delegations;
+
static inline void nfs_request_directory_delegation(struct inode *inode)
{
if (S_ISDIR(inode->i_mode))
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index fa176db362c7..33b64d000c40 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4463,6 +4463,8 @@ static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
#if IS_ENABLED(CONFIG_NFS_V4_1)
static bool should_request_dir_deleg(struct inode *inode)
{
+ if (!directory_delegations)
+ return false;
if (!inode)
return false;
if (!S_ISDIR(inode->i_mode))
--
2.51.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-11-04 15:06 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-04 15:06 [PATCH 0/5] NFS: Client Side Directory Delegations Anna Schumaker
2025-11-04 15:06 ` [PATCH 1/5] NFS: Add support for sending GDD_GETATTR Anna Schumaker
2025-11-04 15:06 ` [PATCH 2/5] NFS: Request a directory delegation on ACCESS, CREATE, and UNLINK Anna Schumaker
2025-11-04 15:06 ` [PATCH 3/5] NFS: Request a directory delegation during RENAME Anna Schumaker
2025-11-04 15:06 ` [PATCH 4/5] NFS: Shortcut lookup revalidations if we have a directory delegation Anna Schumaker
2025-11-04 15:06 ` [PATCH 5/5] NFS: Add a module option to disable directory delegations Anna Schumaker
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).