Linux NFS development
 help / color / mirror / Atom feed
* [PATCH 0/5] Minor NFSD global DRC clean-ups
@ 2026-07-17  0:12 Chuck Lever
  2026-07-17  0:12 ` [PATCH 1/5] NFSD: Fix off-by-one in DRC bucket pruning limit Chuck Lever
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-17  0:12 UTC (permalink / raw)
  To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs

I'm working on a series of global DRC improvements. A few minor
clean-ups are ready for merge now.

Chuck Lever (5):
  NFSD: Fix off-by-one in DRC bucket pruning limit
  NFSD: Eliminate percpu counter contention in DRC memory accounting
  NFSD: Eliminate percpu counter contention in reply cache statistics
  NFSD: Eliminate percpu counter contention in IO byte accounting
  NFSD: Document reply_cache_stats ABI

 .../ABI/testing/procfs-nfsd-reply_cache_stats | 38 +++++++++
 fs/nfsd/nfscache.c                            |  2 +-
 fs/nfsd/stats.h                               | 78 ++++++++++++++++---
 3 files changed, 108 insertions(+), 10 deletions(-)
 create mode 100644 Documentation/ABI/testing/procfs-nfsd-reply_cache_stats

-- 
2.54.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/5] NFSD: Fix off-by-one in DRC bucket pruning limit
  2026-07-17  0:12 [PATCH 0/5] Minor NFSD global DRC clean-ups Chuck Lever
@ 2026-07-17  0:12 ` Chuck Lever
  2026-07-17  0:12 ` [PATCH 2/5] NFSD: Eliminate percpu counter contention in DRC memory accounting Chuck Lever
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-17  0:12 UTC (permalink / raw)
  To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs

nfsd_prune_bucket_locked() evicts an entry before checking
the freed count against @max. The check uses "++freed > max",
which does not break until freed exceeds max, resulting in
max + 1 evictions. Use ">=" so the limit stated in the
function comment is honored.

Fixes: a9507f6af145 ("NFSD: Replace nfsd_prune_bucket()")
Signed-off-by: Chuck Lever <cel@kernel.org>
---
 fs/nfsd/nfscache.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
index 07a53a5b3d37..c7db532c8523 100644
--- a/fs/nfsd/nfscache.c
+++ b/fs/nfsd/nfscache.c
@@ -277,7 +277,7 @@ nfsd_prune_bucket_locked(struct nfsd_net *nn, struct nfsd_drc_bucket *b,
 		nfsd_cacherep_unlink_locked(nn, b, rp);
 		list_add(&rp->c_lru, dispose);
 
-		if (max && ++freed > max)
+		if (max && ++freed >= max)
 			break;
 	}
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/5] NFSD: Eliminate percpu counter contention in DRC memory accounting
  2026-07-17  0:12 [PATCH 0/5] Minor NFSD global DRC clean-ups Chuck Lever
  2026-07-17  0:12 ` [PATCH 1/5] NFSD: Fix off-by-one in DRC bucket pruning limit Chuck Lever
@ 2026-07-17  0:12 ` Chuck Lever
  2026-07-17  0:12 ` [PATCH 3/5] NFSD: Eliminate percpu counter contention in reply cache statistics Chuck Lever
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-17  0:12 UTC (permalink / raw)
  To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs

The DRC memory usage counter (NFSD_STATS_DRC_MEM_USAGE) tracks
bytes, but percpu_counter_add() uses the global percpu_counter_batch
threshold of max(32, 2*nr_cpus). Each DRC entry add or removal
updates the counter by sizeof(struct nfsd_cacherep) (~144 bytes),
which always exceeds the batch threshold. percpu_counter_add()
then acquires the counter's global spinlock on every update,
serializing all nfsd threads.

On a 10-CPU NFS server handling a high rate of non-idempotent
NFSv3 operations, this lock accounts for a measurable fraction
of total spin lock overhead because nfsd_cache_lookup() both
inserts a new entry and prunes up to three old entries per RPC,
producing 4-7 global lock acquisitions per operation.

