From: Jeff Layton <jlayton@kernel.org>
To: Steve Dickson <steved@redhat.com>, Chuck Lever <cel@kernel.org>,
NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
Trond Myklebust <trondmy@kernel.org>,
Anna Schumaker <anna@kernel.org>,
Mike Snitzer <snitzer@kernel.org>
Cc: linux-nfs@vger.kernel.org, Jeff Layton <jlayton@kernel.org>
Subject: [PATCH nfs-utils 2/4] exports: Add riomode= and wiomode= export options
Date: Tue, 27 Oct 2026 12:50:24 -0400 [thread overview]
Message-ID: <20261027-master-v1-2-6f1f7a35a83b@kernel.org> (raw)
In-Reply-To: <20261027-master-v1-0-6f1f7a35a83b@kernel.org>
Parse riomode=/wiomode= (buffered/dontcache/direct) into new
e_riomode/e_wiomode exportent fields, defaulting to unset, and pass
explicitly-set values to the kernel over the netlink svc_export
interface.
- Kernels without the iomode attributes (e.g. v7.2) reject the whole
entry. Add nfsd_export_iomode_supported(), which checks the
svc_export policy's highest attribute via nfsd_export_policy_query(),
and have mountd drop the options with a warning on such kernels
instead of leaving the export unmountable.
- The /proc channel cannot carry them either; warn there too.
- Resync nfsd_netlink.h and bump the configure probe to
NFSD_A_SVC_EXPORT_WRITE_IOMODE.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
configure.ac | 2 +-
support/export/cache.c | 59 +++++++++++++++++++++++++++++++++++++++++-
support/export/export_policy.c | 22 ++++++++++++++++
support/export/v4root.c | 2 ++
support/include/exportfs.h | 1 +
support/include/nfsd_netlink.h | 12 +++++++++
support/include/nfslib.h | 13 ++++++++++
support/nfs/exports.c | 51 ++++++++++++++++++++++++++++++++++++
8 files changed, 160 insertions(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac
index 2c13292961bf..ff752b8bd17f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -283,7 +283,7 @@ AC_CHECK_HEADERS(linux/nfsd_netlink.h)
# ensure the system netlink headers have the latest features
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]],
- [[int foo = NFSD_CMD_FILEHANDLE_GET;]])],
+ [[int foo = NFSD_A_SVC_EXPORT_WRITE_IOMODE;]])],
[AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1,
["Use system's linux/nfsd_netlink.h"])])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/lockd_netlink.h>]],
diff --git a/support/export/cache.c b/support/export/cache.c
index 3c7ee9f90ac1..86b8da135837 100644
--- a/support/export/cache.c
+++ b/support/export/cache.c
@@ -1287,6 +1287,9 @@ static struct nl_sock *nfsd_nl_notify_sock; /* multicast notifications */
static struct nl_sock *nfsd_nl_cmd_sock; /* GET_REQS / SET_REQS commands */
static int nfsd_nl_family;
+/* Does the running kernel accept riomode/wiomode in SET_REQS? */
+static bool nfsd_nl_iomode_ok;
+
#define NL_BUFFER_SIZE 65536
struct export_req {
@@ -1380,8 +1383,10 @@ static int cache_nfsd_nl_open(void)
ret = cache_genl_open(NFSD_FAMILY_NAME, NFSD_MCGRP_EXPORTD,
&nfsd_nl_cmd_sock, &nfsd_nl_notify_sock,
&nfsd_nl_family);
- if (ret == 0)
+ if (ret == 0) {
nfsd_nl_load_export_features();
+ nfsd_nl_iomode_ok = nfsd_export_iomode_supported() > 0;
+ }
return ret;
}
@@ -1648,6 +1653,40 @@ static int nfsd_nl_add_xprtsec(struct nl_msg *msg, struct exportent *ep)
return 0;
}
+static int nfsd_nl_add_iomode(struct nl_msg *msg, struct exportent *ep)
+{
+ static bool warned;
+
+ if (ep->e_riomode == NFSD_IO_UNSET && ep->e_wiomode == NFSD_IO_UNSET)
+ return 0;
+
+ /*
+ * A kernel without per-export I/O modes would refuse the entry,
+ * leaving the export unmountable. Export it with the server's
+ * global I/O modes instead.
+ */
+ if (!nfsd_nl_iomode_ok) {
+ if (!warned) {
+ xlog(L_WARNING, "%s: kernel does not support riomode= "
+ "or wiomode=, ignoring", ep->e_path);
+ warned = true;
+ }
+ return 0;
+ }
+
+ if (ep->e_riomode != NFSD_IO_UNSET &&
+ nla_put_u32(msg, NFSD_A_SVC_EXPORT_READ_IOMODE,
+ ep->e_riomode) < 0)
+ return -1;
+
+ if (ep->e_wiomode != NFSD_IO_UNSET &&
+ nla_put_u32(msg, NFSD_A_SVC_EXPORT_WRITE_IOMODE,
+ ep->e_wiomode) < 0)
+ return -1;
+
+ return 0;
+}
+
/*
* Add one svc_export response. @ea must be the attributes computed by
* export_attrs_build() for (@path, @exp), and is ignored when @exp is NULL.
@@ -1704,6 +1743,9 @@ static int nfsd_nl_add_export(struct nl_msg *msg, char *domain, char *path,
if (nfsd_nl_add_xprtsec(msg, exp))
goto nla_failure;
+
+ if (nfsd_nl_add_iomode(msg, exp))
+ goto nla_failure;
}
nla_nest_end(msg, nest);
@@ -3261,6 +3303,20 @@ static void cache_sunrpc_nl_process(void)
cache_nl_process_unix_gid();
}
+/* The kernel takes per-export I/O modes only over netlink */
+static void warn_iomode_unsupported(struct exportent *exp)
+{
+ static bool warned;
+
+ if (warned || (exp->e_riomode == NFSD_IO_UNSET &&
+ exp->e_wiomode == NFSD_IO_UNSET))
+ return;
+
+ xlog(L_WARNING, "%s: riomode= and wiomode= need the netlink "
+ "export interface, ignoring", exp->e_path);
+ warned = true;
+}
+
static int dump_to_cache(int f, char *buf, int blen, char *domain,
char *path, struct exportent *exp, int ttl)
{
@@ -3293,6 +3349,7 @@ static int dump_to_cache(int f, char *buf, int blen, char *domain,
qword_addhex(&bp, &blen, ea.uuid, 16);
}
write_xprtsec(&bp, &blen, exp);
+ warn_iomode_unsupported(exp);
xlog(D_AUTH, "granted access to %s for %s",
path, *domain == '$' ? domain+1 : domain);
} else {
diff --git a/support/export/export_policy.c b/support/export/export_policy.c
index a0170cb87f91..797a8459e3c0 100644
--- a/support/export/export_policy.c
+++ b/support/export/export_policy.c
@@ -244,6 +244,28 @@ int nfsd_export_policy_query(struct nfsd_export_policy *UNUSED(pol))
#endif /* HAVE_GENL_POLICY_DUMP */
+/**
+ * nfsd_export_iomode_supported - does the kernel take per-export I/O modes?
+ *
+ * A kernel without them refuses any svc_export entry that carries one.
+ *
+ * Returns 1 if it accepts riomode/wiomode, 0 if not or if that cannot be
+ * found out, or -1 if the nfsd netlink family is not registered, as when
+ * nfsd is not loaded.
+ */
+int nfsd_export_iomode_supported(void)
+{
+ struct nfsd_export_policy pol;
+ int ret;
+
+ ret = nfsd_export_policy_query(&pol);
+ if (ret == -ENOENT)
+ return -1;
+ if (ret < 0)
+ return 0;
+ return pol.max_attr >= NFSD_A_SVC_EXPORT_WRITE_IOMODE;
+}
+
/**
* nfsd_nl_load_export_features - learn the kernel's export flags over netlink
*
diff --git a/support/export/v4root.c b/support/export/v4root.c
index 38286b91ede0..4075b9ef2fef 100644
--- a/support/export/v4root.c
+++ b/support/export/v4root.c
@@ -47,6 +47,8 @@ static nfs_export pseudo_root = {
.e_fsid = 0,
.e_mountpoint = NULL,
.e_ttl = 0,
+ .e_riomode = NFSD_IO_UNSET,
+ .e_wiomode = NFSD_IO_UNSET,
},
.m_exported = 0,
.m_xtabent = 1,
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index a7c145543075..9096db37a6ee 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -177,6 +177,7 @@ struct nfsd_export_policy {
struct nfsd_export_policy *get_export_features(void);
void set_export_features(const struct nfsd_export_policy *pol);
int nfsd_export_policy_query(struct nfsd_export_policy *pol);
+int nfsd_export_iomode_supported(void);
void nfsd_nl_load_export_features(void);
void fix_pseudoflavor_flags(struct exportent *ep);
diff --git a/support/include/nfsd_netlink.h b/support/include/nfsd_netlink.h
index dcd2126d848b..6628ed7c0922 100644
--- a/support/include/nfsd_netlink.h
+++ b/support/include/nfsd_netlink.h
@@ -59,6 +59,16 @@ enum nfsd_rpcbind_flags {
NFSD_RPCBIND_FLAGS_NO_UDP = 1,
};
+/*
+ * NFS READ/WRITE I/O mode. These values match the global io_cache_read and
+ * io_cache_write debugfs settings.
+ */
+enum nfsd_io_mode {
+ NFSD_IO_MODE_BUFFERED,
+ NFSD_IO_MODE_DONTCACHE,
+ NFSD_IO_MODE_DIRECT,
+};
+
enum {
NFSD_A_CACHE_NOTIFY_CACHE_TYPE = 1,
@@ -185,6 +195,8 @@ enum {
NFSD_A_SVC_EXPORT_XPRTSEC,
NFSD_A_SVC_EXPORT_FLAGS,
NFSD_A_SVC_EXPORT_FSID,
+ NFSD_A_SVC_EXPORT_READ_IOMODE,
+ NFSD_A_SVC_EXPORT_WRITE_IOMODE,
__NFSD_A_SVC_EXPORT_MAX,
NFSD_A_SVC_EXPORT_MAX = (__NFSD_A_SVC_EXPORT_MAX - 1)
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index c8601a156cba..f2be65b55f8c 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -75,6 +75,16 @@ struct xprtsec_entry {
int flags;
};
+/*
+ * Per-export I/O modes for riomode=/wiomode=. Values must match the
+ * kernel's NFSD_IO_* enum (fs/nfsd/nfsd.h). NFSD_IO_UNSET means no
+ * option was given; the export follows the server's global setting.
+ */
+#define NFSD_IO_BUFFERED 0
+#define NFSD_IO_DONTCACHE 1
+#define NFSD_IO_DIRECT 2
+#define NFSD_IO_UNSET (-1)
+
/*
* Data related to a single exports entry as returned by getexportent.
* FIXME: export options should probably be parsed at a later time to
@@ -100,6 +110,8 @@ struct exportent {
unsigned int e_ttl;
char * e_realpath;
int e_reexport;
+ int e_riomode;
+ int e_wiomode;
};
struct rmtabent {
@@ -115,6 +127,7 @@ void setexportent(char *fname, char *type);
struct exportent * getexportent(int);
void secinfo_show(FILE *fp, struct exportent *ep);
void xprtsecinfo_show(FILE *fp, struct exportent *ep);
+const char * iomode_name(int mode);
void putexportent(struct exportent *xep);
void endexportent(void);
struct exportent * mkexportent(char *hname, char *path, char *opts);
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index 5ccc4ca0582f..94fa50ecefa3 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -107,6 +107,8 @@ static void init_exportent (struct exportent *ee, int fromkernel)
ee->e_uuid = NULL;
ee->e_ttl = default_ttl;
ee->e_reexport = REEXP_NONE;
+ ee->e_riomode = NFSD_IO_UNSET;
+ ee->e_wiomode = NFSD_IO_UNSET;
}
struct exportent *
@@ -313,6 +315,10 @@ putexportent(struct exportent *ep)
if (ep->e_flags & NFSEXP_SIGN_FH)
fprintf(fp, "sign_fh,");
fprintf(fp, "%spnfs,", (ep->e_flags & NFSEXP_PNFS)? "" : "no_");
+ if (ep->e_riomode != NFSD_IO_UNSET)
+ fprintf(fp, "riomode=%s,", iomode_name(ep->e_riomode));
+ if (ep->e_wiomode != NFSD_IO_UNSET)
+ fprintf(fp, "wiomode=%s,", iomode_name(ep->e_wiomode));
if (ep->e_flags & NFSEXP_FSID) {
fprintf(fp, "fsid=%d,", ep->e_fsid);
}
@@ -586,6 +592,45 @@ static unsigned int parse_xprtsec(char *str, struct exportent *ep)
return out;
}
+/*
+ * Parse the value of a riomode= or wiomode= option into @mode.
+ *
+ * A zero return indicates an error.
+ */
+static int parse_iomode(char *str, int *mode)
+{
+ char *flname = efname ? efname : "command line";
+ int flline = efp ? efp->x_line : 0;
+
+ if (!strcmp(str, "buffered"))
+ *mode = NFSD_IO_BUFFERED;
+ else if (!strcmp(str, "dontcache"))
+ *mode = NFSD_IO_DONTCACHE;
+ else if (!strcmp(str, "direct"))
+ *mode = NFSD_IO_DIRECT;
+ else {
+ xlog(L_ERROR, "%s:%d: bad iomode \"%s\"\n",
+ flname, flline, str);
+ return 0;
+ }
+ return 1;
+}
+
+const char *
+iomode_name(int mode)
+{
+ switch (mode) {
+ case NFSD_IO_BUFFERED:
+ return "buffered";
+ case NFSD_IO_DONTCACHE:
+ return "dontcache";
+ case NFSD_IO_DIRECT:
+ return "direct";
+ default:
+ return "unknown";
+ }
+}
+
/* Sets the bits in @mask for the appropriate security flavor flags. */
static void setflags(int mask, unsigned int active, struct exportent *ep)
{
@@ -804,6 +849,12 @@ bad_option:
} else if (strncmp(opt, "xprtsec=", 8) == 0) {
if (!parse_xprtsec(opt + 8, ep))
goto bad_option;
+ } else if (strncmp(opt, "riomode=", 8) == 0) {
+ if (!parse_iomode(opt + 8, &ep->e_riomode))
+ goto bad_option;
+ } else if (strncmp(opt, "wiomode=", 8) == 0) {
+ if (!parse_iomode(opt + 8, &ep->e_wiomode))
+ goto bad_option;
} else if (strncmp(opt, "reexport=", 9) == 0) {
char *strategy = strchr(opt, '=');
--
2.56.0
next prev parent reply other threads:[~2026-10-27 16:50 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 16:50 [PATCH nfs-utils 0/4] exports: add support for new riomode= and wiomode= export options Jeff Layton
2026-10-27 16:50 ` [PATCH nfs-utils 1/4] exportfs/mountd/exportd: track the svc_export policy highest attribute Jeff Layton
2026-10-27 16:50 ` Jeff Layton [this message]
2026-10-27 16:50 ` [PATCH nfs-utils 3/4] exportfs: Display and document riomode= and wiomode= Jeff Layton
2026-10-27 16:50 ` [PATCH nfs-utils 4/4] nfsd-iomode-advisor: recommend riomode= and wiomode= for an export Jeff Layton
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=20261027-master-v1-2-6f1f7a35a83b@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=anna@kernel.org \
--cc=cel@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=snitzer@kernel.org \
--cc=steved@redhat.com \
--cc=tom@talpey.com \
--cc=trondmy@kernel.org \
/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.