18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * debugfs interface for sunrpc
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (c) 2014 Jeff Layton <jlayton@primarydata.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
98c2ecf20Sopenharmony_ci#include <linux/sunrpc/sched.h>
108c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h>
118c2ecf20Sopenharmony_ci#include "netns.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic struct dentry *topdir;
148c2ecf20Sopenharmony_cistatic struct dentry *rpc_clnt_dir;
158c2ecf20Sopenharmony_cistatic struct dentry *rpc_xprt_dir;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ciunsigned int rpc_inject_disconnect;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int
208c2ecf20Sopenharmony_citasks_show(struct seq_file *f, void *v)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	u32 xid = 0;
238c2ecf20Sopenharmony_ci	struct rpc_task *task = v;
248c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = task->tk_client;
258c2ecf20Sopenharmony_ci	const char *rpc_waitq = "none";
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	if (RPC_IS_QUEUED(task))
288c2ecf20Sopenharmony_ci		rpc_waitq = rpc_qname(task->tk_waitqueue);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (task->tk_rqstp)
318c2ecf20Sopenharmony_ci		xid = be32_to_cpu(task->tk_rqstp->rq_xid);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
348c2ecf20Sopenharmony_ci		task->tk_pid, task->tk_flags, task->tk_status,
358c2ecf20Sopenharmony_ci		clnt->cl_clid, xid, rpc_task_timeout(task), task->tk_ops,
368c2ecf20Sopenharmony_ci		clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
378c2ecf20Sopenharmony_ci		task->tk_action, rpc_waitq);
388c2ecf20Sopenharmony_ci	return 0;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void *
428c2ecf20Sopenharmony_citasks_start(struct seq_file *f, loff_t *ppos)
438c2ecf20Sopenharmony_ci	__acquires(&clnt->cl_lock)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = f->private;
468c2ecf20Sopenharmony_ci	loff_t pos = *ppos;
478c2ecf20Sopenharmony_ci	struct rpc_task *task;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	spin_lock(&clnt->cl_lock);
508c2ecf20Sopenharmony_ci	list_for_each_entry(task, &clnt->cl_tasks, tk_task)
518c2ecf20Sopenharmony_ci		if (pos-- == 0)
528c2ecf20Sopenharmony_ci			return task;
538c2ecf20Sopenharmony_ci	return NULL;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void *
578c2ecf20Sopenharmony_citasks_next(struct seq_file *f, void *v, loff_t *pos)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = f->private;
608c2ecf20Sopenharmony_ci	struct rpc_task *task = v;
618c2ecf20Sopenharmony_ci	struct list_head *next = task->tk_task.next;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	++*pos;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* If there's another task on list, return it */
668c2ecf20Sopenharmony_ci	if (next == &clnt->cl_tasks)
678c2ecf20Sopenharmony_ci		return NULL;
688c2ecf20Sopenharmony_ci	return list_entry(next, struct rpc_task, tk_task);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void
728c2ecf20Sopenharmony_citasks_stop(struct seq_file *f, void *v)
738c2ecf20Sopenharmony_ci	__releases(&clnt->cl_lock)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = f->private;
768c2ecf20Sopenharmony_ci	spin_unlock(&clnt->cl_lock);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic const struct seq_operations tasks_seq_operations = {
808c2ecf20Sopenharmony_ci	.start	= tasks_start,
818c2ecf20Sopenharmony_ci	.next	= tasks_next,
828c2ecf20Sopenharmony_ci	.stop	= tasks_stop,
838c2ecf20Sopenharmony_ci	.show	= tasks_show,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int tasks_open(struct inode *inode, struct file *filp)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int ret = seq_open(filp, &tasks_seq_operations);
898c2ecf20Sopenharmony_ci	if (!ret) {
908c2ecf20Sopenharmony_ci		struct seq_file *seq = filp->private_data;
918c2ecf20Sopenharmony_ci		struct rpc_clnt *clnt = seq->private = inode->i_private;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		if (!atomic_inc_not_zero(&clnt->cl_count)) {
948c2ecf20Sopenharmony_ci			seq_release(inode, filp);
958c2ecf20Sopenharmony_ci			ret = -EINVAL;
968c2ecf20Sopenharmony_ci		}
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return ret;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int
1038c2ecf20Sopenharmony_citasks_release(struct inode *inode, struct file *filp)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct seq_file *seq = filp->private_data;
1068c2ecf20Sopenharmony_ci	struct rpc_clnt *clnt = seq->private;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	rpc_release_client(clnt);
1098c2ecf20Sopenharmony_ci	return seq_release(inode, filp);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic const struct file_operations tasks_fops = {
1138c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1148c2ecf20Sopenharmony_ci	.open		= tasks_open,
1158c2ecf20Sopenharmony_ci	.read		= seq_read,
1168c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
1178c2ecf20Sopenharmony_ci	.release	= tasks_release,
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int do_xprt_debugfs(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *numv)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	int len;
1238c2ecf20Sopenharmony_ci	char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
1248c2ecf20Sopenharmony_ci	char link[9]; /* enough for 8 hex digits + NULL */
1258c2ecf20Sopenharmony_ci	int *nump = numv;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(xprt->debugfs))
1288c2ecf20Sopenharmony_ci		return 0;
1298c2ecf20Sopenharmony_ci	len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
1308c2ecf20Sopenharmony_ci		       xprt->debugfs->d_name.name);
1318c2ecf20Sopenharmony_ci	if (len >= sizeof(name))
1328c2ecf20Sopenharmony_ci		return -1;
1338c2ecf20Sopenharmony_ci	if (*nump == 0)
1348c2ecf20Sopenharmony_ci		strcpy(link, "xprt");
1358c2ecf20Sopenharmony_ci	else {
1368c2ecf20Sopenharmony_ci		len = snprintf(link, sizeof(link), "xprt%d", *nump);
1378c2ecf20Sopenharmony_ci		if (len >= sizeof(link))
1388c2ecf20Sopenharmony_ci			return -1;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	debugfs_create_symlink(link, clnt->cl_debugfs, name);
1418c2ecf20Sopenharmony_ci	(*nump)++;
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_civoid
1468c2ecf20Sopenharmony_cirpc_clnt_debugfs_register(struct rpc_clnt *clnt)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int len;
1498c2ecf20Sopenharmony_ci	char name[9]; /* enough for 8 hex digits + NULL */
1508c2ecf20Sopenharmony_ci	int xprtnum = 0;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
1538c2ecf20Sopenharmony_ci	if (len >= sizeof(name))
1548c2ecf20Sopenharmony_ci		return;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* make the per-client dir */
1578c2ecf20Sopenharmony_ci	clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* make tasks file */
1608c2ecf20Sopenharmony_ci	debugfs_create_file("tasks", S_IFREG | 0400, clnt->cl_debugfs, clnt,
1618c2ecf20Sopenharmony_ci			    &tasks_fops);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	rpc_clnt_iterate_for_each_xprt(clnt, do_xprt_debugfs, &xprtnum);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_civoid
1678c2ecf20Sopenharmony_cirpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	debugfs_remove_recursive(clnt->cl_debugfs);
1708c2ecf20Sopenharmony_ci	clnt->cl_debugfs = NULL;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int
1748c2ecf20Sopenharmony_cixprt_info_show(struct seq_file *f, void *v)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct rpc_xprt *xprt = f->private;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
1798c2ecf20Sopenharmony_ci	seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
1808c2ecf20Sopenharmony_ci	seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
1818c2ecf20Sopenharmony_ci	seq_printf(f, "state: 0x%lx\n", xprt->state);
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int
1868c2ecf20Sopenharmony_cixprt_info_open(struct inode *inode, struct file *filp)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int ret;
1898c2ecf20Sopenharmony_ci	struct rpc_xprt *xprt = inode->i_private;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ret = single_open(filp, xprt_info_show, xprt);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (!ret) {
1948c2ecf20Sopenharmony_ci		if (!xprt_get(xprt)) {
1958c2ecf20Sopenharmony_ci			single_release(inode, filp);
1968c2ecf20Sopenharmony_ci			ret = -EINVAL;
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	return ret;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int
2038c2ecf20Sopenharmony_cixprt_info_release(struct inode *inode, struct file *filp)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct rpc_xprt *xprt = inode->i_private;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	xprt_put(xprt);
2088c2ecf20Sopenharmony_ci	return single_release(inode, filp);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic const struct file_operations xprt_info_fops = {
2128c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2138c2ecf20Sopenharmony_ci	.open		= xprt_info_open,
2148c2ecf20Sopenharmony_ci	.read		= seq_read,
2158c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
2168c2ecf20Sopenharmony_ci	.release	= xprt_info_release,
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_civoid
2208c2ecf20Sopenharmony_cirpc_xprt_debugfs_register(struct rpc_xprt *xprt)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	int len, id;
2238c2ecf20Sopenharmony_ci	static atomic_t	cur_id;
2248c2ecf20Sopenharmony_ci	char		name[9]; /* 8 hex digits + NULL term */
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	id = (unsigned int)atomic_inc_return(&cur_id);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	len = snprintf(name, sizeof(name), "%x", id);
2298c2ecf20Sopenharmony_ci	if (len >= sizeof(name))
2308c2ecf20Sopenharmony_ci		return;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* make the per-client dir */
2338c2ecf20Sopenharmony_ci	xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* make tasks file */
2368c2ecf20Sopenharmony_ci	debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt,
2378c2ecf20Sopenharmony_ci			    &xprt_info_fops);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_civoid
2438c2ecf20Sopenharmony_cirpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	debugfs_remove_recursive(xprt->debugfs);
2468c2ecf20Sopenharmony_ci	xprt->debugfs = NULL;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int
2508c2ecf20Sopenharmony_cifault_open(struct inode *inode, struct file *filp)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	filp->private_data = kmalloc(128, GFP_KERNEL);
2538c2ecf20Sopenharmony_ci	if (!filp->private_data)
2548c2ecf20Sopenharmony_ci		return -ENOMEM;
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int
2598c2ecf20Sopenharmony_cifault_release(struct inode *inode, struct file *filp)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	kfree(filp->private_data);
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic ssize_t
2668c2ecf20Sopenharmony_cifault_disconnect_read(struct file *filp, char __user *user_buf,
2678c2ecf20Sopenharmony_ci		      size_t len, loff_t *offset)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	char *buffer = (char *)filp->private_data;
2708c2ecf20Sopenharmony_ci	size_t size;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	size = sprintf(buffer, "%u\n", rpc_inject_disconnect);
2738c2ecf20Sopenharmony_ci	return simple_read_from_buffer(user_buf, len, offset, buffer, size);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic ssize_t
2778c2ecf20Sopenharmony_cifault_disconnect_write(struct file *filp, const char __user *user_buf,
2788c2ecf20Sopenharmony_ci		       size_t len, loff_t *offset)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	char buffer[16];
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (len >= sizeof(buffer))
2838c2ecf20Sopenharmony_ci		len = sizeof(buffer) - 1;
2848c2ecf20Sopenharmony_ci	if (copy_from_user(buffer, user_buf, len))
2858c2ecf20Sopenharmony_ci		return -EFAULT;
2868c2ecf20Sopenharmony_ci	buffer[len] = '\0';
2878c2ecf20Sopenharmony_ci	if (kstrtouint(buffer, 10, &rpc_inject_disconnect))
2888c2ecf20Sopenharmony_ci		return -EINVAL;
2898c2ecf20Sopenharmony_ci	return len;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic const struct file_operations fault_disconnect_fops = {
2938c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2948c2ecf20Sopenharmony_ci	.open		= fault_open,
2958c2ecf20Sopenharmony_ci	.read		= fault_disconnect_read,
2968c2ecf20Sopenharmony_ci	.write		= fault_disconnect_write,
2978c2ecf20Sopenharmony_ci	.release	= fault_release,
2988c2ecf20Sopenharmony_ci};
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_civoid __exit
3018c2ecf20Sopenharmony_cisunrpc_debugfs_exit(void)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	debugfs_remove_recursive(topdir);
3048c2ecf20Sopenharmony_ci	topdir = NULL;
3058c2ecf20Sopenharmony_ci	rpc_clnt_dir = NULL;
3068c2ecf20Sopenharmony_ci	rpc_xprt_dir = NULL;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_civoid __init
3108c2ecf20Sopenharmony_cisunrpc_debugfs_init(void)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct dentry *rpc_fault_dir;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	topdir = debugfs_create_dir("sunrpc", NULL);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	rpc_fault_dir = debugfs_create_dir("inject_fault", topdir);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	debugfs_create_file("disconnect", S_IFREG | 0400, rpc_fault_dir, NULL,
3238c2ecf20Sopenharmony_ci			    &fault_disconnect_fops);
3248c2ecf20Sopenharmony_ci}
325