[PATCH v7 1/6] sunrpc: add per-netns per-procedure call counts to svc_stat
Jeff Layton <[email protected]> Fri, 17 Jul 2026 07:08:53 -0400
| Newsgroups | gmane.linux.nfs,gmane.linux.kernel |
|---|---|
| Message-ID | <[email protected]> |
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: LLM Signed-off-by: Jeff Layton <[email protected]> --- fs/nfsd/nfsctl.c | 8 +++++- include/linux/sunrpc/stats.h | 6 +++++ net/sunrpc/svc.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 7e1d5a5d6511..ae5a7f1ad917 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2516,9 +2516,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++) @@ -2536,6 +2539,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: @@ -2576,6 +2581,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/svc.c b/net/sunrpc/svc.c index d59e078f39d6..aef6406bab65 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1348,6 +1348,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: @@ -1359,6 +1367,60 @@ svc_generic_init_request(struct svc_rqst *rqstp, } EXPORT_SYMBOL_GPL(svc_generic_init_request); +/** + * 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); + /* * Common routine for processing the RPC request. */ -- 2.55.0