Switch to percpu_counter_add_local() and percpu_counter_sub_local(),
which batch with INT_MAX so that updates always remain on the per-CPU
fast path regardless of the amount. The only reader of this counter uses
percpu_counter_sum_positive(), which sums the per-CPU deltas under the
global lock, so read accuracy is unaffected.

Signed-off-by: Chuck Lever <cel@kernel.org>
---
 fs/nfsd/stats.h | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/stats.h b/fs/nfsd/stats.h
index 87736b7fbf28..15d30c045dc3 100644
--- a/fs/nfsd/stats.h
+++ b/fs/nfsd/stats.h
@@ -60,14 +60,32 @@ static inline void nfsd_stats_payload_misses_inc(struct nfsd_net *nn)
 	percpu_counter_inc(&nn->counter[NFSD_STATS_PAYLOAD_MISSES]);
 }
 
+/**
+ * nfsd_stats_drc_mem_usage_add - Add memory used by a cache item
+ * @nn: target network namespace
+ * @amount: byte count
+ *
+ * percpu_counter_add_local() keeps updates on the per-CPU fast
+ * path. The sole reader, percpu_counter_sum_positive(), sums the
+ * per-CPU deltas, so batching locally does not lose accuracy.
+ */
 static inline void nfsd_stats_drc_mem_usage_add(struct nfsd_net *nn, s64 amount)
 {
-	percpu_counter_add(&nn->counter[NFSD_STATS_DRC_MEM_USAGE], amount);
+	percpu_counter_add_local(&nn->counter[NFSD_STATS_DRC_MEM_USAGE],
+				 amount);
 }
 
+/**
+ * nfsd_stats_drc_mem_usage_sub - Subtract memory used by a cache item
+ * @nn: target network namespace
+ * @amount: byte count
+ *
+ * See nfsd_stats_drc_mem_usage_add() for batching rationale.
+ */
 static inline void nfsd_stats_drc_mem_usage_sub(struct nfsd_net *nn, s64 amount)
 {
-	percpu_counter_sub(&nn->counter[NFSD_STATS_DRC_MEM_USAGE], amount);
+	percpu_counter_sub_local(&nn->counter[NFSD_STATS_DRC_MEM_USAGE],
+				 amount);
 }
 
 #ifdef CONFIG_NFSD_V4
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/5] NFSD: Eliminate percpu counter contention in reply cache statistics
  2026-07-17  0:12 [PATCH 0/5] Minor NFSD global DRC clean-ups Chuck Lever
  2026-07-17  0:12 ` [PATCH 1/5] NFSD: Fix off-by-one in DRC bucket pruning limit Chuck Lever
  2026-07-17  0:12 ` [PATCH 2/5] NFSD: Eliminate percpu counter contention in DRC memory accounting Chuck Lever
@ 2026-07-17  0:12 ` Chuck Lever
  2026-07-17  0:12 ` [PATCH 4/5] NFSD: Eliminate percpu counter contention in IO byte accounting Chuck Lever
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-17  0:12 UTC (permalink / raw)
  To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs

Each RPC passes through nfsd_cache_lookup(), which increments one
of nfsd_stats_rc_hits_inc(), nfsd_stats_rc_misses_inc(), or
nfsd_stats_rc_nocache_inc(). These helpers update
per-net-namespace percpu_counters with percpu_counter_inc(),
which applies the default batch threshold of max(32, 2*nr_cpus).

Once a CPU's local delta reaches that threshold, the update folds
into the shared counter under its global spinlock. On a busy
multi-CPU server this produces lock traffic on a counter cacheline
shared across all CPUs, growing with the request rate.

Switch to percpu_counter_add_local(fbc, 1), which batches with
INT_MAX so that increments always remain on the per-CPU fast path.
This matches the treatment already applied to the IO byte and DRC
memory counters. All readers of these counters use
percpu_counter_sum_positive(), which sums the per-CPU deltas under
the global lock, so read accuracy is unaffected.

Signed-off-by: Chuck Lever <cel@kernel.org>
---
 fs/nfsd/stats.h | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/stats.h b/fs/nfsd/stats.h
