Linux NFS development
 help / color / mirror / Atom feed
* [PATCH 0/4] NFS: Add directory delegation support for CB_NOTIFY
@ 2026-08-04 20:59 Anna Schumaker
  2026-08-04 20:59 ` [PATCH 1/4] NFS: Add an outline for the CB_NOTIFY operation Anna Schumaker
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Anna Schumaker @ 2026-08-04 20:59 UTC (permalink / raw)
  To: linux-nfs, trond.myklebust; +Cc: anna

From: Anna Schumaker <anna.schumaker@hammerspace.com>

These patches add support for the CB_NOTIFY callback operation from NFS
servers. Editing directory order or placement in our caches isn't easy
or practical the way things are structured now. Instead, I use CB_NOTIFY
as an indication that the client needs to refresh its view of the
directory.

Thoughts?
Anna


Anna Schumaker (4):
  NFS: Add an outline for the CB_NOTIFY operation
  NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY
  NFS: Add support for CB_NOTIFY4_ADD_ENTRY
  NFS: Add support for CB_NOTIFY4_RENAME_ENTRY

 fs/nfs/callback.h      |  51 ++++++++++++
 fs/nfs/callback_proc.c | 143 ++++++++++++++++++++++++++++++++++
 fs/nfs/callback_xdr.c  | 172 ++++++++++++++++++++++++++++++++++++++++-
 fs/nfs/nfs4_fs.h       |   2 +
 fs/nfs/nfs4xdr.c       |  31 +++++++-
 include/linux/nfs4.h   |   6 ++
 6 files changed, 400 insertions(+), 5 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/4] NFS: Add an outline for the CB_NOTIFY operation
  2026-08-04 20:59 [PATCH 0/4] NFS: Add directory delegation support for CB_NOTIFY Anna Schumaker
@ 2026-08-04 20:59 ` Anna Schumaker
  2026-08-04 20:59 ` [PATCH 2/4] NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY Anna Schumaker
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Anna Schumaker @ 2026-08-04 20:59 UTC (permalink / raw)
  To: linux-nfs, trond.myklebust; +Cc: anna

From: Anna Schumaker <anna.schumaker@oracle.com>

Support for individual notification types will be added over the next
few patches.

Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
---
 fs/nfs/callback.h      | 14 +++++++++++
 fs/nfs/callback_proc.c |  9 +++++++
 fs/nfs/callback_xdr.c  | 54 +++++++++++++++++++++++++++++++++++++++++-
 fs/nfs/nfs4xdr.c       |  2 +-
 include/linux/nfs4.h   |  3 +++
 5 files changed, 80 insertions(+), 2 deletions(-)

diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index 2a721c422d48..f7cc5b6931bf 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -143,6 +143,20 @@ struct cb_layoutrecallargs {
 extern __be32 nfs4_callback_layoutrecall(void *argp, void *resp,
 		struct cb_process_state *cps);
 
+struct cb_notify_changes {
+	u32 notify_mask;
+};
+
+struct cb_notifyargs {
+	nfs4_stateid			cna_stateid;
+	struct nfs_fh			cna_fh;
+	uint32_t			cna_n_changes;
+	struct cb_notify_changes	*cna_changes;
+};
+
+extern __be32 nfs4_callback_notify(void *argp, void *resp,
+				   struct cb_process_state *cps);
+
 struct cb_devicenotifyitem {
 	uint32_t		cbd_notify_type;
 	uint32_t		cbd_layout_type;
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 10f2354ba304..5c414b3b01d8 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -368,6 +368,15 @@ static void pnfs_recall_all_layouts(struct nfs_client *clp,
 	do_callback_layoutrecall(clp, &args, cps);
 }
 
+__be32 nfs4_callback_notify(void *argp, void *resp,
+			    struct cb_process_state *cps)
+{
+	struct cb_notifyargs *args = argp;
+
+	kfree(args->cna_changes);
+	return 0;
+}
+
 __be32 nfs4_callback_devicenotify(void *argp, void *resp,
 				  struct cb_process_state *cps)
 {
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index 4382baddc9ee..b96d0028fa3d 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -31,6 +31,7 @@
 #define CB_OP_RECALL_RES_MAXSZ		(CB_OP_HDR_RES_MAXSZ)
 
 #define CB_OP_LAYOUTRECALL_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
+#define CB_OP_NOTIFY_RES_MAXSZ		(CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_DEVICENOTIFY_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_SEQUENCE_RES_MAXSZ	(CB_OP_HDR_RES_MAXSZ + \
 					 NFS4_MAX_SESSIONID_LEN + \
@@ -251,6 +252,52 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
 	return 0;
 }
 
+static
+__be32 decode_notify_args(struct svc_rqst *rqstp,
+			  struct xdr_stream *xdr,
+			  void *argp)
+{
+	struct cb_notifyargs *args = argp;
+	__be32 status;
+	size_t res;
+	__be32 *p;
+	int i;
+
+	status = decode_stateid(xdr, &args->cna_stateid);
+	if (unlikely(status != 0))
+		return status;
+
+	status = decode_fh(xdr, &args->cna_fh);
+	if (unlikely(status != 0))
+		return status;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		goto out;
+
+	args->cna_n_changes = ntohl(*p++);
+	args->cna_changes = kmalloc_array(args->cna_n_changes,
+					  sizeof(*args->cna_changes),
+					  GFP_KERNEL);
+	if (!args->cna_changes)
+		goto out;
+
+	for (i = 0; i < args->cna_n_changes; i++) {
+		struct cb_notify_changes *change = &args->cna_changes[i];
+
+		res = xdr_stream_decode_uint32_array(xdr,
+						     &change->notify_mask, 1);
+		if (unlikely(res < 0))
+			goto err;
+	}
+
+	return 0;
+err:
+	kfree(args->cna_changes);
+out:
+	return htonl(NFS4ERR_BADXDR);
+}
+
 static
 __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
 				struct xdr_stream *xdr,
@@ -797,12 +844,12 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
 	case OP_CB_RECALL_ANY:
 	case OP_CB_RECALL_SLOT:
 	case OP_CB_LAYOUTRECALL:
+	case OP_CB_NOTIFY:
 	case OP_CB_NOTIFY_DEVICEID:
 	case OP_CB_NOTIFY_LOCK:
 		*op = &callback_ops[op_nr];
 		break;
 
-	case OP_CB_NOTIFY:
 	case OP_CB_PUSH_DELEG:
 	case OP_CB_RECALLABLE_OBJ_AVAIL:
 	case OP_CB_WANTS_CANCELLED:
@@ -1034,6 +1081,11 @@ static struct callback_op callback_ops[] = {
 		.decode_args = decode_layoutrecall_args,
 		.res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ,
 	},
+	[OP_CB_NOTIFY] = {
+		.process_op = nfs4_callback_notify,
+		.decode_args = decode_notify_args,
+		.res_maxsize = CB_OP_NOTIFY_RES_MAXSZ,
+	},
 	[OP_CB_NOTIFY_DEVICEID] = {
 		.process_op = nfs4_callback_devicenotify,
 		.decode_args = decode_devicenotify_args,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index c23c2eee1b5c..64b8fd061852 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2011,7 +2011,7 @@ static void
 encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
 	struct timespec64 ts = { 0, 0 };
-	u32 notifications[1] = { 0 };
+	u32 notifications[1] = { CB_NOTIFY4_GFLAG_EXTEND };
 	u32 attributes[1] = { 0 };
 	__be32 *p;
 
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 44e5e9fa12e1..fcf0eee55c35 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -482,6 +482,9 @@ enum {
 #define THRESHOLD_RD_IO                 (1UL << 2)
 #define THRESHOLD_WR_IO                 (1UL << 3)
 
+/* Directory Delegation / CB_NOTIFY bits */
+#define CB_NOTIFY4_GFLAG_EXTEND		(1UL << 6)
+
 #define NFSPROC4_NULL 0
 #define NFSPROC4_COMPOUND 1
 #define NFS4_VERSION 4
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/4] NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY
  2026-08-04 20:59 [PATCH 0/4] NFS: Add directory delegation support for CB_NOTIFY Anna Schumaker
  2026-08-04 20:59 ` [PATCH 1/4] NFS: Add an outline for the CB_NOTIFY operation Anna Schumaker
