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