index 15d30c045dc3..598ea45d1722 100644
--- a/fs/nfsd/stats.h
+++ b/fs/nfsd/stats.h
@@ -16,19 +16,39 @@
 struct proc_dir_entry *nfsd_proc_stat_init(struct net *net);
 void nfsd_proc_stat_shutdown(struct net *net);
 
+/**
+ * nfsd_stats_rc_hits_inc - Count a duplicate reply cache hit
+ * @nn: target network namespace
+ *
+ * These reply cache counters are updated once per RPC. Readers use
+ * percpu_counter_sum_positive(), so local batching does not affect
+ * read accuracy.
+ */
 static inline void nfsd_stats_rc_hits_inc(struct nfsd_net *nn)
 {
-	percpu_counter_inc(&nn->counter[NFSD_STATS_RC_HITS]);
+	percpu_counter_add_local(&nn->counter[NFSD_STATS_RC_HITS], 1);
 }
 
+/**
+ * nfsd_stats_rc_misses_inc - Count a duplicate reply cache miss
+ * @nn: target network namespace
+ *
+ * See nfsd_stats_rc_hits_inc() for batching rationale.
+ */
 static inline void nfsd_stats_rc_misses_inc(struct nfsd_net *nn)
 {
-	percpu_counter_inc(&nn->counter[NFSD_STATS_RC_MISSES]);
+	percpu_counter_add_local(&nn->counter[NFSD_STATS_RC_MISSES], 1);
 }
 
+/**
+ * nfsd_stats_rc_nocache_inc - Count a request not cached in the reply cache
+ * @nn: target network namespace
+ *
+ * See nfsd_stats_rc_hits_inc() for batching rationale.
+ */
 static inline void nfsd_stats_rc_nocache_inc(struct nfsd_net *nn)
 {
-	percpu_counter_inc(&nn->counter[NFSD_STATS_RC_NOCACHE]);
+	percpu_counter_add_local(&nn->counter[NFSD_STATS_RC_NOCACHE], 1);
 }
 
 static inline void nfsd_stats_fh_stale_inc(struct nfsd_net *nn,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/5] NFSD: Eliminate percpu counter contention in IO byte accounting
  2026-07-17  0:12 [PATCH 0/5] Minor NFSD global DRC clean-ups Chuck Lever
                   ` (2 preceding siblings ...)
  2026-07-17  0:12 ` [PATCH 3/5] NFSD: Eliminate percpu counter contention in reply cache statistics Chuck Lever
