1/**
2 * debugfs interface for sunrpc
3 *
4 * (c) 2014 Jeff Layton <jlayton@primarydata.com>
5 */
6
7#include <linux/debugfs.h>
8#include <linux/sunrpc/sched.h>
9#include <linux/sunrpc/clnt.h>
10#include "netns.h"
11
12static struct dentry *topdir;
13static struct dentry *rpc_clnt_dir;
14static struct dentry *rpc_xprt_dir;
15
16struct rpc_clnt_iter {
17	struct rpc_clnt	*clnt;
18	loff_t		pos;
19};
20
21static int
22tasks_show(struct seq_file *f, void *v)
23{
24	u32 xid = 0;
25	struct rpc_task *task = v;
26	struct rpc_clnt *clnt = task->tk_client;
27	const char *rpc_waitq = "none";
28
29	if (RPC_IS_QUEUED(task))
30		rpc_waitq = rpc_qname(task->tk_waitqueue);
31
32	if (task->tk_rqstp)
33		xid = be32_to_cpu(task->tk_rqstp->rq_xid);
34
35	seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
36		task->tk_pid, task->tk_flags, task->tk_status,
37		clnt->cl_clid, xid, task->tk_timeout, task->tk_ops,
38		clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
39		task->tk_action, rpc_waitq);
40	return 0;
41}
42
43static void *
44tasks_start(struct seq_file *f, loff_t *ppos)
45	__acquires(&clnt->cl_lock)
46{
47	struct rpc_clnt_iter *iter = f->private;
48	loff_t pos = *ppos;
49	struct rpc_clnt *clnt = iter->clnt;
50	struct rpc_task *task;
51
52	iter->pos = pos + 1;
53	spin_lock(&clnt->cl_lock);
54	list_for_each_entry(task, &clnt->cl_tasks, tk_task)
55		if (pos-- == 0)
56			return task;
57	return NULL;
58}
59
60static void *
61tasks_next(struct seq_file *f, void *v, loff_t *pos)
62{
63	struct rpc_clnt_iter *iter = f->private;
64	struct rpc_clnt *clnt = iter->clnt;
65	struct rpc_task *task = v;
66	struct list_head *next = task->tk_task.next;
67
68	++iter->pos;
69	++*pos;
70
71	/* If there's another task on list, return it */
72	if (next == &clnt->cl_tasks)
73		return NULL;
74	return list_entry(next, struct rpc_task, tk_task);
75}
76
77static void
78tasks_stop(struct seq_file *f, void *v)
79	__releases(&clnt->cl_lock)
80{
81	struct rpc_clnt_iter *iter = f->private;
82	struct rpc_clnt *clnt = iter->clnt;
83
84	spin_unlock(&clnt->cl_lock);
85}
86
87static const struct seq_operations tasks_seq_operations = {
88	.start	= tasks_start,
89	.next	= tasks_next,
90	.stop	= tasks_stop,
91	.show	= tasks_show,
92};
93
94static int tasks_open(struct inode *inode, struct file *filp)
95{
96	int ret = seq_open_private(filp, &tasks_seq_operations,
97					sizeof(struct rpc_clnt_iter));
98
99	if (!ret) {
100		struct seq_file *seq = filp->private_data;
101		struct rpc_clnt_iter *iter = seq->private;
102
103		iter->clnt = inode->i_private;
104
105		if (!atomic_inc_not_zero(&iter->clnt->cl_count)) {
106			seq_release_private(inode, filp);
107			ret = -EINVAL;
108		}
109	}
110
111	return ret;
112}
113
114static int
115tasks_release(struct inode *inode, struct file *filp)
116{
117	struct seq_file *seq = filp->private_data;
118	struct rpc_clnt_iter *iter = seq->private;
119
120	rpc_release_client(iter->clnt);
121	return seq_release_private(inode, filp);
122}
123
124static const struct file_operations tasks_fops = {
125	.owner		= THIS_MODULE,
126	.open		= tasks_open,
127	.read		= seq_read,
128	.llseek		= seq_lseek,
129	.release	= tasks_release,
130};
131
132void
133rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
134{
135	int len;
136	char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
137	struct rpc_xprt *xprt;
138
139	/* Already registered? */
140	if (clnt->cl_debugfs || !rpc_clnt_dir)
141		return;
142
143	len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
144	if (len >= sizeof(name))
145		return;
146
147	/* make the per-client dir */
148	clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
149	if (!clnt->cl_debugfs)
150		return;
151
152	/* make tasks file */
153	if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
154				 clnt, &tasks_fops))
155		goto out_err;
156
157	rcu_read_lock();
158	xprt = rcu_dereference(clnt->cl_xprt);
159	/* no "debugfs" dentry? Don't bother with the symlink. */
160	if (!xprt->debugfs) {
161		rcu_read_unlock();
162		return;
163	}
164	len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
165			xprt->debugfs->d_name.name);
166	rcu_read_unlock();
167
168	if (len >= sizeof(name))
169		goto out_err;
170
171	if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
172		goto out_err;
173
174	return;
175out_err:
176	debugfs_remove_recursive(clnt->cl_debugfs);
177	clnt->cl_debugfs = NULL;
178}
179
180void
181rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
182{
183	debugfs_remove_recursive(clnt->cl_debugfs);
184	clnt->cl_debugfs = NULL;
185}
186
187static int
188xprt_info_show(struct seq_file *f, void *v)
189{
190	struct rpc_xprt *xprt = f->private;
191
192	seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
193	seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
194	seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
195	seq_printf(f, "state: 0x%lx\n", xprt->state);
196	return 0;
197}
198
199static int
200xprt_info_open(struct inode *inode, struct file *filp)
201{
202	int ret;
203	struct rpc_xprt *xprt = inode->i_private;
204
205	ret = single_open(filp, xprt_info_show, xprt);
206
207	if (!ret) {
208		if (!xprt_get(xprt)) {
209			single_release(inode, filp);
210			ret = -EINVAL;
211		}
212	}
213	return ret;
214}
215
216static int
217xprt_info_release(struct inode *inode, struct file *filp)
218{
219	struct rpc_xprt *xprt = inode->i_private;
220
221	xprt_put(xprt);
222	return single_release(inode, filp);
223}
224
225static const struct file_operations xprt_info_fops = {
226	.owner		= THIS_MODULE,
227	.open		= xprt_info_open,
228	.read		= seq_read,
229	.llseek		= seq_lseek,
230	.release	= xprt_info_release,
231};
232
233void
234rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
235{
236	int len, id;
237	static atomic_t	cur_id;
238	char		name[9]; /* 8 hex digits + NULL term */
239
240	if (!rpc_xprt_dir)
241		return;
242
243	id = (unsigned int)atomic_inc_return(&cur_id);
244
245	len = snprintf(name, sizeof(name), "%x", id);
246	if (len >= sizeof(name))
247		return;
248
249	/* make the per-client dir */
250	xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
251	if (!xprt->debugfs)
252		return;
253
254	/* make tasks file */
255	if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
256				 xprt, &xprt_info_fops)) {
257		debugfs_remove_recursive(xprt->debugfs);
258		xprt->debugfs = NULL;
259	}
260}
261
262void
263rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
264{
265	debugfs_remove_recursive(xprt->debugfs);
266	xprt->debugfs = NULL;
267}
268
269void __exit
270sunrpc_debugfs_exit(void)
271{
272	debugfs_remove_recursive(topdir);
273	topdir = NULL;
274	rpc_clnt_dir = NULL;
275	rpc_xprt_dir = NULL;
276}
277
278void __init
279sunrpc_debugfs_init(void)
280{
281	topdir = debugfs_create_dir("sunrpc", NULL);
282	if (!topdir)
283		return;
284
285	rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
286	if (!rpc_clnt_dir)
287		goto out_remove;
288
289	rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
290	if (!rpc_xprt_dir)
291		goto out_remove;
292
293	return;
294out_remove:
295	debugfs_remove_recursive(topdir);
296	topdir = NULL;
297	rpc_clnt_dir = NULL;
298}
299