xref: /openbmc/linux/net/sunrpc/debugfs.c (revision 37324e6b)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b6580ab3STrond Myklebust /*
3b4b9d2ccSJeff Layton  * debugfs interface for sunrpc
4b4b9d2ccSJeff Layton  *
5b4b9d2ccSJeff Layton  * (c) 2014 Jeff Layton <jlayton@primarydata.com>
6b4b9d2ccSJeff Layton  */
7b4b9d2ccSJeff Layton 
8b4b9d2ccSJeff Layton #include <linux/debugfs.h>
9b4b9d2ccSJeff Layton #include <linux/sunrpc/sched.h>
10b4b9d2ccSJeff Layton #include <linux/sunrpc/clnt.h>
11c782af25SChuck Lever 
12b4b9d2ccSJeff Layton #include "netns.h"
13c782af25SChuck Lever #include "fail.h"
14b4b9d2ccSJeff Layton 
15b4b9d2ccSJeff Layton static struct dentry *topdir;
16b4b9d2ccSJeff Layton static struct dentry *rpc_clnt_dir;
17388f0c77SJeff Layton static struct dentry *rpc_xprt_dir;
18b4b9d2ccSJeff Layton 
19b4b9d2ccSJeff Layton static int
tasks_show(struct seq_file * f,void * v)20b4b9d2ccSJeff Layton tasks_show(struct seq_file *f, void *v)
21b4b9d2ccSJeff Layton {
22b4b9d2ccSJeff Layton 	u32 xid = 0;
23b4b9d2ccSJeff Layton 	struct rpc_task *task = v;
24b4b9d2ccSJeff Layton 	struct rpc_clnt *clnt = task->tk_client;
25b4b9d2ccSJeff Layton 	const char *rpc_waitq = "none";
26b4b9d2ccSJeff Layton 
27b4b9d2ccSJeff Layton 	if (RPC_IS_QUEUED(task))
28b4b9d2ccSJeff Layton 		rpc_waitq = rpc_qname(task->tk_waitqueue);
29b4b9d2ccSJeff Layton 
30b4b9d2ccSJeff Layton 	if (task->tk_rqstp)
31b4b9d2ccSJeff Layton 		xid = be32_to_cpu(task->tk_rqstp->rq_xid);
32b4b9d2ccSJeff Layton 
33b4b9d2ccSJeff Layton 	seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
34b4b9d2ccSJeff Layton 		task->tk_pid, task->tk_flags, task->tk_status,
355efd1876STrond Myklebust 		clnt->cl_clid, xid, rpc_task_timeout(task), task->tk_ops,
36b4b9d2ccSJeff Layton 		clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
37b4b9d2ccSJeff Layton 		task->tk_action, rpc_waitq);
38b4b9d2ccSJeff Layton 	return 0;
39b4b9d2ccSJeff Layton }
40b4b9d2ccSJeff Layton 
41b4b9d2ccSJeff Layton static void *
tasks_start(struct seq_file * f,loff_t * ppos)42b4b9d2ccSJeff Layton tasks_start(struct seq_file *f, loff_t *ppos)
43b4b9d2ccSJeff Layton 	__acquires(&clnt->cl_lock)
44b4b9d2ccSJeff Layton {
453f373e81SKinglong Mee 	struct rpc_clnt *clnt = f->private;
46b4b9d2ccSJeff Layton 	loff_t pos = *ppos;
47b4b9d2ccSJeff Layton 	struct rpc_task *task;
48b4b9d2ccSJeff Layton 
49b4b9d2ccSJeff Layton 	spin_lock(&clnt->cl_lock);
50b4b9d2ccSJeff Layton 	list_for_each_entry(task, &clnt->cl_tasks, tk_task)
51b4b9d2ccSJeff Layton 		if (pos-- == 0)
52b4b9d2ccSJeff Layton 			return task;
53b4b9d2ccSJeff Layton 	return NULL;
54b4b9d2ccSJeff Layton }
55b4b9d2ccSJeff Layton 
56b4b9d2ccSJeff Layton static void *
tasks_next(struct seq_file * f,void * v,loff_t * pos)57b4b9d2ccSJeff Layton tasks_next(struct seq_file *f, void *v, loff_t *pos)
58b4b9d2ccSJeff Layton {
593f373e81SKinglong Mee 	struct rpc_clnt *clnt = f->private;
60b4b9d2ccSJeff Layton 	struct rpc_task *task = v;
61b4b9d2ccSJeff Layton 	struct list_head *next = task->tk_task.next;
62b4b9d2ccSJeff Layton 
63b4b9d2ccSJeff Layton 	++*pos;
64b4b9d2ccSJeff Layton 
65b4b9d2ccSJeff Layton 	/* If there's another task on list, return it */
66b4b9d2ccSJeff Layton 	if (next == &clnt->cl_tasks)
67b4b9d2ccSJeff Layton 		return NULL;
68b4b9d2ccSJeff Layton 	return list_entry(next, struct rpc_task, tk_task);
69b4b9d2ccSJeff Layton }
70b4b9d2ccSJeff Layton 
71b4b9d2ccSJeff Layton static void
tasks_stop(struct seq_file * f,void * v)72b4b9d2ccSJeff Layton tasks_stop(struct seq_file *f, void *v)
73b4b9d2ccSJeff Layton 	__releases(&clnt->cl_lock)
74b4b9d2ccSJeff Layton {
753f373e81SKinglong Mee 	struct rpc_clnt *clnt = f->private;
76b4b9d2ccSJeff Layton 	spin_unlock(&clnt->cl_lock);
77b4b9d2ccSJeff Layton }
78b4b9d2ccSJeff Layton 
79b4b9d2ccSJeff Layton static const struct seq_operations tasks_seq_operations = {
80b4b9d2ccSJeff Layton 	.start	= tasks_start,
81b4b9d2ccSJeff Layton 	.next	= tasks_next,
82b4b9d2ccSJeff Layton 	.stop	= tasks_stop,
83b4b9d2ccSJeff Layton 	.show	= tasks_show,
84b4b9d2ccSJeff Layton };
85b4b9d2ccSJeff Layton 
tasks_open(struct inode * inode,struct file * filp)86b4b9d2ccSJeff Layton static int tasks_open(struct inode *inode, struct file *filp)
87b4b9d2ccSJeff Layton {
883f373e81SKinglong Mee 	int ret = seq_open(filp, &tasks_seq_operations);
89b4b9d2ccSJeff Layton 	if (!ret) {
90b4b9d2ccSJeff Layton 		struct seq_file *seq = filp->private_data;
913f373e81SKinglong Mee 		struct rpc_clnt *clnt = seq->private = inode->i_private;
92b4b9d2ccSJeff Layton 
9371d3d0ebSTrond Myklebust 		if (!refcount_inc_not_zero(&clnt->cl_count)) {
943f373e81SKinglong Mee 			seq_release(inode, filp);
95b4b9d2ccSJeff Layton 			ret = -EINVAL;
96b4b9d2ccSJeff Layton 		}
97b4b9d2ccSJeff Layton 	}
98b4b9d2ccSJeff Layton 
99b4b9d2ccSJeff Layton 	return ret;
100b4b9d2ccSJeff Layton }
101b4b9d2ccSJeff Layton 
102b4b9d2ccSJeff Layton static int
tasks_release(struct inode * inode,struct file * filp)103b4b9d2ccSJeff Layton tasks_release(struct inode *inode, struct file *filp)
104b4b9d2ccSJeff Layton {
105b4b9d2ccSJeff Layton 	struct seq_file *seq = filp->private_data;
1063f373e81SKinglong Mee 	struct rpc_clnt *clnt = seq->private;
107b4b9d2ccSJeff Layton 
1083f373e81SKinglong Mee 	rpc_release_client(clnt);
1093f373e81SKinglong Mee 	return seq_release(inode, filp);
110b4b9d2ccSJeff Layton }
111b4b9d2ccSJeff Layton 
112b4b9d2ccSJeff Layton static const struct file_operations tasks_fops = {
113b4b9d2ccSJeff Layton 	.owner		= THIS_MODULE,
114b4b9d2ccSJeff Layton 	.open		= tasks_open,
115b4b9d2ccSJeff Layton 	.read		= seq_read,
116b4b9d2ccSJeff Layton 	.llseek		= seq_lseek,
117b4b9d2ccSJeff Layton 	.release	= tasks_release,
118b4b9d2ccSJeff Layton };
119b4b9d2ccSJeff Layton 
do_xprt_debugfs(struct rpc_clnt * clnt,struct rpc_xprt * xprt,void * numv)1202f34b8bfSNeilBrown static int do_xprt_debugfs(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *numv)
1212f34b8bfSNeilBrown {
1222f34b8bfSNeilBrown 	int len;
1232f34b8bfSNeilBrown 	char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
1242f34b8bfSNeilBrown 	char link[9]; /* enough for 8 hex digits + NULL */
1252f34b8bfSNeilBrown 	int *nump = numv;
1262f34b8bfSNeilBrown 
1272f34b8bfSNeilBrown 	if (IS_ERR_OR_NULL(xprt->debugfs))
1282f34b8bfSNeilBrown 		return 0;
1292f34b8bfSNeilBrown 	len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
1302f34b8bfSNeilBrown 		       xprt->debugfs->d_name.name);
13135a6d396SFedor Tokarev 	if (len >= sizeof(name))
1322f34b8bfSNeilBrown 		return -1;
1332f34b8bfSNeilBrown 	if (*nump == 0)
1342f34b8bfSNeilBrown 		strcpy(link, "xprt");
1352f34b8bfSNeilBrown 	else {
1362f34b8bfSNeilBrown 		len = snprintf(link, sizeof(link), "xprt%d", *nump);
13735a6d396SFedor Tokarev 		if (len >= sizeof(link))
1382f34b8bfSNeilBrown 			return -1;
1392f34b8bfSNeilBrown 	}
1406860c981SLinus Torvalds 	debugfs_create_symlink(link, clnt->cl_debugfs, name);
1412f34b8bfSNeilBrown 	(*nump)++;
1422f34b8bfSNeilBrown 	return 0;
1432f34b8bfSNeilBrown }
1442f34b8bfSNeilBrown 
145f9c72d10SJeff Layton void
rpc_clnt_debugfs_register(struct rpc_clnt * clnt)146b4b9d2ccSJeff Layton rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
147b4b9d2ccSJeff Layton {
148f9c72d10SJeff Layton 	int len;
1492f34b8bfSNeilBrown 	char name[9]; /* enough for 8 hex digits + NULL */
1502f34b8bfSNeilBrown 	int xprtnum = 0;
151b4b9d2ccSJeff Layton 
152b4b9d2ccSJeff Layton 	len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
153b4b9d2ccSJeff Layton 	if (len >= sizeof(name))
154f9c72d10SJeff Layton 		return;
155b4b9d2ccSJeff Layton 
156b4b9d2ccSJeff Layton 	/* make the per-client dir */
157b4b9d2ccSJeff Layton 	clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
158b4b9d2ccSJeff Layton 
159b4b9d2ccSJeff Layton 	/* make tasks file */
1600a0762c6SGreg Kroah-Hartman 	debugfs_create_file("tasks", S_IFREG | 0400, clnt->cl_debugfs, clnt,
1610a0762c6SGreg Kroah-Hartman 			    &tasks_fops);
162388f0c77SJeff Layton 
1636860c981SLinus Torvalds 	rpc_clnt_iterate_for_each_xprt(clnt, do_xprt_debugfs, &xprtnum);
164b4b9d2ccSJeff Layton }
165b4b9d2ccSJeff Layton 
166b4b9d2ccSJeff Layton void
rpc_clnt_debugfs_unregister(struct rpc_clnt * clnt)167b4b9d2ccSJeff Layton rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
168b4b9d2ccSJeff Layton {
169b4b9d2ccSJeff Layton 	debugfs_remove_recursive(clnt->cl_debugfs);
170b4b9d2ccSJeff Layton 	clnt->cl_debugfs = NULL;
171b4b9d2ccSJeff Layton }
172b4b9d2ccSJeff Layton 
173388f0c77SJeff Layton static int
xprt_info_show(struct seq_file * f,void * v)174388f0c77SJeff Layton xprt_info_show(struct seq_file *f, void *v)
175388f0c77SJeff Layton {
176388f0c77SJeff Layton 	struct rpc_xprt *xprt = f->private;
177388f0c77SJeff Layton 
178388f0c77SJeff Layton 	seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
179388f0c77SJeff Layton 	seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
180388f0c77SJeff Layton 	seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
181388f0c77SJeff Layton 	seq_printf(f, "state: 0x%lx\n", xprt->state);
182388f0c77SJeff Layton 	return 0;
183388f0c77SJeff Layton }
184388f0c77SJeff Layton 
185388f0c77SJeff Layton static int
xprt_info_open(struct inode * inode,struct file * filp)186388f0c77SJeff Layton xprt_info_open(struct inode *inode, struct file *filp)
187388f0c77SJeff Layton {
188388f0c77SJeff Layton 	int ret;
189388f0c77SJeff Layton 	struct rpc_xprt *xprt = inode->i_private;
190388f0c77SJeff Layton 
191388f0c77SJeff Layton 	ret = single_open(filp, xprt_info_show, xprt);
192388f0c77SJeff Layton 
193388f0c77SJeff Layton 	if (!ret) {
194388f0c77SJeff Layton 		if (!xprt_get(xprt)) {
195388f0c77SJeff Layton 			single_release(inode, filp);
196388f0c77SJeff Layton 			ret = -EINVAL;
197388f0c77SJeff Layton 		}
198388f0c77SJeff Layton 	}
199388f0c77SJeff Layton 	return ret;
200388f0c77SJeff Layton }
201388f0c77SJeff Layton 
202388f0c77SJeff Layton static int
xprt_info_release(struct inode * inode,struct file * filp)203388f0c77SJeff Layton xprt_info_release(struct inode *inode, struct file *filp)
204388f0c77SJeff Layton {
205388f0c77SJeff Layton 	struct rpc_xprt *xprt = inode->i_private;
206388f0c77SJeff Layton 
207388f0c77SJeff Layton 	xprt_put(xprt);
208388f0c77SJeff Layton 	return single_release(inode, filp);
209388f0c77SJeff Layton }
210388f0c77SJeff Layton 
211388f0c77SJeff Layton static const struct file_operations xprt_info_fops = {
212388f0c77SJeff Layton 	.owner		= THIS_MODULE,
213388f0c77SJeff Layton 	.open		= xprt_info_open,
214388f0c77SJeff Layton 	.read		= seq_read,
215388f0c77SJeff Layton 	.llseek		= seq_lseek,
216388f0c77SJeff Layton 	.release	= xprt_info_release,
217388f0c77SJeff Layton };
218388f0c77SJeff Layton 
219f9c72d10SJeff Layton void
rpc_xprt_debugfs_register(struct rpc_xprt * xprt)220388f0c77SJeff Layton rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
221388f0c77SJeff Layton {
222388f0c77SJeff Layton 	int len, id;
223388f0c77SJeff Layton 	static atomic_t	cur_id;
224388f0c77SJeff Layton 	char		name[9]; /* 8 hex digits + NULL term */
225388f0c77SJeff Layton 
226388f0c77SJeff Layton 	id = (unsigned int)atomic_inc_return(&cur_id);
227388f0c77SJeff Layton 
228388f0c77SJeff Layton 	len = snprintf(name, sizeof(name), "%x", id);
229388f0c77SJeff Layton 	if (len >= sizeof(name))
230f9c72d10SJeff Layton 		return;
231388f0c77SJeff Layton 
232388f0c77SJeff Layton 	/* make the per-client dir */
233388f0c77SJeff Layton 	xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
234388f0c77SJeff Layton 
235388f0c77SJeff Layton 	/* make tasks file */
2360a0762c6SGreg Kroah-Hartman 	debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt,
2370a0762c6SGreg Kroah-Hartman 			    &xprt_info_fops);
238388f0c77SJeff Layton }
239388f0c77SJeff Layton 
240388f0c77SJeff Layton void
rpc_xprt_debugfs_unregister(struct rpc_xprt * xprt)241388f0c77SJeff Layton rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
242388f0c77SJeff Layton {
243388f0c77SJeff Layton 	debugfs_remove_recursive(xprt->debugfs);
244388f0c77SJeff Layton 	xprt->debugfs = NULL;
245388f0c77SJeff Layton }
246388f0c77SJeff Layton 
247c782af25SChuck Lever #if IS_ENABLED(CONFIG_FAIL_SUNRPC)
248c782af25SChuck Lever struct fail_sunrpc_attr fail_sunrpc = {
249c782af25SChuck Lever 	.attr			= FAULT_ATTR_INITIALIZER,
250c782af25SChuck Lever };
251c782af25SChuck Lever EXPORT_SYMBOL_GPL(fail_sunrpc);
252a4ae3081SChuck Lever 
fail_sunrpc_init(void)253a4ae3081SChuck Lever static void fail_sunrpc_init(void)
254a4ae3081SChuck Lever {
255a4ae3081SChuck Lever 	struct dentry *dir;
256a4ae3081SChuck Lever 
257a4ae3081SChuck Lever 	dir = fault_create_debugfs_attr("fail_sunrpc", NULL,
258a4ae3081SChuck Lever 					&fail_sunrpc.attr);
259a4ae3081SChuck Lever 
260a4ae3081SChuck Lever 	debugfs_create_bool("ignore-client-disconnect", S_IFREG | 0600, dir,
261a4ae3081SChuck Lever 			    &fail_sunrpc.ignore_client_disconnect);
2623a126180SChuck Lever 
2633a126180SChuck Lever 	debugfs_create_bool("ignore-server-disconnect", S_IFREG | 0600, dir,
2643a126180SChuck Lever 			    &fail_sunrpc.ignore_server_disconnect);
265*37324e6bSChuck Lever 
266*37324e6bSChuck Lever 	debugfs_create_bool("ignore-cache-wait", S_IFREG | 0600, dir,
267*37324e6bSChuck Lever 			    &fail_sunrpc.ignore_cache_wait);
268a4ae3081SChuck Lever }
269a4ae3081SChuck Lever #else
fail_sunrpc_init(void)270a4ae3081SChuck Lever static void fail_sunrpc_init(void)
271a4ae3081SChuck Lever {
272a4ae3081SChuck Lever }
273c782af25SChuck Lever #endif
274c782af25SChuck Lever 
275b4b9d2ccSJeff Layton void __exit
sunrpc_debugfs_exit(void)276b4b9d2ccSJeff Layton sunrpc_debugfs_exit(void)
277b4b9d2ccSJeff Layton {
278b4b9d2ccSJeff Layton 	debugfs_remove_recursive(topdir);
279f9c72d10SJeff Layton 	topdir = NULL;
280f9c72d10SJeff Layton 	rpc_clnt_dir = NULL;
281f9c72d10SJeff Layton 	rpc_xprt_dir = NULL;
282b4b9d2ccSJeff Layton }
283b4b9d2ccSJeff Layton 
284f9c72d10SJeff Layton void __init
sunrpc_debugfs_init(void)285b4b9d2ccSJeff Layton sunrpc_debugfs_init(void)
286b4b9d2ccSJeff Layton {
2870a0762c6SGreg Kroah-Hartman 	topdir = debugfs_create_dir("sunrpc", NULL);
2884a068258SChuck Lever 
289b4b9d2ccSJeff Layton 	rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
290b4b9d2ccSJeff Layton 
291388f0c77SJeff Layton 	rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
292388f0c77SJeff Layton 
293a4ae3081SChuck Lever 	fail_sunrpc_init();
294b4b9d2ccSJeff Layton }
295