xref: /openbmc/linux/net/sunrpc/debugfs.c (revision 56439ab0)
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 
12 static struct dentry *topdir;
13 static struct dentry *rpc_clnt_dir;
14 static struct dentry *rpc_xprt_dir;
15 
16 struct rpc_clnt_iter {
17 	struct rpc_clnt	*clnt;
18 	loff_t		pos;
19 };
20 
21 static int
22 tasks_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 
43 static void *
44 tasks_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 
60 static void *
61 tasks_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 
77 static void
78 tasks_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 
87 static 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 
94 static 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 
114 static int
115 tasks_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 
124 static 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 
132 void
133 rpc_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;
175 out_err:
176 	debugfs_remove_recursive(clnt->cl_debugfs);
177 	clnt->cl_debugfs = NULL;
178 }
179 
180 void
181 rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
182 {
183 	debugfs_remove_recursive(clnt->cl_debugfs);
184 	clnt->cl_debugfs = NULL;
185 }
186 
187 static int
188 xprt_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 
199 static int
200 xprt_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 
216 static int
217 xprt_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 
225 static 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 
233 void
234 rpc_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 
262 void
263 rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
264 {
265 	debugfs_remove_recursive(xprt->debugfs);
266 	xprt->debugfs = NULL;
267 }
268 
269 void __exit
270 sunrpc_debugfs_exit(void)
271 {
272 	debugfs_remove_recursive(topdir);
273 	topdir = NULL;
274 	rpc_clnt_dir = NULL;
275 	rpc_xprt_dir = NULL;
276 }
277 
278 void __init
279 sunrpc_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;
294 out_remove:
295 	debugfs_remove_recursive(topdir);
296 	topdir = NULL;
297 	rpc_clnt_dir = NULL;
298 }
299