@ 2026-07-17  0:12 ` Chuck Lever
  2026-07-17  0:12 ` [PATCH 5/5] NFSD: Document reply_cache_stats ABI Chuck Lever
  2026-07-17  4:44 ` [PATCH 0/5] Minor NFSD global DRC clean-ups NeilBrown
  5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-17  0:12 UTC (permalink / raw)
  To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs

nfsd_stats_io_write_add() and nfsd_stats_io_read_add() accumulate
byte counts in per-net-namespace and per-export percpu_counters
using percpu_counter_add(), which applies the default batch
threshold of max(32, 2*nr_cpus).

For a 4 KB NFS WRITE, the amount (4096) always exceeds this
threshold, so percpu_counter_add_batch() acquires the counter's
global spinlock on every update. Each WRITE RPC updates two
counters (per-net and per-export), producing two global lock
acquisitions per operation. Profiling on a 10-CPU RDMA NFS
server shows 0.44% of total CPU cycles spent contending on
these locks during a small random write workload.

Switch to percpu_counter_add_local(), which batches with
INT_MAX so that updates always remain on the per-CPU fast
path regardless of the amount. All readers of these counters
already use percpu_counter_sum_positive(), which sums the
per-CPU deltas under the global lock, so read accuracy is
unaffected.

Signed-off-by: Chuck Lever <cel@kernel.org>
---
 fs/nfsd/stats.h | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/stats.h b/fs/nfsd/stats.h
index 598ea45d1722..c8d7e3934c33 100644
--- a/fs/nfsd/stats.h
+++ b/fs/nfsd/stats.h
@@ -59,20 +59,42 @@ static inline void nfsd_stats_fh_stale_inc(struct nfsd_net *nn,
 		percpu_counter_inc(&exp->ex_stats->counter[EXP_STATS_FH_STALE]);
 }
 
+/**
+ * nfsd_stats_io_read_add - Count number of bytes for an NFS READ
+ * @nn: target network namespace
+ * @exp: target export
+ * @amount: byte count
+ *
+ * These counters are updated on every READ request. Readers use
+ * percpu_counter_sum_positive(), so local batching does not affect
+ * read accuracy.
+ */
 static inline void nfsd_stats_io_read_add(struct nfsd_net *nn,
 					  struct svc_export *exp, s64 amount)
 {
-	percpu_counter_add(&nn->counter[NFSD_STATS_IO_READ], amount);
+	percpu_counter_add_local(&nn->counter[NFSD_STATS_IO_READ], amount);
 	if (exp && exp->ex_stats)
-		percpu_counter_add(&exp->ex_stats->counter[EXP_STATS_IO_READ], amount);
+		percpu_counter_add_local(&exp->ex_stats->counter[EXP_STATS_IO_READ],
+					 amount);
 }
 
+/**
+ * nfsd_stats_io_write_add - Count number of bytes for an NFS WRITE
+ * @nn: target network namespace
+ * @exp: target export
+ * @amount: byte count
+ *
+ * These counters are updated on every WRITE request. Readers use
+ * percpu_counter_sum_positive(), so local batching does not affect
+ * read accuracy.
+ */
 static inline void nfsd_stats_io_write_add(struct nfsd_net *nn,
 					   struct svc_export *exp, s64 amount)
 {
-	percpu_counter_add(&nn->counter[NFSD_STATS_IO_WRITE], amount);
+	percpu_counter_add_local(&nn->counter[NFSD_STATS_IO_WRITE], amount);
 	if (exp && exp->ex_stats)
-		percpu_counter_add(&exp->ex_stats->counter[EXP_STATS_IO_WRITE], amount);
+		percpu_counter_add_local(&exp->ex_stats->counter[EXP_STATS_IO_WRITE],
+					 amount);
 }
 
 static inline void nfsd_stats_payload_misses_inc(struct nfsd_net *nn)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/5] NFSD: Document reply_cache_stats ABI
  2026-07-17  0:12 [PATCH 0/5] Minor NFSD global DRC clean-ups Chuck Lever
                   ` (3 preceding siblings ...)
  2026-07-17  0:12 ` [PATCH 4/5] NFSD: Eliminate percpu counter contention in IO byte accounting Chuck Lever
@ 2026-07-17  0:12 ` Chuck Lever
  2026-07-17  4:44 ` [PATCH 0/5] Minor NFSD global DRC clean-ups NeilBrown
  5 siblings, 0 replies; 7+ messages in thread
From: Chuck Lever @ 2026-07-17  0:12 UTC (permalink / raw)
  To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs

/proc/fs/nfsd/reply_cache_stats has been present since v3.10 but
has no entry in Documentation/ABI/. Add one under testing/ that
documents the current field set, types, and parsing expectations.

This establishes a contract that parsers should match on field
name rather than line position, allowing fields to be added or
removed across kernel versions without breaking well-written
consumers.

Signed-off-by: Chuck Lever <cel@kernel.org>
---
 .../ABI/testing/procfs-nfsd-reply_cache_stats | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 Documentation/ABI/testing/procfs-nfsd-reply_cache_stats

diff --git a/Documentation/ABI/testing/procfs-nfsd-reply_cache_stats b/Documentation/ABI/testing/procfs-nfsd-reply_cache_stats
new file mode 100644
index 000000000000..57ed5f8e6597
--- /dev/null
+++ b/Documentation/ABI/testing/procfs-nfsd-reply_cache_stats
@@ -0,0 +1,38 @@
+What:		/proc/fs/nfsd/reply_cache_stats
+Date:		March 2013
+KernelVersion:	3.10
+Contact:	linux-nfs@vger.kernel.org
+Description:
+		Provides statistics for the NFS server duplicate reply
+		cache (DRC). The file contains one labeled field per
+		line. Each line has the form "field name:" followed by
+		whitespace and a decimal value.
+
+		Fields:
+
+		=======================  ======  ==========================
+		max entries              u32     Upper bound on cache size
+		num entries              u32     Current entry count
+		hash buckets             u32     Number of hash buckets
+		mem usage                s64     Bytes consumed by the DRC
+		cache hits               s64     Requests answered from cache
+		cache misses             s64     Requests not found in cache
+		not cached               s64     Idempotent requests that
+		                                 bypass the cache
+		payload misses           s64     XID matched but request
+		                                 checksum did not
+		longest chain len        u32     Longest hash chain observed
+		cachesize at longest     u32     Cache size when longest
+		                                 chain was recorded
+		=======================  ======  ==========================
+
+		Counter fields (cache hits, cache misses, not cached,
+		payload misses, mem usage) are maintained with per-cpu
+		counters and may briefly show stale values under
+		concurrent load. There is no way to reset these
+		counters; consumers should compute rates by sampling
+		over time.
+
+		New fields may be appended in future kernels. Parsers
+		should match on field name, not line position.
+Users:		nfs-utils (https://git.linux-nfs.org/?p=steved/nfs-utils.git)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/5] Minor NFSD global DRC clean-ups
  2026-07-17  0:12 [PATCH 0/5] Minor NFSD global DRC clean-ups Chuck Lever
                   ` (4 preceding siblings ...)
  2026-07-17  0:12 ` [PATCH 5/5] NFSD: Document reply_cache_stats ABI Chuck Lever
@ 2026-07-17  4:44 ` NeilBrown
  5 siblings, 0 replies; 7+ messages in thread