@ 2026-08-04 20:59 ` Anna Schumaker
  2026-08-04 20:59 ` [PATCH 3/4] NFS: Add support for CB_NOTIFY4_ADD_ENTRY Anna Schumaker
  2026-08-04 20:59 ` [PATCH 4/4] NFS: Add support for CB_NOTIFY4_RENAME_ENTRY Anna Schumaker
  3 siblings, 0 replies; 5+ messages in thread
From: Anna Schumaker @ 2026-08-04 20:59 UTC (permalink / raw)
  To: linux-nfs, trond.myklebust; +Cc: anna

From: Anna Schumaker <anna.schumaker@oracle.com>

When the server tells us that a directory entry has been removed then we
need to take that as an indication that our knowledge of the directory
has changed and needs to be refreshed.

Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
---
 fs/nfs/callback.h      | 16 +++++++++-
 fs/nfs/callback_proc.c | 68 +++++++++++++++++++++++++++++++++++++++++-
 fs/nfs/callback_xdr.c  | 59 ++++++++++++++++++++++++++++++++++++
 fs/nfs/nfs4xdr.c       |  3 +-
 include/linux/nfs4.h   |  1 +
 5 files changed, 144 insertions(+), 3 deletions(-)

diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index f7cc5b6931bf..3740c999bb82 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -143,8 +143,22 @@ struct cb_layoutrecallargs {
 extern __be32 nfs4_callback_layoutrecall(void *argp, void *resp,
 		struct cb_process_state *cps);
 
+struct cb_notify_entry {
+	u32			ne_namelen;
+	const char		*ne_name;
+	struct nfs_fattr	ne_attrs;
+};
+
+struct cb_notify_remove {
+	struct cb_notify_entry	nrm_old_entry;
+	u64			nrm_old_entry_cookie;
+};
+
 struct cb_notify_changes {
-	u32 notify_mask;
+	u32	notify_mask;
+	union {
+		struct cb_notify_remove notify_remove;
+	};
 };
 
 struct cb_notifyargs {
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 5c414b3b01d8..356fbd8428ec 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -9,6 +9,7 @@
 
 #include <linux/errno.h>
 #include <linux/math.h>
+#include <linux/namei.h>
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
 #include <linux/slab.h>
@@ -368,13 +369,78 @@ static void pnfs_recall_all_layouts(struct nfs_client *clp,
 	do_callback_layoutrecall(clp, &args, cps);
 }
 
+static struct dentry *nfs4_cb_notify_lookup(struct dentry *parent,
+					    struct cb_notify_entry *entry)
+{
+	struct qstr filename = QSTR_INIT(entry->ne_name, entry->ne_namelen);
+	return try_lookup_noperm(&filename, parent);
+}
+
+static __be32 nfs4_cb_notify_remove(struct cb_process_state *cps,
+				    struct dentry *parent,
+				    struct cb_notify_remove *cb_remove)
+{
+	struct dentry *child;
+
+	child = nfs4_cb_notify_lookup(parent, &cb_remove->nrm_old_entry);
+	if (IS_ERR_OR_NULL(child))
+		return htonl(NFS4ERR_BADHANDLE);
+
+	nfs_set_cache_invalid(parent->d_inode, NFS_INO_INVALID_DATA);
+	d_drop(child);
+	dput(child);
+	return 0;
+}
+
 __be32 nfs4_callback_notify(void *argp, void *resp,
 			    struct cb_process_state *cps)
 {
 	struct cb_notifyargs *args = argp;
+	struct dentry *parent;
+	struct inode *inode;
+	unsigned int i;
+	__be32 res;
 
+	if (!cps->clp) {
+		res = htonl(NFS4ERR_OP_NOT_IN_SESSION);
+		goto out;
+	}
+
+	inode = nfs_delegation_find_inode(cps->clp, &args->cna_fh);
+	if (IS_ERR(inode)) {
+		res = htonl(NFS4ERR_BADHANDLE);
+		goto out;
+	}
+	parent = d_find_alias(inode);
+	if (!parent) {
+		res = 0;
+		goto out_iput;
+	}
+
+	for (i = 0; i < args->cna_n_changes; i++) {
+		struct cb_notify_changes *change = &args->cna_changes[i];
+
+		switch (change->notify_mask) {
+		case CB_NOTIFY4_REMOVE_ENTRY:
+			res = nfs4_cb_notify_remove(cps, parent,
+						    &change->notify_remove);
+			break;
+		default:
+			res = htonl(NFS4ERR_NOTSUPP);
+			goto out_dput;
+		}
+
+		if (res < 0)
+			break;
+	}
+
+out_dput:
+	dput(parent);
+out_iput:
+	nfs_iput_and_deactive(inode);
+out:
 	kfree(args->cna_changes);
-	return 0;
+	return res;
 }
 
 __be32 nfs4_callback_devicenotify(void *argp, void *resp,
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index b96d0028fa3d..fd54f31e89ac 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -252,6 +252,50 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
 	return 0;
 }
 
+static __be32 decode_notify_entry(struct xdr_stream *xdr,
+				  struct cb_notify_entry *args)
+{
+	uint32_t bitmap[3] = {0};
+	__be32 status;
+	u32 attrlen;
+	__be32 *p;
+
+	status = decode_string(xdr, &args->ne_namelen, &args->ne_name,
+				NFS4_OPAQUE_LIMIT);
+	if (unlikely(status != 0))
+		return status;
+
+	status = decode_bitmap(xdr, bitmap);
+	if (unlikely(status != 0))
+		return status;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+
+	attrlen = be32_to_cpup(p);
+	if (attrlen != 0)
+		return htonl(NFS4ERR_BADXDR);
+	return 0;
+}
+
+static __be32 decode_notify_remove(struct xdr_stream *xdr,
+				   struct cb_notify_remove *args)
+{
+	__be32 status;
+	__be32 *p;
+
+	status = decode_notify_entry(xdr, &args->nrm_old_entry);
+	if (unlikely(status != 0))
+		return status;
+
+	p = xdr_inline_decode(xdr, 8);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	xdr_decode_hyper(p, &args->nrm_old_entry_cookie);
+	return 0;
+}
+
 static
 __be32 decode_notify_args(struct svc_rqst *rqstp,
 			  struct xdr_stream *xdr,
@@ -289,6 +333,21 @@ __be32 decode_notify_args(struct svc_rqst *rqstp,
 						     &change->notify_mask, 1);
 		if (unlikely(res < 0))
 			goto err;
+
+		/* Decode opaque size */
+		p = xdr_inline_decode(xdr, 4);
+		if (unlikely(!p))
+			goto err;
+		res = ntohl(*p);
+
+		switch (change->notify_mask) {
+		case CB_NOTIFY4_REMOVE_ENTRY:
+			status = decode_notify_remove(xdr,
+						      &change->notify_remove);
+			break;
+		default:
+			goto err;
+		}
 	}
 
 	return 0;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 64b8fd061852..65f9e995fa62 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2011,7 +2011,8 @@ static void
 encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
 	struct timespec64 ts = { 0, 0 };
-	u32 notifications[1] = { CB_NOTIFY4_GFLAG_EXTEND };
+	u32 notifications[1] = { CB_NOTIFY4_REMOVE_ENTRY |
+				 CB_NOTIFY4_GFLAG_EXTEND };
 	u32 attributes[1] = { 0 };
 	__be32 *p;
 
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index fcf0eee55c35..0f84be8e2fd8 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -483,6 +483,7 @@ enum {
 #define THRESHOLD_WR_IO                 (1UL << 3)
 
 /* Directory Delegation / CB_NOTIFY bits */
+#define CB_NOTIFY4_REMOVE_ENTRY		(1UL << 2)
 #define CB_NOTIFY4_GFLAG_EXTEND		(1UL << 6)
 
 #define NFSPROC4_NULL 0
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/4] NFS: Add support for CB_NOTIFY4_ADD_ENTRY
  2026-08-04 20:59 [PATCH 0/4] NFS: Add directory delegation support for CB_NOTIFY Anna Schumaker
  2026-08-04 20:59 ` [PATCH 1/4] NFS: Add an outline for the CB_NOTIFY operation Anna Schumaker
  2026-08-04 20:59 ` [PATCH 2/4] NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY Anna Schumaker
@ 2026-08-04 20:59 ` Anna Schumaker
  2026-08-04 20:59 ` [PATCH 4/4] NFS: Add support for CB_NOTIFY4_RENAME_ENTRY Anna Schumaker
  3 siblings, 0 replies; 5+ messages in thread
From: Anna Schumaker @ 2026-08-04 20:59 UTC (permalink / raw)
  To: linux-nfs, trond.myklebust; +Cc: anna

From: Anna Schumaker <anna.schumaker@oracle.com>

When the server tells us that a directory entry has been created, we
need to instantiate a new dentry and add it to the dcache on the client.

Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
---
 fs/nfs/callback.h      | 17 +++++++++++
 fs/nfs/callback_proc.c | 58 ++++++++++++++++++++++++++++++++++--
 fs/nfs/callback_xdr.c  | 67 +++++++++++++++++++++++++++++++++++-------
 fs/nfs/nfs4_fs.h       |  2 ++
 fs/nfs/nfs4xdr.c       | 27 +++++++++++++++--
 include/linux/nfs4.h   |  1 +
 6 files changed, 155 insertions(+), 17 deletions(-)

diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index 3740c999bb82..d3cc3e5df776 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -147,6 +147,12 @@ struct cb_notify_entry {
 	u32			ne_namelen;
 	const char		*ne_name;
 	struct nfs_fattr	ne_attrs;
+	struct nfs_fh		ne_fh;
+};
+
+struct cb_notify_prev_entry {
+	struct cb_notify_entry	pe_prev_entry;
+	u64			pe_prev_entry_cookie;
 };
 
 struct cb_notify_remove {
@@ -154,10 +160,21 @@ struct cb_notify_remove {
 	u64			nrm_old_entry_cookie;
 };
 
+struct cb_notify_add {
+	bool			na_have_old_entry;
+	struct cb_notify_remove	na_old_entry;
+	struct cb_notify_entry	na_new_entry;
+	bool			na_have_new_entry_cookie;
+	u64			na_new_entry_cookie;
+	bool			na_have_prev_entry;
+	bool			na_last_entry;
+};
+
 struct cb_notify_changes {
 	u32	notify_mask;
 	union {
 		struct cb_notify_remove notify_remove;
+		struct cb_notify_add	notify_add;
 	};
 };
 
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 356fbd8428ec..9fd0f93e6e5a 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -370,10 +370,15 @@ static void pnfs_recall_all_layouts(struct nfs_client *clp,
 }
 
 static struct dentry *nfs4_cb_notify_lookup(struct dentry *parent,
-					    struct cb_notify_entry *entry)
+					    struct cb_notify_entry *entry,
+					    bool alloc_missing)
 {
 	struct qstr filename = QSTR_INIT(entry->ne_name, entry->ne_namelen);
-	return try_lookup_noperm(&filename, parent);
+	struct dentry *child = try_lookup_noperm(&filename, parent);
+
+	if (!child && alloc_missing)
+		child = d_alloc(parent, &filename);
+	return child;
 }
 
 static __be32 nfs4_cb_notify_remove(struct cb_process_state *cps,
@@ -382,7 +387,7 @@ static __be32 nfs4_cb_notify_remove(struct cb_process_state *cps,
 {
 	struct dentry *child;
 
-	child = nfs4_cb_notify_lookup(parent, &cb_remove->nrm_old_entry);
+	child = nfs4_cb_notify_lookup(parent, &cb_remove->nrm_old_entry, false);
 	if (IS_ERR_OR_NULL(child))
 		return htonl(NFS4ERR_BADHANDLE);
 
@@ -392,6 +397,49 @@ static __be32 nfs4_cb_notify_remove(struct cb_process_state *cps,
 	return 0;
 }
 
+static __be32 nfs4_cb_notify_add(struct cb_process_state *cps,
+				 struct dentry *parent,
+				 struct cb_notify_add *cb_add)
+{
+	struct nfs_entry entry = {
+		.cookie = cb_add->na_new_entry_cookie,
+		.name = cb_add->na_new_entry.ne_name,
+		.len = cb_add->na_new_entry.ne_namelen,
+		.eof = cb_add->na_last_entry,
+		.fh = &cb_add->na_new_entry.ne_fh,
+		.fattr = &cb_add->na_new_entry.ne_attrs,
+		.server = NFS_SERVER(d_inode(parent)),
+	};
+	struct dentry *dentry;
+
+	dentry = nfs4_cb_notify_lookup(parent, &cb_add->na_new_entry, true);
+	if (IS_ERR_OR_NULL(dentry))
+		return 0;
+
+	if (!d_in_lookup(dentry) && entry.fh->size > 0) {
+		struct inode *inode = d_inode(dentry);
+
+		if (!inode) {
+			inode = nfs_fhget(parent->d_sb, entry.fh, entry.fattr);
+			if (IS_ERR(inode))
+				goto out;
+		}
+		if (inode) {
+			nfs_set_verifier(dentry, parent->d_time);
+			nfs_refresh_inode(inode, entry.fattr);
+			d_instantiate(dentry, inode);
+			iput(inode);
+		}
+	}
+
+out:
+	d_lookup_done(dentry);
+	dput(dentry);
+
+	nfs_set_cache_invalid(parent->d_inode, NFS_INO_INVALID_DATA);
+	return 0;
+}
+
 __be32 nfs4_callback_notify(void *argp, void *resp,
 			    struct cb_process_state *cps)
 {
@@ -425,6 +473,10 @@ __be32 nfs4_callback_notify(void *argp, void *resp,
 			res = nfs4_cb_notify_remove(cps, parent,
 						    &change->notify_remove);
 			break;
+		case CB_NOTIFY4_ADD_ENTRY:
+			res = nfs4_cb_notify_add(cps, parent,
+						 &change->notify_add);
+			break;
 		default:
 			res = htonl(NFS4ERR_NOTSUPP);
 			goto out_dput;
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index fd54f31e89ac..2885c25d0742 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -257,26 +257,20 @@ static __be32 decode_notify_entry(struct xdr_stream *xdr,
 {
 	uint32_t bitmap[3] = {0};
 	__be32 status;
-	u32 attrlen;
-	__be32 *p;
 
 	status = decode_string(xdr, &args->ne_namelen, &args->ne_name,
 				NFS4_OPAQUE_LIMIT);
 	if (unlikely(status != 0))
 		return status;
 
-	status = decode_bitmap(xdr, bitmap);
-	if (unlikely(status != 0))
-		return status;
-
-	p = xdr_inline_decode(xdr, 4);
-	if (unlikely(!p))
+	status = xdr_stream_decode_uint32_array(xdr, bitmap, 3);
+	if (unlikely(status == 0 || status > 3))
 		return htonl(NFS4ERR_BADXDR);
 
-	attrlen = be32_to_cpup(p);
-	if (attrlen != 0)
+	status = decode_fattr_cb(xdr, bitmap, &args->ne_attrs, &args->ne_fh);
+	if (unlikely(status < 0))
 		return htonl(NFS4ERR_BADXDR);
-	return 0;
+	return status;
 }
 
 static __be32 decode_notify_remove(struct xdr_stream *xdr,
@@ -296,6 +290,54 @@ static __be32 decode_notify_remove(struct xdr_stream *xdr,
 	return 0;
 }
 
+static __be32 decode_notify_add(struct xdr_stream *xdr,
+				struct cb_notify_add *args)
+{
+	__be32 status;
+	__be32 *p;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_have_old_entry = ntohl(*p);
+
+	if (args->na_have_old_entry) {
+		status = decode_notify_remove(xdr, &args->na_old_entry);
+		if (unlikely(status != 0))
+			return status;
+	}
+
+	status = decode_notify_entry(xdr, &args->na_new_entry);
+	if (unlikely(status != 0))
+		return status;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_have_new_entry_cookie = ntohl(*p);
+
+	if (args->na_have_new_entry_cookie) {
+		p = xdr_inline_decode(xdr, 8);
+		if (unlikely(!p))
+			return htonl(NFS4ERR_BADXDR);
+
+		xdr_decode_hyper(p, &args->na_new_entry_cookie);
+	}
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_have_prev_entry = ntohl(*p);
+
+	WARN_ONCE(args->na_have_prev_entry, "NFS: Have prev entry unimplemented\n");
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_last_entry = ntohl(*p);
+	return 0;
+}
+
 static
 __be32 decode_notify_args(struct svc_rqst *rqstp,
 			  struct xdr_stream *xdr,
@@ -345,6 +387,9 @@ __be32 decode_notify_args(struct svc_rqst *rqstp,
 			status = decode_notify_remove(xdr,
 						      &change->notify_remove);
 			break;
+		case CB_NOTIFY4_ADD_ENTRY:
+			status = decode_notify_add(xdr, &change->notify_add);
+			break;
 		default:
 			goto err;
 		}
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index b48e5b87cb2a..b029c9cefc38 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -586,6 +586,8 @@ extern const u32 nfs42_maxlistxattrs_overhead;
 #endif
 
 struct nfs4_mount_data;
+int decode_fattr_cb(struct xdr_stream *xdr, uint32_t *bitmap,
+		    struct nfs_fattr *fattr, struct nfs_fh *fhandle);
 
 /* callback_xdr.c */
 extern const struct svc_version nfs4_callback_version1;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 65f9e995fa62..2d28c3ae37ad 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2012,8 +2012,11 @@ encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
 	struct timespec64 ts = { 0, 0 };
 	u32 notifications[1] = { CB_NOTIFY4_REMOVE_ENTRY |
+				 CB_NOTIFY4_ADD_ENTRY |
 				 CB_NOTIFY4_GFLAG_EXTEND };
-	u32 attributes[1] = { 0 };
+	u32 child_attrs[1] = { FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID |
+			       FATTR4_WORD0_TYPE | FATTR4_WORD0_FILEHANDLE };
+	u32 dir_attrs[1] = { 0 };
 	__be32 *p;
 
 	encode_op_hdr(xdr, OP_GET_DIR_DELEGATION, decode_get_dir_deleg_maxsz, hdr);
@@ -2029,10 +2032,10 @@ encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
 	xdr_encode_nfstime4(p, &ts);
 
 	/* Requested child attributes */
-	xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+	xdr_encode_bitmap4(xdr, child_attrs, ARRAY_SIZE(child_attrs));
 
 	/* Requested dir attributes */
-	xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+	xdr_encode_bitmap4(xdr, dir_attrs, ARRAY_SIZE(dir_attrs));
 }
 
 static void
@@ -4886,6 +4889,24 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
 	return decode_getfattr_generic(xdr, fattr, NULL, NULL, server);
 }
 
+int decode_fattr_cb(struct xdr_stream *xdr, uint32_t *bitmap,
+			     struct nfs_fattr *fattr, struct nfs_fh *fhandle)
+{
+	unsigned int savep;
+	uint32_t attrlen;
+	int status;
+
+	status = decode_attr_length(xdr, &attrlen, &savep);
+	if (status < 0)
+		return status;
+
+	status = decode_getfattr_attrs(xdr, bitmap, fattr, fhandle, NULL, NULL);
+	if (status < 0)
+		return status;
+
+	return verify_attr_len(xdr, savep, attrlen);
+}
+
 /*
  * Decode potentially multiple layout types.
  */
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 0f84be8e2fd8..9435ec5674d0 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -484,6 +484,7 @@ enum {
 
 /* Directory Delegation / CB_NOTIFY bits */
 #define CB_NOTIFY4_REMOVE_ENTRY		(1UL << 2)
+#define CB_NOTIFY4_ADD_ENTRY		(1UL << 3)
 #define CB_NOTIFY4_GFLAG_EXTEND		(1UL << 6)
 
 #define NFSPROC4_NULL 0
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 4/4] NFS: Add support for CB_NOTIFY4_RENAME_ENTRY
  2026-08-04 20:59 [PATCH 0/4] NFS: Add directory delegation support for CB_NOTIFY Anna Schumaker
                   ` (2 preceding siblings ...)
  2026-08-04 20:59 ` [PATCH 3/4] NFS: Add support for CB_NOTIFY4_ADD_ENTRY Anna Schumaker
@ 2026-08-04 20:59 ` Anna Schumaker
  3 siblings, 0 replies; 5+ messages in thread
