* [PATCH v3 01/34] NFSD: Report a first-batch readdir error in the reply status
2026-10-12 22:02 [PATCH v3 00/34] Convert server-side NFSv3 XDR to use xdrgen Chuck Lever
@ 2026-10-12 22:02 ` Chuck Lever
2026-10-13 16:42 ` [PATCH v3 00/34] Convert server-side NFSv3 XDR to use xdrgen Jeff Layton
1 sibling, 0 replies; 3+ messages in thread
From: Chuck Lever @ 2026-10-12 22:02 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
The NFSv2 READDIR encoder pulls directory entries while the reply
is encoded, after the status word is committed. nfsd_readdir_open()
does not read from the directory, so an iterate_dir() failure on the
first batch surfaces only in nfsd_readdir_next(), where nothing can
turn it into a status. The reply carries NFS_OK, no entries, and eof
false. The client resends the same cookie and receives the same reply,
so a directory that fails with -EIO or -ENOENT traps it in a loop.
Read the first batch in nfsd_readdir_open() so the error selects the
reply status before any of it is committed. An error on a later batch
still cuts the list short with eof false. The client's follow-up
request then starts from that cookie and gets the error in its status.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/vfs.c | 76 +++++++++++++++++++++++++++++++++------------------
1 file changed, 50 insertions(+), 26 deletions(-)
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index c7dd94697f2f..c753d3257f1e 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -2397,6 +2397,33 @@ static bool nfsd_buffered_filldir(struct dir_context *ctx, const char *name,
return true;
}
+/*
+ * Read the next batch of entries. Returns false at end of directory
+ * (@iter->eof set) or on a filesystem error (@iter->host_err set).
+ */
+static bool nfsd_readdir_fill(struct nfsd_readdir_iter *iter)
+{
+ /* Between batches the resume cookie advances past the last. */
+ if (iter->batched)
+ iter->offset = vfs_llseek(iter->file, 0, SEEK_CUR);
+
+ iter->used = 0;
+ iter->full = 0;
+ iter->host_err = iterate_dir(iter->file, &iter->ctx);
+ if (iter->full)
+ iter->host_err = 0;
+ if (iter->host_err < 0)
+ return false;
+ if (!iter->used) {
+ iter->eof = true;
+ return false;
+ }
+ iter->batched = true;
+ iter->pos = iter->page;
+ iter->remaining = iter->used;
+ return true;
+}
+
/**
* nfsd_readdir_open - open a directory for streaming readdir
* @rqstp: RPC transaction context
@@ -2404,13 +2431,15 @@ static bool nfsd_buffered_filldir(struct dir_context *ctx, const char *name,
* @offsetp: seek offset at which to resume reading
* @iter: OUT: directory reader to initialize
*
- * On success the directory is open and positioned at @offsetp, and
- * @iter is ready for nfsd_readdir_next(). The caller must release
- * @iter with nfsd_readdir_close() once done, including on error paths
- * that follow a successful open.
+ * On success the directory is open and positioned at @offsetp, the
+ * first batch of entries has been read, and @iter is ready for
+ * nfsd_readdir_next(). The caller must release @iter with
+ * nfsd_readdir_close() once done, including on error paths that
+ * follow a successful open.
*
- * Return: nfs_ok on success, otherwise an nfsstat code. On error @iter
- * is left safe to pass to nfsd_readdir_close().
+ * Return: nfs_ok on success, otherwise an nfsstat code, including the
+ * error from reading the first batch. On error @iter is left safe to
+ * pass to nfsd_readdir_close().
*/
__be32 nfsd_readdir_open(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t *offsetp, struct nfsd_readdir_iter *iter)
@@ -2446,6 +2475,17 @@ __be32 nfsd_readdir_open(struct svc_rqst *rqstp, struct svc_fh *fhp,
iter->file = file;
iter->fhp = fhp;
iter->offset = offset;
+
+ /*
+ * The first batch is read here so a filesystem error surfaces
+ * in the reply status. Once the encoder has committed nfs_ok,
+ * an error can only be reported as a short list.
+ */
+ if (!nfsd_readdir_fill(iter) && iter->host_err) {
+ err = nfserrno(iter->host_err);
+ nfsd_readdir_close(iter);
+ return err;
+ }
return nfs_ok;
out_close:
@@ -2470,26 +2510,10 @@ struct buffered_dirent *nfsd_readdir_next(struct nfsd_readdir_iter *iter)
struct buffered_dirent *de;
unsigned int reclen;
- while (iter->remaining <= 0) {
- /* Between batches the resume cookie advances past the last. */
- if (iter->batched)
- iter->offset = vfs_llseek(iter->file, 0, SEEK_CUR);
-
- iter->used = 0;
- iter->full = 0;
- iter->host_err = iterate_dir(iter->file, &iter->ctx);
- if (iter->full)
- iter->host_err = 0;
- if (iter->host_err < 0)
- return NULL;
- if (!iter->used) {
- iter->eof = true;
- return NULL;
- }
- iter->batched = true;
- iter->pos = iter->page;
- iter->remaining = iter->used;
- }
+ if (iter->eof || iter->host_err)
+ return NULL;
+ if (iter->remaining <= 0 && !nfsd_readdir_fill(iter))
+ return NULL;
de = (struct buffered_dirent *)iter->pos;
iter->offset = de->offset;
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH v3 00/34] Convert server-side NFSv3 XDR to use xdrgen
2026-10-12 22:02 [PATCH v3 00/34] Convert server-side NFSv3 XDR to use xdrgen Chuck Lever
2026-10-12 22:02 ` [PATCH v3 01/34] NFSD: Report a first-batch readdir error in the reply status Chuck Lever
@ 2026-10-13 16:42 ` Jeff Layton
1 sibling, 0 replies; 3+ messages in thread
From: Jeff Layton @ 2026-10-13 16:42 UTC (permalink / raw)
To: Chuck Lever, NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
On Mon, 2026-10-12 at 18:02 -0400, Chuck Lever wrote:
> Replace NFSD's NFSv3 XDR encoders and decoders with code that is
> mechanically generated directly from the NFSv3 RPC language
> specification. Unlike NFSv2, where there are at least two
> incomplete language specifications and much lore, nfs3.x here is
> based closely on RFC 1813.
>
> As we convert newer NFS versions to use xdrgen, I expect a reduction
> in human coding errors, improved spec compliance, quicker implement-
> ation of new protocol, and better memory safety for the more complex
> NFS procedures.
>
> Changes in v3:
> - Copy the SYMLINK target with nfsd_symlink_target(), moved into vfs.c.
> - Add a patch removing the now-unused svc_fill_symlink_pathname().
> - Reject a READDIRPLUS entry on its minimum size before the lookup.
> - Drop the pr_warn_once() for an unexpected file type in GETATTR.
> - Explain why a late readdir host error is not reported as the status.
> - Write "committed", not "on the wire", in the first-batch patch.
> - Link to v2: https://lore.kernel.org/linux-nfs/20260924171012.3978-1-cel@kernel.org/
>
> Changes in v2:
> - Include the generated NFSv3 header directly in nfsfh.c and nfsctl.c.
> - Remove the unused struct nfsd3_createargs in the MKDIR patch.
> - Regenerate the READDIR and READDIRPLUS result decoders.
> - Drop the decode-hook stubs the regenerated decoders no longer call.
> - Link to v1: https://lore.kernel.org/linux-nfs/20260923132811.244160-1-cel@kernel.org/
>
> Chuck Lever (34):
> NFSD: Report a first-batch readdir error in the reply status
> Documentation: Add the RPC language description of NFSv3
> NFSD: Add infrastructure for generating NFSv3 XDR encoders and
> decoders
> NFSD: Replace nfs3.h with nfs3xdr_gen.h
> NFSD: Replace the nfsd3_createres macro
> NFSD: Relocate the NFSv3 XDR storage union into nfs3proc.c
> NFSD: Use xdrgen XDR functions for the NFSv3 NULL procedure
> NFSD: Use xdrgen XDR functions for NFSv3 GETATTR procedure
> NFSD: Use xdrgen XDR functions for NFSv3 SETATTR procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure
> NFSD: Use xdrgen XDR functions for NFSv3 ACCESS procedure
> NFSD: Use xdrgen XDR functions for NFSv3 READLINK procedure
> NFSD: Use xdrgen XDR functions for NFSv3 READ procedure
> NFSD: Use xdrgen XDR functions for NFSv3 WRITE procedure
> NFSD: Use xdrgen XDR functions for NFSv3 CREATE procedure
> NFSD: Use xdrgen XDR functions for NFSv3 MKDIR procedure
> NFSD: Use xdrgen XDR functions for NFSv3 SYMLINK procedure
> NFSD: Use xdrgen XDR functions for NFSv3 MKNOD procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 REMOVE procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 RMDIR procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 RENAME procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 LINK procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 FSSTAT procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 FSINFO procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 PATHCONF procedure
> NFSD: Use xdrgen XDR functions for the NFSv3 COMMIT procedure
> NFSD: Use xdrgen XDR functions for NFSv3 READDIR arguments
> NFSD: Use xdrgen XDR functions for NFSv3 READDIRPLUS arguments
> NFSD: Refactor NFSv3 directory cookie encoding
> NFSD: Refactor NFSv3 directory entry encoding
> NFSD: Split struct nfsd3_readdirres
> NFSD: Use xdrgen XDR functions for NFSv3 READDIR results
> NFSD: Use xdrgen XDR functions for NFSv3 READDIRPLUS results
> SUNRPC: Remove svc_fill_symlink_pathname()
>
> Documentation/sunrpc/xdr/nfs3.x | 839 ++++++
> fs/nfsd/Makefile | 15 +-
> fs/nfsd/nfs2acl.c | 41 +-
> fs/nfsd/nfs3acl.c | 12 +-
> fs/nfsd/nfs3proc.c | 2541 +++++++++++++------
> fs/nfsd/nfs3xdr.c | 1374 +++-------
> fs/nfsd/nfs3xdr_gen.c | 3808 ++++++++++++++++++++++++++++
> fs/nfsd/nfs3xdr_gen.h | 82 +
> fs/nfsd/nfsctl.c | 2 +-
> fs/nfsd/nfserr.h | 2 +-
> fs/nfsd/nfsfh.c | 2 +-
> fs/nfsd/nfsproc.c | 22 -
> fs/nfsd/vfs.c | 105 +-
> fs/nfsd/vfs.h | 1 +
> fs/nfsd/xdr3.h | 300 +--
> include/linux/nfs3.h | 3 -
> include/linux/sunrpc/svc.h | 3 -
> include/linux/sunrpc/xdrgen/nfs3.h | 1092 ++++++++
> net/sunrpc/svc.c | 50 -
> 19 files changed, 8052 insertions(+), 2242 deletions(-)
> create mode 100644 Documentation/sunrpc/xdr/nfs3.x
> create mode 100644 fs/nfsd/nfs3xdr_gen.c
> create mode 100644 fs/nfsd/nfs3xdr_gen.h
> create mode 100644 include/linux/sunrpc/xdrgen/nfs3.h
I skimmed over the changes. They all look fairly straightforward and I
definitely support moving toward machine-generated code for this sort
of thing.
Acked-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 3+ messages in thread