[PATCH 2/6] NFSD: Apply per-export I/O modes to READ and WRITE
From: Jeff Layton
Date: Tue Oct 27 2026 - 12:48:25 EST
Resolve each export's effective I/O mode (explicit setting, else the
global io_cache_read/write knob) in nfsd_iter_read and nfsd_vfs_write,
and skip splice for reads on exports whose effective read mode is not
buffered. Update the debugfs knob comments to match.
Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
fs/nfsd/debugfs.c | 6 ++++--
fs/nfsd/nfs4xdr.c | 6 ++++--
fs/nfsd/vfs.c | 39 ++++++++++++++++++++++++++++++++++++---
fs/nfsd/vfs.h | 2 ++
4 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/fs/nfsd/debugfs.c b/fs/nfsd/debugfs.c
index 4af1e6b8b212..a10159db3b60 100644
--- a/fs/nfsd/debugfs.c
+++ b/fs/nfsd/debugfs.c
@@ -47,7 +47,8 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_dsr_fops, nfsd_dsr_get, nfsd_dsr_set, "%llu\n");
* %2: NFS READ will use direct IO
*
* This setting takes immediate effect for all NFS versions,
- * all exports, and in all NFSD net namespaces.
+ * all exports without an explicit riomode= setting, and in all
+ * NFSD net namespaces.
*/
static int nfsd_io_cache_read_get(void *data, u64 *val)
@@ -92,7 +93,8 @@ DEFINE_DEBUGFS_ATTRIBUTE(nfsd_io_cache_read_fops, nfsd_io_cache_read_get,
* %1: NFS WRITE will use dontcache (buffered IO w/ dropbehind)
*
* This setting takes immediate effect for all NFS versions,
- * all exports, and in all NFSD net namespaces.
+ * all exports without an explicit wiomode= setting, and in all
+ * NFSD net namespaces.
*/
static int nfsd_io_cache_write_get(void *data, u64 *val)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 4d983444604a..9215feeeb0ba 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -5366,7 +5366,8 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
maxcount = min_t(unsigned long, read->rd_length,
(xdr->buf->buflen - xdr->buf->len));
- if (file->f_op->splice_read && splice_ok)
+ if (file->f_op->splice_read && splice_ok &&
+ nfsd_read_iomode(read->rd_fhp->fh_export) == NFSD_IO_BUFFERED)
nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
else
nfserr = nfsd4_encode_readv(resp, read, maxcount);
@@ -6264,7 +6265,8 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
maxcount = min_t(unsigned long, read->rd_length,
(xdr->buf->buflen - xdr->buf->len));
- if (file->f_op->splice_read && splice_ok)
+ if (file->f_op->splice_read && splice_ok &&
+ nfsd_read_iomode(read->rd_fhp->fh_export) == NFSD_IO_BUFFERED)
nfserr = nfsd4_encode_splice_read(resp, read, file, maxcount);
else
nfserr = nfsd4_encode_readv(resp, read, maxcount);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 7f9ec6c4af6d..15613b6df290 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1266,6 +1266,38 @@ static bool nfsd_direct_read_ok(struct svc_rqst *rqstp, struct svc_fh *fhp,
return true;
}
+/**
+ * nfsd_read_iomode - effective NFS READ I/O mode for an export
+ * @exp: export being accessed
+ *
+ * An export without an explicit riomode= setting follows the global
+ * nfsd_io_cache_read knob.
+ *
+ * Returns one of NFSD_IO_BUFFERED, NFSD_IO_DONTCACHE, or NFSD_IO_DIRECT.
+ */
+int nfsd_read_iomode(struct svc_export *exp)
+{
+ if (exp->ex_riomode == NFSD_IO_DEFAULT)
+ return nfsd_io_cache_read;
+ return exp->ex_riomode;
+}
+
+/**
+ * nfsd_write_iomode - effective NFS WRITE I/O mode for an export
+ * @exp: export being accessed
+ *
+ * An export without an explicit wiomode= setting follows the global
+ * nfsd_io_cache_write knob.
+ *
+ * Returns one of NFSD_IO_BUFFERED, NFSD_IO_DONTCACHE, or NFSD_IO_DIRECT.
+ */
+int nfsd_write_iomode(struct svc_export *exp)
+{
+ if (exp->ex_wiomode == NFSD_IO_DEFAULT)
+ return nfsd_io_cache_write;
+ return exp->ex_wiomode;
+}
+
/**
* nfsd_iter_read - Perform a VFS read using an iterator
* @rqstp: RPC transaction context
@@ -1300,7 +1332,7 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
init_sync_kiocb(&kiocb, file);
- switch (nfsd_io_cache_read) {
+ switch (nfsd_read_iomode(fhp->fh_export)) {
case NFSD_IO_BUFFERED:
break;
case NFSD_IO_DIRECT:
@@ -1746,7 +1778,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (verf)
nfsd_copy_write_verifier(verf, nn);
- switch (nfsd_io_cache_write) {
+ switch (nfsd_write_iomode(exp)) {
case NFSD_IO_DIRECT:
host_err = nfsd_direct_write(rqstp, fhp, nf, nvecs,
cnt, &kiocb);
@@ -1853,7 +1885,8 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
return err;
file = nf->nf_file;
- if (file->f_op->splice_read && nfsd_read_splice_ok(rqstp))
+ if (file->f_op->splice_read && nfsd_read_splice_ok(rqstp) &&
+ nfsd_read_iomode(fhp->fh_export) == NFSD_IO_BUFFERED)
err = nfsd_splice_read(rqstp, fhp, file, offset, count, eof);
else
err = nfsd_iter_read(rqstp, fhp, nf, offset, count, eof);
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 1f05c83a3ea6..e95611b79397 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -184,6 +184,8 @@ __be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file *nf, loff_t offset,
unsigned long *count, u32 *eof);
bool nfsd_read_splice_ok(struct svc_rqst *rqstp);
+int nfsd_read_iomode(struct svc_export *exp);
+int nfsd_write_iomode(struct svc_export *exp);
__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t offset, unsigned long *count,
u32 *eof);
--
2.56.0