From: Anna Schumaker @ 2026-08-04 20:59 UTC (permalink / raw)
  To: linux-nfs, trond.myklebust; +Cc: anna

From: Anna Schumaker <anna.schumaker@oracle.com>

Renaming an entry essentially combines a remove with an add, allowing us
to reuse most of the code from the previous patches to do the work for
us.

Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
---
 fs/nfs/callback.h      |  6 ++++++
 fs/nfs/callback_proc.c | 16 ++++++++++++++++
 fs/nfs/callback_xdr.c  | 14 ++++++++++++++
 fs/nfs/nfs4xdr.c       |  1 +
 include/linux/nfs4.h   |  1 +
 5 files changed, 38 insertions(+)

diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index d3cc3e5df776..019ee9eed9fe 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -170,11 +170,17 @@ struct cb_notify_add {
 	bool			na_last_entry;
 };
 
+struct cb_notify_rename {
+	struct cb_notify_remove	nrn_old_entry;
+	struct cb_notify_add	nrn_new_entry;
+};
+
 struct cb_notify_changes {
 	u32	notify_mask;
 	union {
 		struct cb_notify_remove notify_remove;
 		struct cb_notify_add	notify_add;
+		struct cb_notify_rename	notify_rename;
 	};
 };
 
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 9fd0f93e6e5a..5dff65ac0377 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -440,6 +440,18 @@ static __be32 nfs4_cb_notify_add(struct cb_process_state *cps,
 	return 0;
 }
 
