Linux NFS development
 help / color / mirror / Atom feed
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


  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