1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/net/sunrpc/stats.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * procfs-based user access to generic RPC statistics. The stats files
61da177e4SLinus Torvalds * reside in /proc/net/rpc.
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * The read routines assume that the buffer passed in is just big enough.
91da177e4SLinus Torvalds * If you implement an RPC service that has its own stats routine which
101da177e4SLinus Torvalds * appends the generic RPC stats, make sure you don't exceed the PAGE_SIZE
111da177e4SLinus Torvalds * limit.
121da177e4SLinus Torvalds *
131da177e4SLinus Torvalds * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
141da177e4SLinus Torvalds */
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds #include <linux/module.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds #include <linux/init.h>
201da177e4SLinus Torvalds #include <linux/kernel.h>
211da177e4SLinus Torvalds #include <linux/proc_fs.h>
221da177e4SLinus Torvalds #include <linux/seq_file.h>
231da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
241da177e4SLinus Torvalds #include <linux/sunrpc/svcsock.h>
2511c556b3SChuck Lever #include <linux/sunrpc/metrics.h>
262446ab60STrond Myklebust #include <linux/rcupdate.h>
274f42d0d5SPavel Emelyanov
2840bf7eb3SChuck Lever #include <trace/events/sunrpc.h>
2940bf7eb3SChuck Lever
304f42d0d5SPavel Emelyanov #include "netns.h"
311da177e4SLinus Torvalds
321da177e4SLinus Torvalds #define RPCDBG_FACILITY RPCDBG_MISC
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds /*
351da177e4SLinus Torvalds * Get RPC client stats
361da177e4SLinus Torvalds */
rpc_proc_show(struct seq_file * seq,void * v)371da177e4SLinus Torvalds static int rpc_proc_show(struct seq_file *seq, void *v) {
381da177e4SLinus Torvalds const struct rpc_stat *statp = seq->private;
391da177e4SLinus Torvalds const struct rpc_program *prog = statp->program;
40ea339d46SChuck Lever unsigned int i, j;
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds seq_printf(seq,
4349e31cbaSMax Kellermann "net %u %u %u %u\n",
441da177e4SLinus Torvalds statp->netcnt,
451da177e4SLinus Torvalds statp->netudpcnt,
461da177e4SLinus Torvalds statp->nettcpcnt,
471da177e4SLinus Torvalds statp->nettcpconn);
481da177e4SLinus Torvalds seq_printf(seq,
4949e31cbaSMax Kellermann "rpc %u %u %u\n",
501da177e4SLinus Torvalds statp->rpccnt,
511da177e4SLinus Torvalds statp->rpcretrans,
521da177e4SLinus Torvalds statp->rpcauthrefresh);
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds for (i = 0; i < prog->nrvers; i++) {
551da177e4SLinus Torvalds const struct rpc_version *vers = prog->version[i];
561da177e4SLinus Torvalds if (!vers)
571da177e4SLinus Torvalds continue;
5849e31cbaSMax Kellermann seq_printf(seq, "proc%u %u",
591da177e4SLinus Torvalds vers->number, vers->nrprocs);
601da177e4SLinus Torvalds for (j = 0; j < vers->nrprocs; j++)
611c5876ddSChristoph Hellwig seq_printf(seq, " %u", vers->counts[j]);
621da177e4SLinus Torvalds seq_putc(seq, '\n');
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds return 0;
651da177e4SLinus Torvalds }
661da177e4SLinus Torvalds
rpc_proc_open(struct inode * inode,struct file * file)671da177e4SLinus Torvalds static int rpc_proc_open(struct inode *inode, struct file *file)
681da177e4SLinus Torvalds {
69359745d7SMuchun Song return single_open(file, rpc_proc_show, pde_data(inode));
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds
7297a32539SAlexey Dobriyan static const struct proc_ops rpc_proc_ops = {
7397a32539SAlexey Dobriyan .proc_open = rpc_proc_open,
7497a32539SAlexey Dobriyan .proc_read = seq_read,
7597a32539SAlexey Dobriyan .proc_lseek = seq_lseek,
7697a32539SAlexey Dobriyan .proc_release = single_release,
771da177e4SLinus Torvalds };
781da177e4SLinus Torvalds
791da177e4SLinus Torvalds /*
801da177e4SLinus Torvalds * Get RPC server stats
811da177e4SLinus Torvalds */
svc_seq_show(struct seq_file * seq,const struct svc_stat * statp)827fd38af9SChristoph Hellwig void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp)
837fd38af9SChristoph Hellwig {
841da177e4SLinus Torvalds const struct svc_program *prog = statp->program;
851da177e4SLinus Torvalds const struct svc_version *vers;
8665ba3d24SChuck Lever unsigned int i, j, k;
8765ba3d24SChuck Lever unsigned long count;
881da177e4SLinus Torvalds
891da177e4SLinus Torvalds seq_printf(seq,
9049e31cbaSMax Kellermann "net %u %u %u %u\n",
911da177e4SLinus Torvalds statp->netcnt,
921da177e4SLinus Torvalds statp->netudpcnt,
931da177e4SLinus Torvalds statp->nettcpcnt,
941da177e4SLinus Torvalds statp->nettcpconn);
951da177e4SLinus Torvalds seq_printf(seq,
9649e31cbaSMax Kellermann "rpc %u %u %u %u %u\n",
971da177e4SLinus Torvalds statp->rpccnt,
981da177e4SLinus Torvalds statp->rpcbadfmt+statp->rpcbadauth+statp->rpcbadclnt,
991da177e4SLinus Torvalds statp->rpcbadfmt,
1001da177e4SLinus Torvalds statp->rpcbadauth,
1011da177e4SLinus Torvalds statp->rpcbadclnt);
1021da177e4SLinus Torvalds
1031da177e4SLinus Torvalds for (i = 0; i < prog->pg_nvers; i++) {
1047fd38af9SChristoph Hellwig vers = prog->pg_vers[i];
1057fd38af9SChristoph Hellwig if (!vers)
1061da177e4SLinus Torvalds continue;
10749e31cbaSMax Kellermann seq_printf(seq, "proc%d %u", i, vers->vs_nproc);
10865ba3d24SChuck Lever for (j = 0; j < vers->vs_nproc; j++) {
10965ba3d24SChuck Lever count = 0;
11065ba3d24SChuck Lever for_each_possible_cpu(k)
11165ba3d24SChuck Lever count += per_cpu(vers->vs_count[j], k);
11265ba3d24SChuck Lever seq_printf(seq, " %lu", count);
11365ba3d24SChuck Lever }
1141da177e4SLinus Torvalds seq_putc(seq, '\n');
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds }
11724c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(svc_seq_show);
1181da177e4SLinus Torvalds
11911c556b3SChuck Lever /**
12011c556b3SChuck Lever * rpc_alloc_iostats - allocate an rpc_iostats structure
12111c556b3SChuck Lever * @clnt: RPC program, version, and xprt
12211c556b3SChuck Lever *
12311c556b3SChuck Lever */
rpc_alloc_iostats(struct rpc_clnt * clnt)12411c556b3SChuck Lever struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt)
12511c556b3SChuck Lever {
126edef1297SChuck Lever struct rpc_iostats *stats;
127edef1297SChuck Lever int i;
128edef1297SChuck Lever
129edef1297SChuck Lever stats = kcalloc(clnt->cl_maxproc, sizeof(*stats), GFP_KERNEL);
130edef1297SChuck Lever if (stats) {
131edef1297SChuck Lever for (i = 0; i < clnt->cl_maxproc; i++)
132edef1297SChuck Lever spin_lock_init(&stats[i].om_lock);
133edef1297SChuck Lever }
134edef1297SChuck Lever return stats;
13511c556b3SChuck Lever }
136e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpc_alloc_iostats);
13711c556b3SChuck Lever
13811c556b3SChuck Lever /**
13911c556b3SChuck Lever * rpc_free_iostats - release an rpc_iostats structure
14011c556b3SChuck Lever * @stats: doomed rpc_iostats structure
14111c556b3SChuck Lever *
14211c556b3SChuck Lever */
rpc_free_iostats(struct rpc_iostats * stats)14311c556b3SChuck Lever void rpc_free_iostats(struct rpc_iostats *stats)
14411c556b3SChuck Lever {
14511c556b3SChuck Lever kfree(stats);
14611c556b3SChuck Lever }
147e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpc_free_iostats);
14811c556b3SChuck Lever
14911c556b3SChuck Lever /**
150840210fcSWeston Andros Adamson * rpc_count_iostats_metrics - tally up per-task stats
15111c556b3SChuck Lever * @task: completed rpc_task
152840210fcSWeston Andros Adamson * @op_metrics: stat structure for OP that will accumulate stats from @task
15311c556b3SChuck Lever */
rpc_count_iostats_metrics(const struct rpc_task * task,struct rpc_iostats * op_metrics)154840210fcSWeston Andros Adamson void rpc_count_iostats_metrics(const struct rpc_task *task,
155840210fcSWeston Andros Adamson struct rpc_iostats *op_metrics)
15611c556b3SChuck Lever {
15711c556b3SChuck Lever struct rpc_rqst *req = task->tk_rqstp;
15840bf7eb3SChuck Lever ktime_t backlog, execute, now;
15911c556b3SChuck Lever
160840210fcSWeston Andros Adamson if (!op_metrics || !req)
16111c556b3SChuck Lever return;
16255ae1aabSRicardo Labiaga
163edef1297SChuck Lever now = ktime_get();
164edef1297SChuck Lever spin_lock(&op_metrics->om_lock);
165edef1297SChuck Lever
16611c556b3SChuck Lever op_metrics->om_ops++;
167ae09531dSChuck Lever /* kernel API: om_ops must never become larger than om_ntrans */
168ae09531dSChuck Lever op_metrics->om_ntrans += max(req->rq_ntrans, 1);
16911c556b3SChuck Lever op_metrics->om_timeouts += task->tk_timeouts;
17011c556b3SChuck Lever
171d60dbb20STrond Myklebust op_metrics->om_bytes_sent += req->rq_xmit_bytes_sent;
172dd2b63d0SRicardo Labiaga op_metrics->om_bytes_recv += req->rq_reply_bytes_recvd;
17311c556b3SChuck Lever
17440bf7eb3SChuck Lever backlog = 0;
175ae09531dSChuck Lever if (ktime_to_ns(req->rq_xtime)) {
17640bf7eb3SChuck Lever backlog = ktime_sub(req->rq_xtime, task->tk_start);
17740bf7eb3SChuck Lever op_metrics->om_queue = ktime_add(op_metrics->om_queue, backlog);
178ae09531dSChuck Lever }
17940bf7eb3SChuck Lever
180d60dbb20STrond Myklebust op_metrics->om_rtt = ktime_add(op_metrics->om_rtt, req->rq_rtt);
18111c556b3SChuck Lever
18240bf7eb3SChuck Lever execute = ktime_sub(now, task->tk_start);
18340bf7eb3SChuck Lever op_metrics->om_execute = ktime_add(op_metrics->om_execute, execute);
184a332518fSDave Wysochanski if (task->tk_status < 0)
185a332518fSDave Wysochanski op_metrics->om_error_status++;
186edef1297SChuck Lever
187edef1297SChuck Lever spin_unlock(&op_metrics->om_lock);
18840bf7eb3SChuck Lever
18940bf7eb3SChuck Lever trace_rpc_stats_latency(req->rq_task, backlog, req->rq_rtt, execute);
19011c556b3SChuck Lever }
191840210fcSWeston Andros Adamson EXPORT_SYMBOL_GPL(rpc_count_iostats_metrics);
192840210fcSWeston Andros Adamson
193840210fcSWeston Andros Adamson /**
194840210fcSWeston Andros Adamson * rpc_count_iostats - tally up per-task stats
195840210fcSWeston Andros Adamson * @task: completed rpc_task
196840210fcSWeston Andros Adamson * @stats: array of stat structures
197840210fcSWeston Andros Adamson *
198840210fcSWeston Andros Adamson * Uses the statidx from @task
199840210fcSWeston Andros Adamson */
rpc_count_iostats(const struct rpc_task * task,struct rpc_iostats * stats)200840210fcSWeston Andros Adamson void rpc_count_iostats(const struct rpc_task *task, struct rpc_iostats *stats)
201840210fcSWeston Andros Adamson {
202840210fcSWeston Andros Adamson rpc_count_iostats_metrics(task,
203840210fcSWeston Andros Adamson &stats[task->tk_msg.rpc_proc->p_statidx]);
204840210fcSWeston Andros Adamson }
2050a702195SWeston Andros Adamson EXPORT_SYMBOL_GPL(rpc_count_iostats);
20611c556b3SChuck Lever
_print_name(struct seq_file * seq,unsigned int op,const struct rpc_procinfo * procs)207ec535ce1SAdrian Bunk static void _print_name(struct seq_file *seq, unsigned int op,
208499b4988SChristoph Hellwig const struct rpc_procinfo *procs)
209cc0175c1SChuck Lever {
210cc0175c1SChuck Lever if (procs[op].p_name)
211cc0175c1SChuck Lever seq_printf(seq, "\t%12s: ", procs[op].p_name);
212cc0175c1SChuck Lever else if (op == 0)
213cc0175c1SChuck Lever seq_printf(seq, "\t NULL: ");
214cc0175c1SChuck Lever else
215cc0175c1SChuck Lever seq_printf(seq, "\t%12u: ", op);
216cc0175c1SChuck Lever }
217cc0175c1SChuck Lever
_add_rpc_iostats(struct rpc_iostats * a,struct rpc_iostats * b)218189e1955SDave Wysochanski static void _add_rpc_iostats(struct rpc_iostats *a, struct rpc_iostats *b)
219189e1955SDave Wysochanski {
220189e1955SDave Wysochanski a->om_ops += b->om_ops;
221189e1955SDave Wysochanski a->om_ntrans += b->om_ntrans;
222189e1955SDave Wysochanski a->om_timeouts += b->om_timeouts;
223189e1955SDave Wysochanski a->om_bytes_sent += b->om_bytes_sent;
224189e1955SDave Wysochanski a->om_bytes_recv += b->om_bytes_recv;
225189e1955SDave Wysochanski a->om_queue = ktime_add(a->om_queue, b->om_queue);
226189e1955SDave Wysochanski a->om_rtt = ktime_add(a->om_rtt, b->om_rtt);
227189e1955SDave Wysochanski a->om_execute = ktime_add(a->om_execute, b->om_execute);
228a332518fSDave Wysochanski a->om_error_status += b->om_error_status;
229189e1955SDave Wysochanski }
230189e1955SDave Wysochanski
_print_rpc_iostats(struct seq_file * seq,struct rpc_iostats * stats,int op,const struct rpc_procinfo * procs)231acdce5fbSDave Wysochanski static void _print_rpc_iostats(struct seq_file *seq, struct rpc_iostats *stats,
232acdce5fbSDave Wysochanski int op, const struct rpc_procinfo *procs)
233acdce5fbSDave Wysochanski {
234acdce5fbSDave Wysochanski _print_name(seq, op, procs);
235a332518fSDave Wysochanski seq_printf(seq, "%lu %lu %lu %llu %llu %llu %llu %llu %lu\n",
236acdce5fbSDave Wysochanski stats->om_ops,
237acdce5fbSDave Wysochanski stats->om_ntrans,
238acdce5fbSDave Wysochanski stats->om_timeouts,
239acdce5fbSDave Wysochanski stats->om_bytes_sent,
240acdce5fbSDave Wysochanski stats->om_bytes_recv,
241acdce5fbSDave Wysochanski ktime_to_ms(stats->om_queue),
242acdce5fbSDave Wysochanski ktime_to_ms(stats->om_rtt),
243a332518fSDave Wysochanski ktime_to_ms(stats->om_execute),
244a332518fSDave Wysochanski stats->om_error_status);
245acdce5fbSDave Wysochanski }
246acdce5fbSDave Wysochanski
do_print_stats(struct rpc_clnt * clnt,struct rpc_xprt * xprt,void * seqv)24710db5691SNeilBrown static int do_print_stats(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *seqv)
24810db5691SNeilBrown {
24910db5691SNeilBrown struct seq_file *seq = seqv;
25010db5691SNeilBrown
25110db5691SNeilBrown xprt->ops->print_stats(xprt, seq);
25210db5691SNeilBrown return 0;
25310db5691SNeilBrown }
25410db5691SNeilBrown
rpc_clnt_show_stats(struct seq_file * seq,struct rpc_clnt * clnt)255016583d7SDave Wysochanski void rpc_clnt_show_stats(struct seq_file *seq, struct rpc_clnt *clnt)
25611c556b3SChuck Lever {
25711c556b3SChuck Lever unsigned int op, maxproc = clnt->cl_maxproc;
25811c556b3SChuck Lever
259016583d7SDave Wysochanski if (!clnt->cl_metrics)
26011c556b3SChuck Lever return;
26111c556b3SChuck Lever
26211c556b3SChuck Lever seq_printf(seq, "\tRPC iostats version: %s ", RPC_IOSTATS_VERS);
26311c556b3SChuck Lever seq_printf(seq, "p/v: %u/%u (%s)\n",
26455909f21STrond Myklebust clnt->cl_prog, clnt->cl_vers, clnt->cl_program->name);
26511c556b3SChuck Lever
26610db5691SNeilBrown rpc_clnt_iterate_for_each_xprt(clnt, do_print_stats, seq);
26711c556b3SChuck Lever
26811c556b3SChuck Lever seq_printf(seq, "\tper-op statistics\n");
26911c556b3SChuck Lever for (op = 0; op < maxproc; op++) {
270016583d7SDave Wysochanski struct rpc_iostats stats = {};
271016583d7SDave Wysochanski struct rpc_clnt *next = clnt;
272016583d7SDave Wysochanski do {
273016583d7SDave Wysochanski _add_rpc_iostats(&stats, &next->cl_metrics[op]);
274016583d7SDave Wysochanski if (next == next->cl_parent)
275016583d7SDave Wysochanski break;
276016583d7SDave Wysochanski next = next->cl_parent;
277016583d7SDave Wysochanski } while (next);
278016583d7SDave Wysochanski _print_rpc_iostats(seq, &stats, op, clnt->cl_procinfo);
27911c556b3SChuck Lever }
28011c556b3SChuck Lever }
281016583d7SDave Wysochanski EXPORT_SYMBOL_GPL(rpc_clnt_show_stats);
28211c556b3SChuck Lever
2831da177e4SLinus Torvalds /*
2841da177e4SLinus Torvalds * Register/unregister RPC proc files
2851da177e4SLinus Torvalds */
2861da177e4SLinus Torvalds static inline struct proc_dir_entry *
do_register(struct net * net,const char * name,void * data,const struct proc_ops * proc_ops)287ec7652aaSStanislav Kinsbursky do_register(struct net *net, const char *name, void *data,
28897a32539SAlexey Dobriyan const struct proc_ops *proc_ops)
2891da177e4SLinus Torvalds {
2904f42d0d5SPavel Emelyanov struct sunrpc_net *sn;
2911da177e4SLinus Torvalds
2924f42d0d5SPavel Emelyanov dprintk("RPC: registering /proc/net/rpc/%s\n", name);
293ec7652aaSStanislav Kinsbursky sn = net_generic(net, sunrpc_net_id);
29497a32539SAlexey Dobriyan return proc_create_data(name, 0, sn->proc_net_rpc, proc_ops, data);
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds
2971da177e4SLinus Torvalds struct proc_dir_entry *
rpc_proc_register(struct net * net,struct rpc_stat * statp)298ec7652aaSStanislav Kinsbursky rpc_proc_register(struct net *net, struct rpc_stat *statp)
2991da177e4SLinus Torvalds {
30097a32539SAlexey Dobriyan return do_register(net, statp->program->name, statp, &rpc_proc_ops);
3011da177e4SLinus Torvalds }
302e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpc_proc_register);
3031da177e4SLinus Torvalds
3041da177e4SLinus Torvalds void
rpc_proc_unregister(struct net * net,const char * name)305ec7652aaSStanislav Kinsbursky rpc_proc_unregister(struct net *net, const char *name)
3061da177e4SLinus Torvalds {
3074f42d0d5SPavel Emelyanov struct sunrpc_net *sn;
3084f42d0d5SPavel Emelyanov
309ec7652aaSStanislav Kinsbursky sn = net_generic(net, sunrpc_net_id);
3104f42d0d5SPavel Emelyanov remove_proc_entry(name, sn->proc_net_rpc);
3111da177e4SLinus Torvalds }
312e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpc_proc_unregister);
3131da177e4SLinus Torvalds
3141da177e4SLinus Torvalds struct proc_dir_entry *
svc_proc_register(struct net * net,struct svc_stat * statp,const struct proc_ops * proc_ops)31597a32539SAlexey Dobriyan svc_proc_register(struct net *net, struct svc_stat *statp, const struct proc_ops *proc_ops)
3161da177e4SLinus Torvalds {
317*2e8076dfSJosef Bacik return do_register(net, statp->program->pg_name, net, proc_ops);
3181da177e4SLinus Torvalds }
31924c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(svc_proc_register);
3201da177e4SLinus Torvalds
3211da177e4SLinus Torvalds void
svc_proc_unregister(struct net * net,const char * name)322246590f5SStanislav Kinsbursky svc_proc_unregister(struct net *net, const char *name)
3231da177e4SLinus Torvalds {
3244f42d0d5SPavel Emelyanov struct sunrpc_net *sn;
3254f42d0d5SPavel Emelyanov
326246590f5SStanislav Kinsbursky sn = net_generic(net, sunrpc_net_id);
3274f42d0d5SPavel Emelyanov remove_proc_entry(name, sn->proc_net_rpc);
3281da177e4SLinus Torvalds }
32924c3767eSTrond Myklebust EXPORT_SYMBOL_GPL(svc_proc_unregister);
3301da177e4SLinus Torvalds
rpc_proc_init(struct net * net)3314f42d0d5SPavel Emelyanov int rpc_proc_init(struct net *net)
3321da177e4SLinus Torvalds {
3334f42d0d5SPavel Emelyanov struct sunrpc_net *sn;
3344f42d0d5SPavel Emelyanov
3351da177e4SLinus Torvalds dprintk("RPC: registering /proc/net/rpc\n");
3364f42d0d5SPavel Emelyanov sn = net_generic(net, sunrpc_net_id);
3374f42d0d5SPavel Emelyanov sn->proc_net_rpc = proc_mkdir("rpc", net->proc_net);
3384f42d0d5SPavel Emelyanov if (sn->proc_net_rpc == NULL)
3394f42d0d5SPavel Emelyanov return -ENOMEM;
3404f42d0d5SPavel Emelyanov
3414f42d0d5SPavel Emelyanov return 0;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds
rpc_proc_exit(struct net * net)3444f42d0d5SPavel Emelyanov void rpc_proc_exit(struct net *net)
3451da177e4SLinus Torvalds {
3461da177e4SLinus Torvalds dprintk("RPC: unregistering /proc/net/rpc\n");
3474f42d0d5SPavel Emelyanov remove_proc_entry("rpc", net->proc_net);
3481da177e4SLinus Torvalds }
349