+static __be32 nfs4_cb_notify_rename(struct cb_process_state *cps,
+				    struct dentry *parent,
+				    struct cb_notify_rename *cb_rename)
+{
+	__be32 status;
+
+	status = nfs4_cb_notify_remove(cps, parent, &cb_rename->nrn_old_entry);
+	if (status != 0)
+		return status;
+	return nfs4_cb_notify_add(cps, parent, &cb_rename->nrn_new_entry);
+}
+
 __be32 nfs4_callback_notify(void *argp, void *resp,
 			    struct cb_process_state *cps)
 {
@@ -477,6 +489,10 @@ __be32 nfs4_callback_notify(void *argp, void *resp,
 			res = nfs4_cb_notify_add(cps, parent,
 						 &change->notify_add);
 			break;
+		case CB_NOTIFY4_RENAME_ENTRY:
+			res = nfs4_cb_notify_rename(cps, parent,
+						    &change->notify_rename);
+			break;
 		default:
 			res = htonl(NFS4ERR_NOTSUPP);
 			goto out_dput;
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index 2885c25d0742..271476e9472d 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -338,6 +338,17 @@ static __be32 decode_notify_add(struct xdr_stream *xdr,
 	return 0;
 }
 
+static __be32 decode_notify_rename(struct xdr_stream *xdr,
+				   struct cb_notify_rename *args)
+{
+	__be32 status;
+
+	status = decode_notify_remove(xdr, &args->nrn_old_entry);
+	if (unlikely(status != 0))
+		return status;
+	return decode_notify_add(xdr, &args->nrn_new_entry);
+}
+
 static
 __be32 decode_notify_args(struct svc_rqst *rqstp,
 			  struct xdr_stream *xdr,
@@ -390,6 +401,9 @@ __be32 decode_notify_args(struct svc_rqst *rqstp,
 		case CB_NOTIFY4_ADD_ENTRY:
 			status = decode_notify_add(xdr, &change->notify_add);
 			break;
+		case CB_NOTIFY4_RENAME_ENTRY:
+			status = decode_notify_rename(xdr, &change->notify_rename);
+			break;
 		default:
 			goto err;
 		}
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 2d28c3ae37ad..ef9c6a24729b 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2013,6 +2013,7 @@ encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
 	struct timespec64 ts = { 0, 0 };
 	u32 notifications[1] = { CB_NOTIFY4_REMOVE_ENTRY |
 				 CB_NOTIFY4_ADD_ENTRY |
+				 CB_NOTIFY4_RENAME_ENTRY |
 				 CB_NOTIFY4_GFLAG_EXTEND };
 	u32 child_attrs[1] = { FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID |
 			       FATTR4_WORD0_TYPE | FATTR4_WORD0_FILEHANDLE };
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 9435ec5674d0..d3ecdaf01084 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -485,6 +485,7 @@ enum {
 /* Directory Delegation / CB_NOTIFY bits */
 #define CB_NOTIFY4_REMOVE_ENTRY		(1UL << 2)
 #define CB_NOTIFY4_ADD_ENTRY		(1UL << 3)
+#define CB_NOTIFY4_RENAME_ENTRY		(1UL << 4)
 #define CB_NOTIFY4_GFLAG_EXTEND		(1UL << 6)
 
 #define NFSPROC4_NULL 0
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-04 20:59 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 20:59 [PATCH 0/4] NFS: Add directory delegation support for CB_NOTIFY Anna Schumaker
2026-08-04 20:59 ` [PATCH 1/4] NFS: Add an outline for the CB_NOTIFY operation Anna Schumaker
2026-08-04 20:59 ` [PATCH 2/4] NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY Anna Schumaker
2026-08-04 20:59 ` [PATCH 3/4] NFS: Add support for CB_NOTIFY4_ADD_ENTRY Anna Schumaker
2026-08-04 20:59 ` [PATCH 4/4] NFS: Add support for CB_NOTIFY4_RENAME_ENTRY Anna Schumaker

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox