From: Mike Snitzer <snitzer@hammerspace.com>
To: Chuck Lever <cel@kernel.org>, Jeff Layton <jlayton@kernel.org>
Cc: linux-nfs@vger.kernel.org
Subject: [PATCH 01/10] NFSD: interlock the use of NFSD_IO_DIRECT for NFS READ and WRITE
Date: Tue, 29 Sep 2026 13:34:14 -0400 [thread overview]
Message-ID: <20260929173423.16149-2-snitzer@kernel.org> (raw)
In-Reply-To: <20260929173423.16149-1-snitzer@kernel.org>
Now that NFSD supports NFSD_IO_DIRECT for both READ and WRITE it is
much safer to avoid needless buffered vs direct contention if/when
only one of them has been configured to use NFSD_IO_DIRECT.
Mixing direct and buffered I/O to the same file causes needless page
cache invalidation and writeback, so although io_cache_read and
io_cache_write remain separate interfaces, writing either one adjusts
the other so that READ and WRITE are never left on opposite sides of
the buffered/direct divide:
- Setting io_cache_read to NFSD_IO_DIRECT elevates a BUFFERED or
DONTCACHE io_cache_write to NFSD_IO_DIRECT. A WRITE mode that is
already direct is left as it is.
- Setting io_cache_write to any direct mode elevates a BUFFERED or
DONTCACHE io_cache_read to NFSD_IO_DIRECT.
- Setting io_cache_read to NFSD_IO_BUFFERED or NFSD_IO_DONTCACHE
demotes a direct io_cache_write to that same mode.
- Setting io_cache_write to NFSD_IO_BUFFERED or NFSD_IO_DONTCACHE
demotes a direct io_cache_read to that same mode.
- Enabling splice for READ, by writing 0 to disable-splice-read,
forces io_cache_read to NFSD_IO_BUFFERED and demotes a direct
io_cache_write along with it.
The demotion is factored into nfsd_io_cache_write_demote() so that its
three callers stay in sync.
Both DONTCACHE and DIRECT READ must copy into the RPC reply buffer, so
splice for READ is disabled whenever either interface leaves
nfsd_io_cache_read above NFSD_IO_BUFFERED, regardless of which one was
written.
Document the complete interlock in
Documentation/filesystems/nfs/nfsd-io-modes.rst.
Fixes: 06c5c97293e3 ("NFSD: Implement NFSD_IO_DIRECT for NFS WRITE")
Assisted-by: Claude:claude-opus-5[1m]
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
.../filesystems/nfs/nfsd-io-modes.rst | 35 ++++++++++++
fs/nfsd/debugfs.c | 54 ++++++++++++++++---
2 files changed, 83 insertions(+), 6 deletions(-)
diff --git a/Documentation/filesystems/nfs/nfsd-io-modes.rst b/Documentation/filesystems/nfs/nfsd-io-modes.rst
index 0fd6e82478fe6..a8e277bcb08b1 100644
--- a/Documentation/filesystems/nfs/nfsd-io-modes.rst
+++ b/Documentation/filesystems/nfs/nfsd-io-modes.rst
@@ -41,6 +41,41 @@ corresponding IO operation's debugfs interface, e.g.::
If you experiment with NFSD's IO modes on a recent kernel and have
interesting results, please report them to linux-nfs@vger.kernel.org
+READ and WRITE IO mode interlock
+================================
+
+Although io_cache_read and io_cache_write are separate interfaces, NFSD
+keeps them from being configured such that one of READ or WRITE uses
+DIRECT IO while the other uses the page cache. Mixing DIRECT and
+buffered IO to the same file causes needless page cache invalidation and
+writeback (see the DIRECT IO discussion in the Linux open(2) manpage),
+so writing one interface may adjust the other:
+
+- Setting io_cache_read to NFSD_IO_DIRECT (2) elevates io_cache_write
+ to NFSD_IO_DIRECT (2) if it was BUFFERED or DONTCACHE. A WRITE mode
+ that is already DIRECT (2, 3 or 4) is left unchanged.
+- Setting io_cache_write to any DIRECT mode (2, 3 or 4) elevates
+ io_cache_read to NFSD_IO_DIRECT (2) if it was BUFFERED or DONTCACHE.
+- Setting io_cache_read to NFSD_IO_BUFFERED (0) or NFSD_IO_DONTCACHE (1)
+ while io_cache_write is a DIRECT mode demotes io_cache_write to that
+ same value (0 or 1). A WRITE mode that is already BUFFERED or
+ DONTCACHE is left unchanged.
+- Setting io_cache_write to NFSD_IO_BUFFERED (0) or NFSD_IO_DONTCACHE
+ (1) while io_cache_read is NFSD_IO_DIRECT demotes io_cache_read to
+ that same value (0 or 1).
+
+Setting either interface to a value other than NFSD_IO_BUFFERED also
+disables NFSD's use of splice for READ, because both DONTCACHE and
+DIRECT READ must copy into the RPC reply buffer. This is reflected in
+/sys/kernel/debug/nfsd/disable-splice-read reading as 1. Writing 0 to
+disable-splice-read re-enables splice, which requires buffered READ, so
+it forces io_cache_read back to NFSD_IO_BUFFERED (0) and, if
+io_cache_write was a DIRECT mode, demotes it to NFSD_IO_BUFFERED (0) as
+well.
+
+Always read both interfaces back after writing either of them to
+confirm the resulting configuration.
+
NFSD DONTCACHE
==============
diff --git a/fs/nfsd/debugfs.c b/fs/nfsd/debugfs.c
index 386fd1c54f527..995872a1a7f87 100644
--- a/fs/nfsd/debugfs.c
+++ b/fs/nfsd/debugfs.c
@@ -24,6 +24,17 @@ static int nfsd_dsr_get(void *data, u64 *val)
return 0;
}
+/*
+ * NFS READ is no longer using direct I/O: demote NFS WRITE from direct
+ * I/O to the same buffered mode, to avoid needless buffered vs direct
+ * contention.
+ */
+static void nfsd_io_cache_write_demote(u64 io_mode)
+{
+ if (nfsd_io_cache_write >= NFSD_IO_DIRECT)
+ nfsd_io_cache_write = io_mode;
+}
+
static int nfsd_dsr_set(void *data, u64 val)
{
nfsd_disable_splice_read = (val > 0);
@@ -32,6 +43,7 @@ static int nfsd_dsr_set(void *data, u64 val)
* Must use buffered I/O if splice_read is enabled.
*/
nfsd_io_cache_read = NFSD_IO_BUFFERED;
+ nfsd_io_cache_write_demote(NFSD_IO_BUFFERED);
}
return 0;
}
@@ -62,22 +74,33 @@ static int nfsd_io_cache_read_set(void *data, u64 val)
switch (val) {
case NFSD_IO_BUFFERED:
- nfsd_io_cache_read = NFSD_IO_BUFFERED;
- break;
case NFSD_IO_DONTCACHE:
+ nfsd_io_cache_read = val;
+ nfsd_io_cache_write_demote(val);
+ break;
case NFSD_IO_DIRECT:
+ nfsd_io_cache_read = val;
/*
- * Must disable splice_read when enabling
- * NFSD_IO_DONTCACHE.
+ * Elevate nfsd_io_cache_write if not already
+ * configured to use NFSD_IO_DIRECT.
*/
- nfsd_disable_splice_read = true;
- nfsd_io_cache_read = val;
+ if (nfsd_io_cache_write < NFSD_IO_DIRECT)
+ nfsd_io_cache_write = NFSD_IO_DIRECT;
break;
default:
ret = -EINVAL;
break;
}
+ if (ret == 0) {
+ /*
+ * Must disable splice_read when enabling
+ * NFSD_IO_DONTCACHE and NFSD_IO_DIRECT.
+ */
+ if (nfsd_io_cache_read > NFSD_IO_BUFFERED)
+ nfsd_disable_splice_read = true;
+ }
+
return ret;
}
@@ -110,12 +133,31 @@ static int nfsd_io_cache_write_set(void *data, u64 val)
case NFSD_IO_DONTCACHE:
case NFSD_IO_DIRECT:
nfsd_io_cache_write = val;
+ /*
+ * Adjust nfsd_io_cache_{read,write} to avoid
+ * needless buffered vs direct contention.
+ */
+ if (nfsd_io_cache_write >= NFSD_IO_DIRECT &&
+ nfsd_io_cache_read < NFSD_IO_DIRECT)
+ nfsd_io_cache_read = NFSD_IO_DIRECT;
+ else if (nfsd_io_cache_write < NFSD_IO_DIRECT &&
+ nfsd_io_cache_read == NFSD_IO_DIRECT)
+ nfsd_io_cache_read = nfsd_io_cache_write;
break;
default:
ret = -EINVAL;
break;
}
+ if (ret == 0) {
+ /*
+ * Must disable splice_read when enabling
+ * NFSD_IO_DONTCACHE and NFSD_IO_DIRECT.
+ */
+ if (nfsd_io_cache_read > NFSD_IO_BUFFERED)
+ nfsd_disable_splice_read = true;
+ }
+
return ret;
}
--
2.52.0
next prev parent reply other threads:[~2026-09-29 17:34 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 17:34 [PATCH 00/10] NFSD: keep direct-mode I/O out of the page cache and elide COMMITs Mike Snitzer
2026-09-29 17:34 ` Mike Snitzer [this message]
2026-09-29 18:27 ` [PATCH 01/10] NFSD: interlock the use of NFSD_IO_DIRECT for NFS READ and WRITE Chuck Lever
2026-09-29 19:56 ` Mike Snitzer
2026-09-29 23:17 ` Chuck Lever
2026-09-29 23:30 ` Mike Snitzer
2026-09-30 0:20 ` Chuck Lever
2026-09-30 12:46 ` Mike Snitzer
2026-09-29 17:34 ` [PATCH 02/10] NFSD: mark the direct middle of a split WRITE IOCB_DONTCACHE as well Mike Snitzer
2026-09-29 17:34 ` [PATCH 03/10] NFSD: only split a direct-mode WRITE for a worthwhile direct middle Mike Snitzer
2026-09-29 17:34 ` [PATCH 04/10] NFSD: do not use direct I/O for a READ smaller than its alignment Mike Snitzer
2026-09-29 17:34 ` [PATCH 05/10] NFSD: Enable return of an updated stable_how to NFS clients Mike Snitzer
2026-09-29 17:34 ` [PATCH 06/10] NFSD: let a direct-mode WRITE raise stable_how and elide the client's COMMIT Mike Snitzer
2026-09-29 17:34 ` [PATCH 07/10] NFSD: persist a synchronous direct-mode WRITE once, after all of its segments Mike Snitzer
2026-09-29 17:34 ` [PATCH 08/10] NFSD: keep boundary page of a split direct-mode WRITE until both writers complete Mike Snitzer
2026-09-29 17:34 ` [PATCH 09/10] NFSD: add direct_misaligned_dontcache debugfs knob Mike Snitzer
2026-09-29 17:34 ` [PATCH 10/10] NFSD: add tracing for how direct-mode READ and WRITE are serviced Mike Snitzer
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=20260929173423.16149-2-snitzer@kernel.org \
--to=snitzer@hammerspace.com \
--cc=cel@kernel.org \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox