All of lore.kernel.org
 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 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.