* [PATCH v3 1/4] sunrpc: add per-netns per-procedure call counts to svc_stat
2026-06-09 16:15 [PATCH v3 0/4] nfsd/sunrpc: convert nfsstat server-side interfaces to use netlink Jeff Layton
@ 2026-06-09 16:15 ` Jeff Layton
2026-06-09 16:15 ` [PATCH v3 2/4] sunrpc: use per-net counts in svc_seq_show() Jeff Layton
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2026-06-09 16:15 UTC (permalink / raw)
To: Chuck Lever, NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: Trond Myklebust, Anna Schumaker, Steve Dickson, linux-nfs,
linux-kernel, Jeff Layton
The existing per-procedure call counts live in global
svc_version->vs_count[] arrays which are not network-namespace-aware.
Add per-netns equivalents in struct svc_stat so the upcoming netlink
stats interface can return namespace-scoped statistics.
Add a vs_count pointer array to struct svc_stat, along with
svc_stat_alloc_counts() and svc_stat_free_counts() helpers to manage
per-version percpu call count arrays.
Increment the per-net counter alongside the global one in
svc_generic_init_request(). Call the alloc/free helpers from
nfsd_net_init() and nfsd_net_exit().
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/nfsctl.c | 8 ++++++-
include/linux/sunrpc/stats.h | 6 +++++
net/sunrpc/stats.c | 54 ++++++++++++++++++++++++++++++++++++++++++++
net/sunrpc/svc.c | 8 +++++++
4 files changed, 75 insertions(+), 1 deletion(-)
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index c06d25c06f06..7b802d335501 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2436,9 +2436,12 @@ static __net_init int nfsd_net_init(struct net *net)
memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats));
nn->nfsd_svcstats.program = &nfsd_programs[0];
+ retval = svc_stat_alloc_counts(&nn->nfsd_svcstats);
+ if (retval)
+ goto out_proc_error;
if (!nfsd_proc_stat_init(net)) {
retval = -ENOMEM;
- goto out_proc_error;
+ goto out_svcstats_error;
}
for (i = 0; i < sizeof(nn->nfsd_versions); i++)
@@ -2456,6 +2459,8 @@ static __net_init int nfsd_net_init(struct net *net)
#endif
return 0;
+out_svcstats_error:
+ svc_stat_free_counts(&nn->nfsd_svcstats);
out_proc_error:
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
out_repcache_error:
@@ -2496,6 +2501,7 @@ static __net_exit void nfsd_net_exit(struct net *net)
kfree_sensitive(nn->fh_key);
nfsd_net_cb_shutdown(nn);
nfsd_proc_stat_shutdown(net);
+ svc_stat_free_counts(&nn->nfsd_svcstats);
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
nfsd_idmap_shutdown(net);
nfsd_export_shutdown(net);
diff --git a/include/linux/sunrpc/stats.h b/include/linux/sunrpc/stats.h
index 3ce1550d1beb..087ade905e29 100644
--- a/include/linux/sunrpc/stats.h
+++ b/include/linux/sunrpc/stats.h
@@ -37,9 +37,15 @@ struct svc_stat {
rpcbadfmt,
rpcbadauth,
rpcbadclnt;
+
+ /* Per-version per-procedure call counts (per-cpu, per-netns) */
+ unsigned long __percpu **vs_count;
};
struct net;
+int svc_stat_alloc_counts(struct svc_stat *statp);
+void svc_stat_free_counts(struct svc_stat *statp);
+
#ifdef CONFIG_PROC_FS
int rpc_proc_init(struct net *);
void rpc_proc_exit(struct net *);
diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c
index 7093e18ac26c..5bcecd2919b1 100644
--- a/net/sunrpc/stats.c
+++ b/net/sunrpc/stats.c
@@ -116,6 +116,60 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp)
}
EXPORT_SYMBOL_GPL(svc_seq_show);
+/**
+ * svc_stat_alloc_counts - allocate per-netns per-version call count arrays
+ * @statp: svc_stat whose vs_count arrays should be allocated
+ *
+ * statp->program must be set before calling this.
+ *
+ * Returns zero on success, or a negative errno otherwise.
+ */
+int svc_stat_alloc_counts(struct svc_stat *statp)
+{
+ struct svc_program *prog = statp->program;
+ unsigned int i;
+
+ statp->vs_count = kcalloc(prog->pg_nvers,
+ sizeof(unsigned long __percpu *),
+ GFP_KERNEL);
+ if (!statp->vs_count)
+ return -ENOMEM;
+
+ for (i = 0; i < prog->pg_nvers; i++) {
+ if (!prog->pg_vers[i])
+ continue;
+ statp->vs_count[i] = __alloc_percpu(prog->pg_vers[i]->vs_nproc *
+ sizeof(unsigned long),
+ sizeof(unsigned long));
+ if (!statp->vs_count[i])
+ goto err;
+ }
+ return 0;
+err:
+ svc_stat_free_counts(statp);
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(svc_stat_alloc_counts);
+
+/**
+ * svc_stat_free_counts - free per-netns per-version call count arrays
+ * @statp: svc_stat whose vs_count arrays should be freed
+ */
+void svc_stat_free_counts(struct svc_stat *statp)
+{
+ struct svc_program *prog = statp->program;
+ unsigned int i;
+
+ if (!statp->vs_count)
+ return;
+
+ for (i = 0; i < prog->pg_nvers; i++)
+ free_percpu(statp->vs_count[i]);
+ kfree(statp->vs_count);
+ statp->vs_count = NULL;
+}
+EXPORT_SYMBOL_GPL(svc_stat_free_counts);
+
/**
* rpc_alloc_iostats - allocate an rpc_iostats structure
* @clnt: RPC program, version, and xprt
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 009373737ea9..200b57e633dd 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1408,6 +1408,14 @@ svc_generic_init_request(struct svc_rqst *rqstp,
/* Bump per-procedure stats counter */
this_cpu_inc(versp->vs_count[rqstp->rq_proc]);
+ /* Bump per-net per-procedure stats counter */
+ if (rqstp->rq_server->sv_stats &&
+ rqstp->rq_server->sv_stats->program == progp &&
+ rqstp->rq_server->sv_stats->vs_count &&
+ rqstp->rq_server->sv_stats->vs_count[rqstp->rq_vers])
+ this_cpu_inc(rqstp->rq_server->sv_stats->vs_count
+ [rqstp->rq_vers][rqstp->rq_proc]);
+
ret->dispatch = versp->vs_dispatch;
return rpc_success;
err_bad_vers:
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v3 2/4] sunrpc: use per-net counts in svc_seq_show()
2026-06-09 16:15 [PATCH v3 0/4] nfsd/sunrpc: convert nfsstat server-side interfaces to use netlink Jeff Layton
2026-06-09 16:15 ` [PATCH v3 1/4] sunrpc: add per-netns per-procedure call counts to svc_stat Jeff Layton
@ 2026-06-09 16:15 ` Jeff Layton
2026-06-09 16:15 ` [PATCH v3 3/4] nfsd: implement server-stats-get netlink handler Jeff Layton
2026-06-09 16:15 ` [PATCH v3 4/4] sunrpc: remove unused svc_version vs_count field Jeff Layton
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2026-06-09 16:15 UTC (permalink / raw)
To: Chuck Lever, NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: Trond Myklebust, Anna Schumaker, Steve Dickson, linux-nfs,
linux-kernel, Jeff Layton
Update svc_seq_show() to read from the per-netns
statp->vs_count[] arrays instead of the global
svc_version->vs_count[].
The only caller is nfsd, which always allocates vs_count via
svc_stat_alloc_counts() in nfsd_net_init(), so the per-netns
arrays are always available.
This makes /proc/net/rpc/nfsd report per-network-namespace
procedure call counts.
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
net/sunrpc/stats.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c
index 5bcecd2919b1..fc2db251dfa0 100644
--- a/net/sunrpc/stats.c
+++ b/net/sunrpc/stats.c
@@ -108,7 +108,7 @@ void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp)
for (j = 0; j < vers->vs_nproc; j++) {
count = 0;
for_each_possible_cpu(k)
- count += per_cpu(vers->vs_count[j], k);
+ count += per_cpu(statp->vs_count[i][j], k);
seq_printf(seq, " %lu", count);
}
seq_putc(seq, '\n');
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v3 3/4] nfsd: implement server-stats-get netlink handler
2026-06-09 16:15 [PATCH v3 0/4] nfsd/sunrpc: convert nfsstat server-side interfaces to use netlink Jeff Layton
2026-06-09 16:15 ` [PATCH v3 1/4] sunrpc: add per-netns per-procedure call counts to svc_stat Jeff Layton
2026-06-09 16:15 ` [PATCH v3 2/4] sunrpc: use per-net counts in svc_seq_show() Jeff Layton
@ 2026-06-09 16:15 ` Jeff Layton
2026-06-09 16:15 ` [PATCH v3 4/4] sunrpc: remove unused svc_version vs_count field Jeff Layton
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2026-06-09 16:15 UTC (permalink / raw)
To: Chuck Lever, NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: Trond Myklebust, Anna Schumaker, Steve Dickson, linux-nfs,
linux-kernel, Jeff Layton
Implement nfsd_nl_server_stats_get_dumpit() which exposes the
NFS server statistics currently available via /proc/net/rpc/nfsd
through the nfsd generic netlink family.
The handler uses a dump operation to stream statistics across
multiple netlink messages:
- First message: all scalar stats (reply cache, filehandle,
IO, network, RPC) plus per-version procedure counts
(proc2/3/4-ops) using per-netns vs_count arrays.
- Subsequent messages: NFSv4 per-operation counts
(proc4ops-ops), one entry per message, using cb->args[0]
to track the current operation index across dump calls.
This allows nfsstat to retrieve server statistics via netlink
with a procfs fallback for older kernels.
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Documentation/netlink/specs/nfsd.yaml | 105 +++++++++++++++++
fs/nfsd/netlink.c | 5 +
fs/nfsd/netlink.h | 2 +
fs/nfsd/nfsctl.c | 213 ++++++++++++++++++++++++++++++++++
include/uapi/linux/nfsd_netlink.h | 35 ++++++
5 files changed, 360 insertions(+)
diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
index 8f36fadd68f7..2a89d355ee7b 100644
--- a/Documentation/netlink/specs/nfsd.yaml
+++ b/Documentation/netlink/specs/nfsd.yaml
@@ -330,6 +330,86 @@ attribute-sets:
of which client holds the state. Intended for use after
all clients have been unexported from a given path,
enabling the underlying filesystem to be unmounted.
+ -
+ name: server-proc-entry
+ attributes:
+ -
+ name: op
+ type: u32
+ -
+ name: count
+ type: u64
+ -
+ name: pad
+ type: pad
+ -
+ name: server-stats
+ attributes:
+ -
+ name: rc-hits
+ type: u64
+ -
+ name: rc-misses
+ type: u64
+ -
+ name: rc-nocache
+ type: u64
+ -
+ name: pad
+ type: pad
+ -
+ name: fh-stale
+ type: u64
+ -
+ name: io-read
+ type: u64
+ -
+ name: io-write
+ type: u64
+ -
+ name: netcnt
+ type: u32
+ -
+ name: netudpcnt
+ type: u32
+ -
+ name: nettcpcnt
+ type: u32
+ -
+ name: nettcpconn
+ type: u32
+ -
+ name: rpccnt
+ type: u32
+ -
+ name: rpcbadfmt
+ type: u32
+ -
+ name: rpcbadauth
+ type: u32
+ -
+ name: rpcbadclnt
+ type: u32
+ -
+ name: proc2-ops
+ type: nest
+ nested-attributes: server-proc-entry
+ multi-attr: true
+ -
+ name: proc3-ops
+ type: nest
+ nested-attributes: server-proc-entry
+ multi-attr: true
+ -
+ name: proc4-ops
+ type: nest
+ nested-attributes: server-proc-entry
+ multi-attr: true
+ -
+ name: proc4ops-ops
+ type: nest
+ nested-attributes: server-proc-entry
+ multi-attr: true
operations:
list:
@@ -516,6 +596,31 @@ operations:
request:
attributes:
- path
+ -
+ name: server-stats-get
+ doc: dump NFS server statistics
+ attribute-set: server-stats
+ dump:
+ reply:
+ attributes:
+ - rc-hits
+ - rc-misses
+ - rc-nocache
+ - fh-stale
+ - io-read
+ - io-write
+ - netcnt
+ - netudpcnt
+ - nettcpcnt
+ - nettcpconn
+ - rpccnt
+ - rpcbadfmt
+ - rpcbadauth
+ - rpcbadclnt
+ - proc2-ops
+ - proc3-ops
+ - proc4-ops
+ - proc4ops-ops
mcast-groups:
list:
diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
index fbee3676d253..eba8b353f412 100644
--- a/fs/nfsd/netlink.c
+++ b/fs/nfsd/netlink.c
@@ -225,6 +225,11 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
.maxattr = NFSD_A_UNLOCK_EXPORT_PATH,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
+ {
+ .cmd = NFSD_CMD_SERVER_STATS_GET,
+ .dumpit = nfsd_nl_server_stats_get_dumpit,
+ .flags = GENL_CMD_CAP_DUMP,
+ },
};
static const struct genl_multicast_group nfsd_nl_mcgrps[] = {
diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h
index af41aa0d4a65..027e2953db26 100644
--- a/fs/nfsd/netlink.h
+++ b/fs/nfsd/netlink.h
@@ -42,6 +42,8 @@ int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info);
+int nfsd_nl_server_stats_get_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb);
enum {
NFSD_NLGRP_NONE,
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 7b802d335501..d60f2a0f07f0 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2252,6 +2252,219 @@ int nfsd_nl_cache_flush_doit(struct sk_buff *skb, struct genl_info *info)
return 0;
}
+static int nfsd_nl_fill_proc_ops(struct sk_buff *skb, int attr,
+ unsigned long __percpu *counts,
+ unsigned int nproc)
+{
+ struct nlattr *nest;
+ unsigned int j;
+ int k;
+
+ for (j = 0; j < nproc; j++) {
+ unsigned long count = 0;
+
+ for_each_possible_cpu(k)
+ count += per_cpu(counts[j], k);
+
+ nest = nla_nest_start(skb, attr);
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u32(skb, NFSD_A_SERVER_PROC_ENTRY_OP, j) ||
+ nla_put_u64_64bit(skb, NFSD_A_SERVER_PROC_ENTRY_COUNT,
+ count, NFSD_A_SERVER_PROC_ENTRY_PAD)) {
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+ }
+ nla_nest_end(skb, nest);
+ }
+
+ return 0;
+}
+
+/**
+ * nfsd_nl_server_stats_get_dumpit - dump NFS server statistics
+ * @skb: reply buffer
+ * @cb: netlink metadata and command arguments
+ *
+ * cb->args[0] tracks the current NFSv4 operation index for proc4ops.
+ * A value of 0 means we haven't started yet. We emit all scalar stats
+ * and per-version procedure counts in the first message, then emit
+ * proc4ops entries filling as many as will fit per message.
+ *
+ * Returns the size of the reply or a negative errno.
+ */
+int nfsd_nl_server_stats_get_dumpit(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct svc_stat *statp = &nn->nfsd_svcstats;
+ struct svc_program *prog = statp->program;
+ int start = cb->args[0];
+ void *hdr;
+ int ret;
+
+ /*
+ * cb->args[0] == 0: first call, emit scalar stats + procN counts
+ * cb->args[0] > 0: emit proc4ops entries starting from args[0] - 1
+ * cb->args[0] < 0: done
+ */
+ if (start < 0)
+ return 0;
+
+ if (start == 0) {
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &nfsd_nl_family,
+ NLM_F_MULTI, NFSD_CMD_SERVER_STATS_GET);
+ if (!hdr)
+ return -ENOBUFS;
+
+ /* Reply cache stats */
+ {
+ u64 hits, misses, nocache;
+
+ hits = percpu_counter_sum_positive(&nn->counter[NFSD_STATS_RC_HITS]);
+ misses = percpu_counter_sum_positive(&nn->counter[NFSD_STATS_RC_MISSES]);
+ nocache = percpu_counter_sum_positive(&nn->counter[NFSD_STATS_RC_NOCACHE]);
+ ret = nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_RC_HITS,
+ hits, NFSD_A_SERVER_STATS_PAD) ||
+ nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_RC_MISSES,
+ misses, NFSD_A_SERVER_STATS_PAD) ||
+ nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_RC_NOCACHE,
+ nocache, NFSD_A_SERVER_STATS_PAD);
+ }
+ if (ret)
+ goto err_cancel;
+
+ /* Filehandle stats */
+ ret = nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_FH_STALE,
+ percpu_counter_sum_positive(&nn->counter[NFSD_STATS_FH_STALE]),
+ NFSD_A_SERVER_STATS_PAD);
+ if (ret)
+ goto err_cancel;
+
+ /* IO stats */
+ {
+ u64 rd, wr;
+
+ rd = percpu_counter_sum_positive(&nn->counter[NFSD_STATS_IO_READ]);
+ wr = percpu_counter_sum_positive(&nn->counter[NFSD_STATS_IO_WRITE]);
+ ret = nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_IO_READ,
+ rd, NFSD_A_SERVER_STATS_PAD) ||
+ nla_put_u64_64bit(skb, NFSD_A_SERVER_STATS_IO_WRITE,
+ wr, NFSD_A_SERVER_STATS_PAD);
+ }
+ if (ret)
+ goto err_cancel;
+
+ /* Network stats */
+ ret = nla_put_u32(skb, NFSD_A_SERVER_STATS_NETCNT,
+ statp->netcnt) ||
+ nla_put_u32(skb, NFSD_A_SERVER_STATS_NETUDPCNT,
+ statp->netudpcnt) ||
+ nla_put_u32(skb, NFSD_A_SERVER_STATS_NETTCPCNT,
+ statp->nettcpcnt) ||
+ nla_put_u32(skb, NFSD_A_SERVER_STATS_NETTCPCONN,
+ statp->nettcpconn);
+ if (ret)
+ goto err_cancel;
+
+ /* RPC stats */
+ ret = nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCCNT,
+ statp->rpccnt) ||
+ nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCBADFMT,
+ statp->rpcbadfmt) ||
+ nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCBADAUTH,
+ statp->rpcbadauth) ||
+ nla_put_u32(skb, NFSD_A_SERVER_STATS_RPCBADCLNT,
+ statp->rpcbadclnt);
+ if (ret)
+ goto err_cancel;
+
+ /* Per-version procedure counts */
+ if (statp->vs_count) {
+ static const int proc_attrs[] = {
+ [2] = NFSD_A_SERVER_STATS_PROC2_OPS,
+ [3] = NFSD_A_SERVER_STATS_PROC3_OPS,
+ [4] = NFSD_A_SERVER_STATS_PROC4_OPS,
+ };
+ unsigned int i;
+
+ for (i = 0; i < prog->pg_nvers &&
+ i < ARRAY_SIZE(proc_attrs); i++) {
+ if (!prog->pg_vers[i] ||
+ !statp->vs_count[i])
+ continue;
+ if (!proc_attrs[i])
+ continue;
+ ret = nfsd_nl_fill_proc_ops(skb,
+ proc_attrs[i],
+ statp->vs_count[i],
+ prog->pg_vers[i]->vs_nproc);
+ if (ret)
+ goto err_cancel;
+ }
+ }
+
+ genlmsg_end(skb, hdr);
+ cb->args[0] = 1;
+ }
+
+#ifdef CONFIG_NFSD_V4
+ /* NFSv4 individual operation counts */
+ {
+ int i = (start > 0) ? start - 1 : 0;
+
+ for (; i <= LAST_NFS4_OP; i++) {
+ struct percpu_counter *ctr;
+ struct nlattr *nest;
+ u64 cnt;
+
+ ctr = &nn->counter[NFSD_STATS_NFS4_OP(i)];
+ cnt = percpu_counter_sum_positive(ctr);
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ &nfsd_nl_family, NLM_F_MULTI,
+ NFSD_CMD_SERVER_STATS_GET);
+ if (!hdr) {
+ cb->args[0] = i + 1;
+ goto out;
+ }
+
+ nest = nla_nest_start(skb,
+ NFSD_A_SERVER_STATS_PROC4OPS_OPS);
+ if (!nest) {
+ genlmsg_cancel(skb, hdr);
+ cb->args[0] = i + 1;
+ goto out;
+ }
+ if (nla_put_u32(skb, NFSD_A_SERVER_PROC_ENTRY_OP,
+ i) ||
+ nla_put_u64_64bit(skb,
+ NFSD_A_SERVER_PROC_ENTRY_COUNT,
+ cnt,
+ NFSD_A_SERVER_PROC_ENTRY_PAD)) {
+ nla_nest_cancel(skb, nest);
+ genlmsg_cancel(skb, hdr);
+ cb->args[0] = i + 1;
+ goto out;
+ }
+ nla_nest_end(skb, nest);
+ genlmsg_end(skb, hdr);
+ }
+ }
+#endif
+
+ cb->args[0] = -1;
+out:
+ return skb->len;
+
+err_cancel:
+ genlmsg_cancel(skb, hdr);
+ return ret;
+}
+
int nfsd_cache_notify(struct cache_detail *cd, struct cache_head *h, u32 cache_type)
{
struct genlmsghdr *hdr;
diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
index f5b75d5caba9..3d076d173b1d 100644
--- a/include/uapi/linux/nfsd_netlink.h
+++ b/include/uapi/linux/nfsd_netlink.h
@@ -225,6 +225,40 @@ enum {
NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1)
};
+enum {
+ NFSD_A_SERVER_PROC_ENTRY_OP = 1,
+ NFSD_A_SERVER_PROC_ENTRY_COUNT,
+ NFSD_A_SERVER_PROC_ENTRY_PAD,
+
+ __NFSD_A_SERVER_PROC_ENTRY_MAX,
+ NFSD_A_SERVER_PROC_ENTRY_MAX = (__NFSD_A_SERVER_PROC_ENTRY_MAX - 1)
+};
+
+enum {
+ NFSD_A_SERVER_STATS_RC_HITS = 1,
+ NFSD_A_SERVER_STATS_RC_MISSES,
+ NFSD_A_SERVER_STATS_RC_NOCACHE,
+ NFSD_A_SERVER_STATS_PAD,
+ NFSD_A_SERVER_STATS_FH_STALE,
+ NFSD_A_SERVER_STATS_IO_READ,
+ NFSD_A_SERVER_STATS_IO_WRITE,
+ NFSD_A_SERVER_STATS_NETCNT,
+ NFSD_A_SERVER_STATS_NETUDPCNT,
+ NFSD_A_SERVER_STATS_NETTCPCNT,
+ NFSD_A_SERVER_STATS_NETTCPCONN,
+ NFSD_A_SERVER_STATS_RPCCNT,
+ NFSD_A_SERVER_STATS_RPCBADFMT,
+ NFSD_A_SERVER_STATS_RPCBADAUTH,
+ NFSD_A_SERVER_STATS_RPCBADCLNT,
+ NFSD_A_SERVER_STATS_PROC2_OPS,
+ NFSD_A_SERVER_STATS_PROC3_OPS,
+ NFSD_A_SERVER_STATS_PROC4_OPS,
+ NFSD_A_SERVER_STATS_PROC4OPS_OPS,
+
+ __NFSD_A_SERVER_STATS_MAX,
+ NFSD_A_SERVER_STATS_MAX = (__NFSD_A_SERVER_STATS_MAX - 1)
+};
+
enum {
NFSD_CMD_RPC_STATUS_GET = 1,
NFSD_CMD_THREADS_SET,
@@ -244,6 +278,7 @@ enum {
NFSD_CMD_UNLOCK_IP,
NFSD_CMD_UNLOCK_FILESYSTEM,
NFSD_CMD_UNLOCK_EXPORT,
+ NFSD_CMD_SERVER_STATS_GET,
__NFSD_CMD_MAX,
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v3 4/4] sunrpc: remove unused svc_version vs_count field
2026-06-09 16:15 [PATCH v3 0/4] nfsd/sunrpc: convert nfsstat server-side interfaces to use netlink Jeff Layton
` (2 preceding siblings ...)
2026-06-09 16:15 ` [PATCH v3 3/4] nfsd: implement server-stats-get netlink handler Jeff Layton
@ 2026-06-09 16:15 ` Jeff Layton
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2026-06-09 16:15 UTC (permalink / raw)
To: Chuck Lever, NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey
Cc: Trond Myklebust, Anna Schumaker, Steve Dickson, linux-nfs,
linux-kernel, Jeff Layton
Now that svc_seq_show() and the nfsd netlink stats handler both use
the per-netns svc_stat vs_count arrays, the global per-version
vs_count percpu counters are no longer read by anything. Remove the
vs_count field from struct svc_version and all the associated
DEFINE_PER_CPU_ALIGNED arrays and initializers across nfsd, lockd,
and the NFS client callback service.
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/lockd/svc4proc.c | 4 ----
fs/lockd/svcproc.c | 7 -------
fs/nfs/callback_xdr.c | 6 ------
fs/nfsd/localio.c | 3 ---
fs/nfsd/nfs2acl.c | 3 ---
fs/nfsd/nfs3acl.c | 3 ---
fs/nfsd/nfs3proc.c | 3 ---
fs/nfsd/nfs4proc.c | 3 ---
fs/nfsd/nfsproc.c | 3 ---
include/linux/sunrpc/svc.h | 1 -
net/sunrpc/svc.c | 3 ---
11 files changed, 39 deletions(-)
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 78e675470c4b..8910ee9c2c24 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -1420,14 +1420,10 @@ union nlm4svc_xdrstore {
struct nlm4_shareres_wrapper shareres;
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nlm4svc_call_counters[ARRAY_SIZE(nlm4svc_procedures)]);
-
const struct svc_version nlmsvc_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nlm4svc_procedures),
.vs_proc = nlm4svc_procedures,
- .vs_count = nlm4svc_call_counters,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = sizeof(union nlm4svc_xdrstore),
};
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 4836887f11ef..8f81d95fc5ff 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -1433,25 +1433,18 @@ union nlmsvc_xdrstore {
* NLMv1 defines only procedures 1 - 15. Linux lockd also implements
* procedures 0 (NULL) and 16 (SM_NOTIFY).
*/
-static DEFINE_PER_CPU_ALIGNED(unsigned long, nlm1svc_call_counters[17]);
-
const struct svc_version nlmsvc_version1 = {
.vs_vers = 1,
.vs_nproc = 17,
.vs_proc = nlmsvc_procedures,
- .vs_count = nlm1svc_call_counters,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = sizeof(union nlmsvc_xdrstore),
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nlm3svc_call_counters[ARRAY_SIZE(nlmsvc_procedures)]);
-
const struct svc_version nlmsvc_version3 = {
.vs_vers = 3,
.vs_nproc = ARRAY_SIZE(nlmsvc_procedures),
.vs_proc = nlmsvc_procedures,
- .vs_count = nlm3svc_call_counters,
.vs_dispatch = nlmsvc_dispatch,
.vs_xdrsize = sizeof(union nlmsvc_xdrstore),
};
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index 4382baddc9ee..eec6040556c9 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -1090,26 +1090,20 @@ static const struct svc_procedure nfs4_callback_procedures1[] = {
}
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nfs4_callback_count1[ARRAY_SIZE(nfs4_callback_procedures1)]);
const struct svc_version nfs4_callback_version1 = {
.vs_vers = 1,
.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
.vs_proc = nfs4_callback_procedures1,
- .vs_count = nfs4_callback_count1,
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
.vs_dispatch = nfs_callback_dispatch,
.vs_hidden = true,
.vs_need_cong_ctrl = true,
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nfs4_callback_count4[ARRAY_SIZE(nfs4_callback_procedures1)]);
const struct svc_version nfs4_callback_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
.vs_proc = nfs4_callback_procedures1,
- .vs_count = nfs4_callback_count4,
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
.vs_dispatch = nfs_callback_dispatch,
.vs_hidden = true,
diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
index c3eb0557b3e1..c458c01e9478 100644
--- a/fs/nfsd/localio.c
+++ b/fs/nfsd/localio.c
@@ -210,14 +210,11 @@ static const struct svc_procedure localio_procedures1[] = {
};
#define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1)
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- localio_count[LOCALIO_NR_PROCEDURES]);
const struct svc_version localio_version1 = {
.vs_vers = 1,
.vs_nproc = LOCALIO_NR_PROCEDURES,
.vs_proc = localio_procedures1,
.vs_dispatch = nfsd_dispatch,
- .vs_count = localio_count,
.vs_xdrsize = XDR_QUADLEN(UUID_SIZE),
.vs_hidden = true,
};
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index 827f90194c43..0fd6d9def3b4 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -389,13 +389,10 @@ static const struct svc_procedure nfsd_acl_procedures2[5] = {
},
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)]);
const struct svc_version nfsd_acl_version2 = {
.vs_vers = 2,
.vs_nproc = ARRAY_SIZE(nfsd_acl_procedures2),
.vs_proc = nfsd_acl_procedures2,
- .vs_count = nfsd_acl_count2,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
};
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index a87f9d7f32be..6b6b289db636 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -278,13 +278,10 @@ static const struct svc_procedure nfsd_acl_procedures3[3] = {
},
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)]);
const struct svc_version nfsd_acl_version3 = {
.vs_vers = 3,
.vs_nproc = ARRAY_SIZE(nfsd_acl_procedures3),
.vs_proc = nfsd_acl_procedures3,
- .vs_count = nfsd_acl_count3,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
};
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index aeda7a802bdf..2d5ab2178702 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -1068,13 +1068,10 @@ static const struct svc_procedure nfsd_procedures3[22] = {
},
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nfsd_count3[ARRAY_SIZE(nfsd_procedures3)]);
const struct svc_version nfsd_version3 = {
.vs_vers = 3,
.vs_nproc = ARRAY_SIZE(nfsd_procedures3),
.vs_proc = nfsd_procedures3,
.vs_dispatch = nfsd_dispatch,
- .vs_count = nfsd_count3,
.vs_xdrsize = NFS3_SVC_XDRSIZE,
};
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 0c37d7c6d28c..3e9819fa6cee 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -4129,13 +4129,10 @@ static const struct svc_procedure nfsd_procedures4[2] = {
},
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nfsd_count4[ARRAY_SIZE(nfsd_procedures4)]);
const struct svc_version nfsd_version4 = {
.vs_vers = 4,
.vs_nproc = ARRAY_SIZE(nfsd_procedures4),
.vs_proc = nfsd_procedures4,
- .vs_count = nfsd_count4,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS4_SVC_XDRSIZE,
.vs_rpcb_optnl = true,
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 8873033d1e82..9efd261fae45 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -838,13 +838,10 @@ static const struct svc_procedure nfsd_procedures2[18] = {
},
};
-static DEFINE_PER_CPU_ALIGNED(unsigned long,
- nfsd_count2[ARRAY_SIZE(nfsd_procedures2)]);
const struct svc_version nfsd_version2 = {
.vs_vers = 2,
.vs_nproc = ARRAY_SIZE(nfsd_procedures2),
.vs_proc = nfsd_procedures2,
- .vs_count = nfsd_count2,
.vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS2_SVC_XDRSIZE,
};
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 4be6204f6630..db9ee1d3a50b 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -408,7 +408,6 @@ struct svc_version {
u32 vs_vers; /* version number */
u32 vs_nproc; /* number of procedures */
const struct svc_procedure *vs_proc; /* per-procedure info */
- unsigned long __percpu *vs_count; /* call counts */
u32 vs_xdrsize; /* xdrsize needed for this version */
/* Don't register with rpcbind */
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 200b57e633dd..61e1c56c3657 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1405,9 +1405,6 @@ svc_generic_init_request(struct svc_rqst *rqstp,
memset(rqstp->rq_argp, 0, procp->pc_argzero);
memset(rqstp->rq_resp, 0, procp->pc_ressize);
- /* Bump per-procedure stats counter */
- this_cpu_inc(versp->vs_count[rqstp->rq_proc]);
-
/* Bump per-net per-procedure stats counter */
if (rqstp->rq_server->sv_stats &&
rqstp->rq_server->sv_stats->program == progp &&
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread