* [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