From: NeilBrown @ 2026-07-17  4:44 UTC (permalink / raw)
  To: Chuck Lever
  Cc: Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs

On Fri, 17 Jul 2026, Chuck Lever wrote:
> I'm working on a series of global DRC improvements. A few minor
> clean-ups are ready for merge now.
> 
> Chuck Lever (5):
>   NFSD: Fix off-by-one in DRC bucket pruning limit
>   NFSD: Eliminate percpu counter contention in DRC memory accounting
>   NFSD: Eliminate percpu counter contention in reply cache statistics
>   NFSD: Eliminate percpu counter contention in IO byte accounting
>   NFSD: Document reply_cache_stats ABI
> 
>  .../ABI/testing/procfs-nfsd-reply_cache_stats | 38 +++++++++
>  fs/nfsd/nfscache.c                            |  2 +-
>  fs/nfsd/stats.h                               | 78 ++++++++++++++++---
>  3 files changed, 108 insertions(+), 10 deletions(-)
>  create mode 100644 Documentation/ABI/testing/procfs-nfsd-reply_cache_stats
> 
> -- 
> 2.54.0
> 
> 

All looks very sensible to me.
Reviewed-by: NeilBrown <neil@brown.name>

Thanks,
NeilBrown

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-17  4:44 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-17  0:12 [PATCH 0/5] Minor NFSD global DRC clean-ups Chuck Lever
2026-07-17  0:12 ` [PATCH 1/5] NFSD: Fix off-by-one in DRC bucket pruning limit Chuck Lever
2026-07-17  0:12 ` [PATCH 2/5] NFSD: Eliminate percpu counter contention in DRC memory accounting Chuck Lever
2026-07-17  0:12 ` [PATCH 3/5] NFSD: Eliminate percpu counter contention in reply cache statistics Chuck Lever
2026-07-17  0:12 ` [PATCH 4/5] NFSD: Eliminate percpu counter contention in IO byte accounting Chuck Lever
2026-07-17  0:12 ` [PATCH 5/5] NFSD: Document reply_cache_stats ABI Chuck Lever
2026-07-17  4:44 ` [PATCH 0/5] Minor NFSD global DRC clean-ups NeilBrown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox