From: Chuck Lever <cel@kernel.org>
To: NeilBrown <neil@brown.name>, Jeff Layton <jlayton@kernel.org>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <dai.ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Cc: <linux-nfs@vger.kernel.org>, Chuck Lever <chuck.lever@oracle.com>
Subject: [PATCH v1 14/27] NFSD: Use xdrgen XDR functions for NFSv2 WRITE procedure
Date: Wed, 16 Sep 2026 12:28:45 -0400 [thread overview]
Message-ID: <20260916162859.2051-17-cel@kernel.org> (raw)
In-Reply-To: <20260916162859.2051-1-cel@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
The NFSv2 WRITE argument decoder could not be generated because
the write payload must be directed into the server's page cache
and decoded in place; a materializing decoder would copy every
write. The "pages" directive now expresses this: it represents
the payload as a struct xdr_buf and decodes it with
svcxdr_decode_opaque_payload(), which subsegments the source
stream in place rather than copying it.
Annotate the data member of writeargs with "pragma pages" and
replace the NFSPROC_WRITE entry in nfsd_procedures2 with the
generated nfs_svc_decode_writeargs and nfs_svc_encode_attrstat. A
wrapper structure bridges the generated writeargs type and the
svc_fh the NFSD VFS layer still uses; the result reuses the
attrstat wrapper from the GETATTR conversion.
The pc_argzero field is now set to zero for the NFSv2 WRITE
procedure. The generated decoder initializes every argument in
the argp->xdrgen field, making the early defensive memset
unnecessary.
This refactor replaces the use of svcxdr_encode_fattr(), so the
reference to the file handle can be released directly by
nfsd_proc_write(). A separate ->pc_release callback is thus no
longer needed. This makes it straightforward to confirm that the
file handle is properly released during every error flow.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
Documentation/sunrpc/xdr/nfs2.x | 1 +
fs/nfsd/nfs2xdr_gen.c | 6 +--
fs/nfsd/nfs2xdr_gen.h | 2 +-
fs/nfsd/nfsproc.c | 76 ++++++++++++++++++------------
fs/nfsd/nfsxdr.c | 26 ----------
fs/nfsd/xdr.h | 8 ----
include/linux/sunrpc/xdrgen/nfs2.h | 4 +-
7 files changed, 54 insertions(+), 69 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs2.x b/Documentation/sunrpc/xdr/nfs2.x
index b64b939f11f8..b2ed52bd1631 100644
--- a/Documentation/sunrpc/xdr/nfs2.x
+++ b/Documentation/sunrpc/xdr/nfs2.x
@@ -156,6 +156,7 @@ struct writeargs {
unsigned int totalcount;
opaque data<NFS_MAXDATA>;
};
+pragma pages writeargs data;
struct createargs {
diropargs where;
diff --git a/fs/nfsd/nfs2xdr_gen.c b/fs/nfsd/nfs2xdr_gen.c
index abde6fa8e510..3c2f78c802a6 100644
--- a/fs/nfsd/nfs2xdr_gen.c
+++ b/fs/nfsd/nfs2xdr_gen.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs2.x
-// XDR specification modification time: Tue Sep 8 11:25:45 2026
+// XDR specification modification time: Tue Sep 8 11:26:00 2026
#include <linux/sunrpc/svc.h>
@@ -282,7 +282,7 @@ xdrgen_decode_writeargs(struct xdr_stream *xdr, struct writeargs *ptr)
return false;
if (!xdrgen_decode_unsigned_int(xdr, &ptr->totalcount))
return false;
- if (!xdrgen_decode_opaque(xdr, &ptr->data, NFS_MAXDATA))
+ if (!svcxdr_decode_opaque_payload(xdr, &ptr->data, NFS_MAXDATA))
return false;
return true;
}
@@ -811,7 +811,7 @@ xdrgen_encode_writeargs(struct xdr_stream *xdr, const struct writeargs *value)
return false;
if (value->data.len > NFS_MAXDATA)
return false;
- if (xdr_stream_encode_opaque(xdr, value->data.data, value->data.len) < 0)
+ if (!svcxdr_encode_opaque_payload(xdr, value->data.len))
return false;
return true;
}
diff --git a/fs/nfsd/nfs2xdr_gen.h b/fs/nfsd/nfs2xdr_gen.h
index e8ff072e32e5..8f7d17e8e483 100644
--- a/fs/nfsd/nfs2xdr_gen.h
+++ b/fs/nfsd/nfs2xdr_gen.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs2.x */
-/* XDR specification modification time: Tue Sep 8 11:25:45 2026 */
+/* XDR specification modification time: Tue Sep 8 11:26:00 2026 */
#ifndef _LINUX_XDRGEN_NFS2_DECL_H
#define _LINUX_XDRGEN_NFS2_DECL_H
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 71b792c8d78f..4d09da326070 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -70,6 +70,13 @@ struct readargs_wrapper {
static_assert(offsetof(struct readargs_wrapper, xdrgen) == 0);
+struct writeargs_wrapper {
+ struct writeargs xdrgen;
+ struct svc_fh fh;
+};
+
+static_assert(offsetof(struct writeargs_wrapper, xdrgen) == 0);
+
static __be32 nfsd_map_status(__be32 status)
{
switch (status) {
@@ -581,29 +588,41 @@ static __be32 nfsd_proc_writecache(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Write data to a file
- * N.B. After this call resp->fh needs an fh_put
+/**
+ * nfsd_proc_write - WRITE: Write to file
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * attrstat NFSPROC_WRITE(writeargs) = 8;
*/
-static __be32
-nfsd_proc_write(struct svc_rqst *rqstp)
+static __be32 nfsd_proc_write(struct svc_rqst *rqstp)
{
- struct nfsd_writeargs *argp = rqstp->rq_argp;
- struct nfsd_attrstat *resp = rqstp->rq_resp;
- unsigned long cnt = argp->len;
+ struct writeargs_wrapper *argp = rqstp->rq_argp;
+ struct attrstat_wrapper *resp = rqstp->rq_resp;
+ struct kstat *statp = &resp->stat;
+ unsigned long count = argp->xdrgen.data.len;
+ struct svc_fh *fhp = &argp->fh;
- dprintk("nfsd: WRITE %s %u bytes at %d\n",
- SVCFH_fmt(&argp->fh),
- argp->len, argp->offset);
+ nfsd_fhandle_to_svc_fh(fhp, &argp->xdrgen.file);
- fh_copy(&resp->fh, &argp->fh);
- resp->status = nfsd_write(rqstp, &resp->fh, argp->offset,
- &argp->payload, &cnt, IOCB_DSYNC, NULL);
- if (resp->status == nfs_ok)
- resp->status = fh_getattr(&resp->fh, &resp->stat);
- else if (resp->status == nfserr_jukebox)
+ resp->xdrgen.status = nfsd_write(rqstp, fhp, argp->xdrgen.offset,
+ &argp->xdrgen.data, &count,
+ IOCB_DSYNC, NULL);
+ if (resp->xdrgen.status == nfs_ok) {
+ resp->xdrgen.status = fh_getattr(fhp, statp);
+ if (resp->xdrgen.status == nfs_ok)
+ nfsd_stat_to_fattr(rqstp, &resp->xdrgen.u.attributes,
+ statp, fhp);
+ }
+ if (resp->xdrgen.status == nfserr_jukebox)
set_bit(RQ_DROPME, &rqstp->rq_flags);
- resp->status = nfsd_map_io_status(resp->status);
+ if (resp->xdrgen.status != nfs_ok)
+ resp->xdrgen.status = nfsd_map_io_status(resp->xdrgen.status);
+
+ fh_put(fhp);
return rpc_success;
}
@@ -1076,16 +1095,15 @@ static const struct svc_procedure nfsd_procedures2[18] = {
.pc_name = "WRITECACHE",
},
[NFSPROC_WRITE] = {
- .pc_func = nfsd_proc_write,
- .pc_decode = nfssvc_decode_writeargs,
- .pc_encode = nfssvc_encode_attrstatres,
- .pc_release = nfssvc_release_attrstat,
- .pc_argsize = sizeof(struct nfsd_writeargs),
- .pc_argzero = sizeof(struct nfsd_writeargs),
- .pc_ressize = sizeof(struct nfsd_attrstat),
- .pc_cachetype = RC_REPLBUFF,
- .pc_xdrressize = ST+AT,
- .pc_name = "WRITE",
+ .pc_func = nfsd_proc_write,
+ .pc_decode = nfs_svc_decode_writeargs,
+ .pc_encode = nfs_svc_encode_attrstat,
+ .pc_argsize = sizeof(struct writeargs_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct attrstat_wrapper),
+ .pc_cachetype = RC_REPLBUFF,
+ .pc_xdrressize = NFS2_attrstat_sz,
+ .pc_name = "WRITE",
},
[NFSPROC_CREATE] = {
.pc_func = nfsd_proc_create,
@@ -1197,7 +1215,7 @@ union nfsd_xdrstore {
struct sattrargs_wrapper sattrargs;
struct diropargs_wrapper diropargs;
struct readargs_wrapper readargs;
- struct nfsd_writeargs write;
+ struct writeargs_wrapper writeargs;
struct nfsd_createargs create;
struct nfsd_renameargs rename;
struct nfsd_linkargs link;
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index 1b79f9534af6..33b3fb73053e 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -311,32 +311,6 @@ nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd_writeargs *args = rqstp->rq_argp;
- u32 beginoffset, totalcount;
-
- if (!svcxdr_decode_fhandle(xdr, &args->fh))
- return false;
- /* beginoffset is ignored */
- if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
- return false;
- /* totalcount is ignored */
- if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
- return false;
-
- /* opaque data */
- if (xdr_stream_decode_u32(xdr, &args->len) < 0)
- return false;
- if (args->len > NFS_MAXDATA)
- return false;
-
- return xdr_stream_subsegment(xdr, &args->payload, args->len);
-}
-
bool
nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
diff --git a/fs/nfsd/xdr.h b/fs/nfsd/xdr.h
index 9d9150760a0b..75f40bef6614 100644
--- a/fs/nfsd/xdr.h
+++ b/fs/nfsd/xdr.h
@@ -14,13 +14,6 @@ struct nfsd_diropargs {
unsigned int len;
};
-struct nfsd_writeargs {
- svc_fh fh;
- __u32 offset;
- __u32 len;
- struct xdr_buf payload;
-};
-
struct nfsd_createargs {
struct svc_fh fh;
char * name;
@@ -96,7 +89,6 @@ struct nfsd_statfsres {
bool nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/include/linux/sunrpc/xdrgen/nfs2.h b/include/linux/sunrpc/xdrgen/nfs2.h
index eeb8b9ded65f..7a469ac4b772 100644
--- a/include/linux/sunrpc/xdrgen/nfs2.h
+++ b/include/linux/sunrpc/xdrgen/nfs2.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs2.x */
-/* XDR specification modification time: Tue Sep 8 11:25:45 2026 */
+/* XDR specification modification time: Tue Sep 8 11:26:00 2026 */
#ifndef _LINUX_XDRGEN_NFS2_DEF_H
#define _LINUX_XDRGEN_NFS2_DEF_H
@@ -152,7 +152,7 @@ struct writeargs {
u32 beginoffset;
u32 offset;
u32 totalcount;
- opaque data;
+ struct xdr_buf data;
};
struct createargs {
--
2.55.0
next prev parent reply other threads:[~2026-09-16 16:29 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-16 16:28 [PATCH v1 00/27] Convert server-side NFSv2 XDR to use xdrgen Chuck Lever
2026-09-16 16:28 ` [PATCH v1 01/27] Documentation: Add the RPC language description of NFSv2 Chuck Lever
2026-09-16 16:28 ` [PATCH v1 1/2] NFSD: map fh_verify() status codes for NFS_ACLv2 replies Chuck Lever
2026-09-17 11:57 ` Jeff Layton
2026-09-16 16:28 ` [PATCH v1 02/27] NFSD: Add infrastructure for generating NFSv2 XDR encoders and decoders Chuck Lever
2026-09-16 16:28 ` [PATCH v1 2/2] NFSD: map fh_verify() status codes for NFS_ACLv3 replies Chuck Lever
2026-09-17 11:57 ` Jeff Layton
2026-09-16 16:28 ` [PATCH v1 03/27] NFSD: Use xdrgen-generated NFSv2 protocol definitions Chuck Lever
2026-09-16 16:28 ` [PATCH v1 04/27] NFSD: Remove '#include "xdr.h"' from fs/nfsd/xdr3.h Chuck Lever
2026-09-16 16:28 ` [PATCH v1 05/27] NFSD: Relocate the NFSv2 XDR storage union into nfsproc.c Chuck Lever
2026-09-16 16:28 ` [PATCH v1 06/27] NFSD: Use xdrgen XDR functions for the NFSv2 NULL procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 07/27] NFSD: Use xdrgen XDR functions for NFSv2 GETATTR procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 08/27] NFSD: Use xdrgen XDR functions for NFSv2 SETATTR procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 09/27] NFSD: Use xdrgen XDR functions for the NFSv2 ROOT procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 10/27] NFSD: Use xdrgen XDR functions for the NFSv2 LOOKUP procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 11/27] NFSD: Use xdrgen XDR functions for NFSv2 READLINK procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 12/27] NFSD: Use xdrgen XDR functions for NFSv2 READ procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 13/27] NFSD: Use xdrgen XDR functions for the NFSv2 WRITECACHE procedure Chuck Lever
2026-09-16 16:28 ` Chuck Lever [this message]
2026-09-16 16:28 ` [PATCH v1 15/27] NFSD: Refactor nfsd_proc_create() Chuck Lever
2026-09-16 16:28 ` [PATCH v1 16/27] NFSD: Use xdrgen XDR functions for NFSv2 CREATE procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 17/27] NFSD: Use xdrgen XDR functions for the NFSv2 REMOVE procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 18/27] NFSD: Use xdrgen XDR functions for the NFSv2 RENAME procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 19/27] NFSD: Use xdrgen XDR functions for the NFSv2 LINK procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 20/27] NFSD: Use xdrgen XDR functions for NFSv2 SYMLINK procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 21/27] NFSD: Use xdrgen XDR functions for NFSv2 MKDIR procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 22/27] NFSD: Use xdrgen XDR functions for NFSv2 RMDIR procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 23/27] NFSD: Use xdrgen XDR functions for the NFSv2 STATFS procedure Chuck Lever
2026-09-16 16:28 ` [PATCH v1 24/27] NFSD: Use xdrgen XDR functions for NFSv2 READDIR arguments Chuck Lever
2026-09-16 16:28 ` [PATCH v1 25/27] NFSD: Add a streaming directory reader Chuck Lever
2026-09-16 16:28 ` [PATCH v1 26/27] NFSD: Refactor NFSv2 directory cookie encoding Chuck Lever
2026-09-16 16:28 ` [PATCH v1 27/27] NFSD: Use xdrgen XDR functions for NFSv2 READDIR results Chuck Lever
2026-09-17 11:56 ` [PATCH v1 00/27] Convert server-side NFSv2 XDR to use xdrgen Jeff Layton
2026-09-17 14:57 ` Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260916162859.2051-17-cel@kernel.org \
--to=cel@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=dai.ngo@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.