162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/net/sunrpc/stats.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * procfs-based user access to generic RPC statistics. The stats files
662306a36Sopenharmony_ci * reside in /proc/net/rpc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * The read routines assume that the buffer passed in is just big enough.
962306a36Sopenharmony_ci * If you implement an RPC service that has its own stats routine which
1062306a36Sopenharmony_ci * appends the generic RPC stats, make sure you don't exceed the PAGE_SIZE
1162306a36Sopenharmony_ci * limit.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/proc_fs.h>
2262306a36Sopenharmony_ci#include <linux/seq_file.h>
2362306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
2462306a36Sopenharmony_ci#include <linux/sunrpc/svcsock.h>
2562306a36Sopenharmony_ci#include <linux/sunrpc/metrics.h>
2662306a36Sopenharmony_ci#include <linux/rcupdate.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <trace/events/sunrpc.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "netns.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define RPCDBG_FACILITY	RPCDBG_MISC
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * Get RPC client stats
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic int rpc_proc_show(struct seq_file *seq, void *v) {
3862306a36Sopenharmony_ci	const struct rpc_stat	*statp = seq->private;
3962306a36Sopenharmony_ci	const struct rpc_program *prog = statp->program;
4062306a36Sopenharmony_ci	unsigned int i, j;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	seq_printf(seq,
4362306a36Sopenharmony_ci		"net %u %u %u %u\n",
4462306a36Sopenharmony_ci			statp->netcnt,
4562306a36Sopenharmony_ci			statp->netudpcnt,
4662306a36Sopenharmony_ci			statp->nettcpcnt,
4762306a36Sopenharmony_ci			statp->nettcpconn);
4862306a36Sopenharmony_ci	seq_printf(seq,
4962306a36Sopenharmony_ci		"rpc %u %u %u\n",
5062306a36Sopenharmony_ci			statp->rpccnt,
5162306a36Sopenharmony_ci			statp->rpcretrans,
5262306a36Sopenharmony_ci			statp->rpcauthrefresh);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < prog->nrvers; i++) {
5562306a36Sopenharmony_ci		const struct rpc_version *vers = prog->version[i];
5662306a36Sopenharmony_ci		if (!vers)
5762306a36Sopenharmony_ci			continue;
5862306a36Sopenharmony_ci		seq_printf(seq, "proc%u %u",
5962306a36Sopenharmony_ci					vers->number, vers->nrprocs);
6062306a36Sopenharmony_ci		for (j = 0; j < vers->nrprocs; j++)
6162306a36Sopenharmony_ci			seq_printf(seq, " %u", vers->counts[j]);
6262306a36Sopenharmony_ci		seq_putc(seq, '\n');
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int rpc_proc_open(struct inode *inode, struct file *file)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	return single_open(file, rpc_proc_show, pde_data(inode));
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic const struct proc_ops rpc_proc_ops = {
7362306a36Sopenharmony_ci	.proc_open	= rpc_proc_open,
7462306a36Sopenharmony_ci	.proc_read	= seq_read,
7562306a36Sopenharmony_ci	.proc_lseek	= seq_lseek,
7662306a36Sopenharmony_ci	.proc_release	= single_release,
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * Get RPC server stats
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_civoid svc_seq_show(struct seq_file *seq, const struct svc_stat *statp)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	const struct svc_program *prog = statp->program;
8562306a36Sopenharmony_ci	const struct svc_version *vers;
8662306a36Sopenharmony_ci	unsigned int i, j, k;
8762306a36Sopenharmony_ci	unsigned long count;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	seq_printf(seq,
9062306a36Sopenharmony_ci		"net %u %u %u %u\n",
9162306a36Sopenharmony_ci			statp->netcnt,
9262306a36Sopenharmony_ci			statp->netudpcnt,
9362306a36Sopenharmony_ci			statp->nettcpcnt,
9462306a36Sopenharmony_ci			statp->nettcpconn);
9562306a36Sopenharmony_ci	seq_printf(seq,
9662306a36Sopenharmony_ci		"rpc %u %u %u %u %u\n",
9762306a36Sopenharmony_ci			statp->rpccnt,
9862306a36Sopenharmony_ci			statp->rpcbadfmt+statp->rpcbadauth+statp->rpcbadclnt,
9962306a36Sopenharmony_ci			statp->rpcbadfmt,
10062306a36Sopenharmony_ci			statp->rpcbadauth,
10162306a36Sopenharmony_ci			statp->rpcbadclnt);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for (i = 0; i < prog->pg_nvers; i++) {
10462306a36Sopenharmony_ci		vers = prog->pg_vers[i];
10562306a36Sopenharmony_ci		if (!vers)
10662306a36Sopenharmony_ci			continue;
10762306a36Sopenharmony_ci		seq_printf(seq, "proc%d %u", i, vers->vs_nproc);
10862306a36Sopenharmony_ci		for (j = 0; j < vers->vs_nproc; j++) {
10962306a36Sopenharmony_ci			count = 0;
11062306a36Sopenharmony_ci			for_each_possible_cpu(k)
11162306a36Sopenharmony_ci				count += per_cpu(vers->vs_count[j], k);
11262306a36Sopenharmony_ci			seq_printf(seq, " %lu", count);
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci		seq_putc(seq, '\n');
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_seq_show);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * rpc_alloc_iostats - allocate an rpc_iostats structure
12162306a36Sopenharmony_ci * @clnt: RPC program, version, and xprt
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistruct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct rpc_iostats *stats;
12762306a36Sopenharmony_ci	int i;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	stats = kcalloc(clnt->cl_maxproc, sizeof(*stats), GFP_KERNEL);
13062306a36Sopenharmony_ci	if (stats) {
13162306a36Sopenharmony_ci		for (i = 0; i < clnt->cl_maxproc; i++)
13262306a36Sopenharmony_ci			spin_lock_init(&stats[i].om_lock);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	return stats;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_alloc_iostats);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/**
13962306a36Sopenharmony_ci * rpc_free_iostats - release an rpc_iostats structure
14062306a36Sopenharmony_ci * @stats: doomed rpc_iostats structure
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_civoid rpc_free_iostats(struct rpc_iostats *stats)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	kfree(stats);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_free_iostats);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/**
15062306a36Sopenharmony_ci * rpc_count_iostats_metrics - tally up per-task stats
15162306a36Sopenharmony_ci * @task: completed rpc_task
15262306a36Sopenharmony_ci * @op_metrics: stat structure for OP that will accumulate stats from @task
15362306a36Sopenharmony_ci */
15462306a36Sopenharmony_civoid rpc_count_iostats_metrics(const struct rpc_task *task,
15562306a36Sopenharmony_ci			       struct rpc_iostats *op_metrics)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct rpc_rqst *req = task->tk_rqstp;
15862306a36Sopenharmony_ci	ktime_t backlog, execute, now;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (!op_metrics || !req)
16162306a36Sopenharmony_ci		return;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	now = ktime_get();
16462306a36Sopenharmony_ci	spin_lock(&op_metrics->om_lock);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	op_metrics->om_ops++;
16762306a36Sopenharmony_ci	/* kernel API: om_ops must never become larger than om_ntrans */
16862306a36Sopenharmony_ci	op_metrics->om_ntrans += max(req->rq_ntrans, 1);
16962306a36Sopenharmony_ci	op_metrics->om_timeouts += task->tk_timeouts;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	op_metrics->om_bytes_sent += req->rq_xmit_bytes_sent;
17262306a36Sopenharmony_ci	op_metrics->om_bytes_recv += req->rq_reply_bytes_recvd;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	backlog = 0;
17562306a36Sopenharmony_ci	if (ktime_to_ns(req->rq_xtime)) {
17662306a36Sopenharmony_ci		backlog = ktime_sub(req->rq_xtime, task->tk_start);
17762306a36Sopenharmony_ci		op_metrics->om_queue = ktime_add(op_metrics->om_queue, backlog);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	op_metrics->om_rtt = ktime_add(op_metrics->om_rtt, req->rq_rtt);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	execute = ktime_sub(now, task->tk_start);
18362306a36Sopenharmony_ci	op_metrics->om_execute = ktime_add(op_metrics->om_execute, execute);
18462306a36Sopenharmony_ci	if (task->tk_status < 0)
18562306a36Sopenharmony_ci		op_metrics->om_error_status++;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	spin_unlock(&op_metrics->om_lock);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	trace_rpc_stats_latency(req->rq_task, backlog, req->rq_rtt, execute);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_count_iostats_metrics);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/**
19462306a36Sopenharmony_ci * rpc_count_iostats - tally up per-task stats
19562306a36Sopenharmony_ci * @task: completed rpc_task
19662306a36Sopenharmony_ci * @stats: array of stat structures
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * Uses the statidx from @task
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_civoid rpc_count_iostats(const struct rpc_task *task, struct rpc_iostats *stats)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	rpc_count_iostats_metrics(task,
20362306a36Sopenharmony_ci				  &stats[task->tk_msg.rpc_proc->p_statidx]);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_count_iostats);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void _print_name(struct seq_file *seq, unsigned int op,
20862306a36Sopenharmony_ci			const struct rpc_procinfo *procs)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	if (procs[op].p_name)
21162306a36Sopenharmony_ci		seq_printf(seq, "\t%12s: ", procs[op].p_name);
21262306a36Sopenharmony_ci	else if (op == 0)
21362306a36Sopenharmony_ci		seq_printf(seq, "\t        NULL: ");
21462306a36Sopenharmony_ci	else
21562306a36Sopenharmony_ci		seq_printf(seq, "\t%12u: ", op);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void _add_rpc_iostats(struct rpc_iostats *a, struct rpc_iostats *b)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	a->om_ops += b->om_ops;
22162306a36Sopenharmony_ci	a->om_ntrans += b->om_ntrans;
22262306a36Sopenharmony_ci	a->om_timeouts += b->om_timeouts;
22362306a36Sopenharmony_ci	a->om_bytes_sent += b->om_bytes_sent;
22462306a36Sopenharmony_ci	a->om_bytes_recv += b->om_bytes_recv;
22562306a36Sopenharmony_ci	a->om_queue = ktime_add(a->om_queue, b->om_queue);
22662306a36Sopenharmony_ci	a->om_rtt = ktime_add(a->om_rtt, b->om_rtt);
22762306a36Sopenharmony_ci	a->om_execute = ktime_add(a->om_execute, b->om_execute);
22862306a36Sopenharmony_ci	a->om_error_status += b->om_error_status;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void _print_rpc_iostats(struct seq_file *seq, struct rpc_iostats *stats,
23262306a36Sopenharmony_ci			       int op, const struct rpc_procinfo *procs)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	_print_name(seq, op, procs);
23562306a36Sopenharmony_ci	seq_printf(seq, "%lu %lu %lu %llu %llu %llu %llu %llu %lu\n",
23662306a36Sopenharmony_ci		   stats->om_ops,
23762306a36Sopenharmony_ci		   stats->om_ntrans,
23862306a36Sopenharmony_ci		   stats->om_timeouts,
23962306a36Sopenharmony_ci		   stats->om_bytes_sent,
24062306a36Sopenharmony_ci		   stats->om_bytes_recv,
24162306a36Sopenharmony_ci		   ktime_to_ms(stats->om_queue),
24262306a36Sopenharmony_ci		   ktime_to_ms(stats->om_rtt),
24362306a36Sopenharmony_ci		   ktime_to_ms(stats->om_execute),
24462306a36Sopenharmony_ci		   stats->om_error_status);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int do_print_stats(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *seqv)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct seq_file *seq = seqv;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	xprt->ops->print_stats(xprt, seq);
25262306a36Sopenharmony_ci	return 0;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_civoid rpc_clnt_show_stats(struct seq_file *seq, struct rpc_clnt *clnt)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	unsigned int op, maxproc = clnt->cl_maxproc;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (!clnt->cl_metrics)
26062306a36Sopenharmony_ci		return;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	seq_printf(seq, "\tRPC iostats version: %s  ", RPC_IOSTATS_VERS);
26362306a36Sopenharmony_ci	seq_printf(seq, "p/v: %u/%u (%s)\n",
26462306a36Sopenharmony_ci			clnt->cl_prog, clnt->cl_vers, clnt->cl_program->name);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	rpc_clnt_iterate_for_each_xprt(clnt, do_print_stats, seq);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	seq_printf(seq, "\tper-op statistics\n");
26962306a36Sopenharmony_ci	for (op = 0; op < maxproc; op++) {
27062306a36Sopenharmony_ci		struct rpc_iostats stats = {};
27162306a36Sopenharmony_ci		struct rpc_clnt *next = clnt;
27262306a36Sopenharmony_ci		do {
27362306a36Sopenharmony_ci			_add_rpc_iostats(&stats, &next->cl_metrics[op]);
27462306a36Sopenharmony_ci			if (next == next->cl_parent)
27562306a36Sopenharmony_ci				break;
27662306a36Sopenharmony_ci			next = next->cl_parent;
27762306a36Sopenharmony_ci		} while (next);
27862306a36Sopenharmony_ci		_print_rpc_iostats(seq, &stats, op, clnt->cl_procinfo);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_clnt_show_stats);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/*
28462306a36Sopenharmony_ci * Register/unregister RPC proc files
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_cistatic inline struct proc_dir_entry *
28762306a36Sopenharmony_cido_register(struct net *net, const char *name, void *data,
28862306a36Sopenharmony_ci	    const struct proc_ops *proc_ops)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct sunrpc_net *sn;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	dprintk("RPC:       registering /proc/net/rpc/%s\n", name);
29362306a36Sopenharmony_ci	sn = net_generic(net, sunrpc_net_id);
29462306a36Sopenharmony_ci	return proc_create_data(name, 0, sn->proc_net_rpc, proc_ops, data);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistruct proc_dir_entry *
29862306a36Sopenharmony_cirpc_proc_register(struct net *net, struct rpc_stat *statp)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	return do_register(net, statp->program->name, statp, &rpc_proc_ops);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_proc_register);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_civoid
30562306a36Sopenharmony_cirpc_proc_unregister(struct net *net, const char *name)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct sunrpc_net *sn;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	sn = net_generic(net, sunrpc_net_id);
31062306a36Sopenharmony_ci	remove_proc_entry(name, sn->proc_net_rpc);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_proc_unregister);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistruct proc_dir_entry *
31562306a36Sopenharmony_cisvc_proc_register(struct net *net, struct svc_stat *statp, const struct proc_ops *proc_ops)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	return do_register(net, statp->program->pg_name, statp, proc_ops);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_proc_register);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_civoid
32262306a36Sopenharmony_cisvc_proc_unregister(struct net *net, const char *name)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct sunrpc_net *sn;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	sn = net_generic(net, sunrpc_net_id);
32762306a36Sopenharmony_ci	remove_proc_entry(name, sn->proc_net_rpc);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(svc_proc_unregister);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ciint rpc_proc_init(struct net *net)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct sunrpc_net *sn;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	dprintk("RPC:       registering /proc/net/rpc\n");
33662306a36Sopenharmony_ci	sn = net_generic(net, sunrpc_net_id);
33762306a36Sopenharmony_ci	sn->proc_net_rpc = proc_mkdir("rpc", net->proc_net);
33862306a36Sopenharmony_ci	if (sn->proc_net_rpc == NULL)
33962306a36Sopenharmony_ci		return -ENOMEM;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return 0;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_civoid rpc_proc_exit(struct net *net)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	dprintk("RPC:       unregistering /proc/net/rpc\n");
34762306a36Sopenharmony_ci	remove_proc_entry("rpc", net->proc_net);
34862306a36Sopenharmony_ci}
349