]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - net/sunrpc/debugfs.c
Merge remote-tracking branch 'remotes/stable/linux-4.4.y' into karo-tx6-mainline
[karo-tx-linux.git] / net / sunrpc / debugfs.c
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_fault_dir;
14 static struct dentry *rpc_clnt_dir;
15 static struct dentry *rpc_xprt_dir;
16
17 unsigned int rpc_inject_disconnect;
18
19 struct rpc_clnt_iter {
20         struct rpc_clnt *clnt;
21         loff_t          pos;
22 };
23
24 static int
25 tasks_show(struct seq_file *f, void *v)
26 {
27         u32 xid = 0;
28         struct rpc_task *task = v;
29         struct rpc_clnt *clnt = task->tk_client;
30         const char *rpc_waitq = "none";
31
32         if (RPC_IS_QUEUED(task))
33                 rpc_waitq = rpc_qname(task->tk_waitqueue);
34
35         if (task->tk_rqstp)
36                 xid = be32_to_cpu(task->tk_rqstp->rq_xid);
37
38         seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
39                 task->tk_pid, task->tk_flags, task->tk_status,
40                 clnt->cl_clid, xid, task->tk_timeout, task->tk_ops,
41                 clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
42                 task->tk_action, rpc_waitq);
43         return 0;
44 }
45
46 static void *
47 tasks_start(struct seq_file *f, loff_t *ppos)
48         __acquires(&clnt->cl_lock)
49 {
50         struct rpc_clnt_iter *iter = f->private;
51         loff_t pos = *ppos;
52         struct rpc_clnt *clnt = iter->clnt;
53         struct rpc_task *task;
54
55         iter->pos = pos + 1;
56         spin_lock(&clnt->cl_lock);
57         list_for_each_entry(task, &clnt->cl_tasks, tk_task)
58                 if (pos-- == 0)
59                         return task;
60         return NULL;
61 }
62
63 static void *
64 tasks_next(struct seq_file *f, void *v, loff_t *pos)
65 {
66         struct rpc_clnt_iter *iter = f->private;
67         struct rpc_clnt *clnt = iter->clnt;
68         struct rpc_task *task = v;
69         struct list_head *next = task->tk_task.next;
70
71         ++iter->pos;
72         ++*pos;
73
74         /* If there's another task on list, return it */
75         if (next == &clnt->cl_tasks)
76                 return NULL;
77         return list_entry(next, struct rpc_task, tk_task);
78 }
79
80 static void
81 tasks_stop(struct seq_file *f, void *v)
82         __releases(&clnt->cl_lock)
83 {
84         struct rpc_clnt_iter *iter = f->private;
85         struct rpc_clnt *clnt = iter->clnt;
86
87         spin_unlock(&clnt->cl_lock);
88 }
89
90 static const struct seq_operations tasks_seq_operations = {
91         .start  = tasks_start,
92         .next   = tasks_next,
93         .stop   = tasks_stop,
94         .show   = tasks_show,
95 };
96
97 static int tasks_open(struct inode *inode, struct file *filp)
98 {
99         int ret = seq_open_private(filp, &tasks_seq_operations,
100                                         sizeof(struct rpc_clnt_iter));
101
102         if (!ret) {
103                 struct seq_file *seq = filp->private_data;
104                 struct rpc_clnt_iter *iter = seq->private;
105
106                 iter->clnt = inode->i_private;
107
108                 if (!atomic_inc_not_zero(&iter->clnt->cl_count)) {
109                         seq_release_private(inode, filp);
110                         ret = -EINVAL;
111                 }
112         }
113
114         return ret;
115 }
116
117 static int
118 tasks_release(struct inode *inode, struct file *filp)
119 {
120         struct seq_file *seq = filp->private_data;
121         struct rpc_clnt_iter *iter = seq->private;
122
123         rpc_release_client(iter->clnt);
124         return seq_release_private(inode, filp);
125 }
126
127 static const struct file_operations tasks_fops = {
128         .owner          = THIS_MODULE,
129         .open           = tasks_open,
130         .read           = seq_read,
131         .llseek         = seq_lseek,
132         .release        = tasks_release,
133 };
134
135 void
136 rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
137 {
138         int len;
139         char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
140         struct rpc_xprt *xprt;
141
142         /* Already registered? */
143         if (clnt->cl_debugfs || !rpc_clnt_dir)
144                 return;
145
146         len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
147         if (len >= sizeof(name))
148                 return;
149
150         /* make the per-client dir */
151         clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
152         if (!clnt->cl_debugfs)
153                 return;
154
155         /* make tasks file */
156         if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
157                                  clnt, &tasks_fops))
158                 goto out_err;
159
160         rcu_read_lock();
161         xprt = rcu_dereference(clnt->cl_xprt);
162         /* no "debugfs" dentry? Don't bother with the symlink. */
163         if (!xprt->debugfs) {
164                 rcu_read_unlock();
165                 return;
166         }
167         len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
168                         xprt->debugfs->d_name.name);
169         rcu_read_unlock();
170
171         if (len >= sizeof(name))
172                 goto out_err;
173
174         if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
175                 goto out_err;
176
177         return;
178 out_err:
179         debugfs_remove_recursive(clnt->cl_debugfs);
180         clnt->cl_debugfs = NULL;
181 }
182
183 void
184 rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
185 {
186         debugfs_remove_recursive(clnt->cl_debugfs);
187         clnt->cl_debugfs = NULL;
188 }
189
190 static int
191 xprt_info_show(struct seq_file *f, void *v)
192 {
193         struct rpc_xprt *xprt = f->private;
194
195         seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
196         seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
197         seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
198         seq_printf(f, "state: 0x%lx\n", xprt->state);
199         return 0;
200 }
201
202 static int
203 xprt_info_open(struct inode *inode, struct file *filp)
204 {
205         int ret;
206         struct rpc_xprt *xprt = inode->i_private;
207
208         ret = single_open(filp, xprt_info_show, xprt);
209
210         if (!ret) {
211                 if (!xprt_get(xprt)) {
212                         single_release(inode, filp);
213                         ret = -EINVAL;
214                 }
215         }
216         return ret;
217 }
218
219 static int
220 xprt_info_release(struct inode *inode, struct file *filp)
221 {
222         struct rpc_xprt *xprt = inode->i_private;
223
224         xprt_put(xprt);
225         return single_release(inode, filp);
226 }
227
228 static const struct file_operations xprt_info_fops = {
229         .owner          = THIS_MODULE,
230         .open           = xprt_info_open,
231         .read           = seq_read,
232         .llseek         = seq_lseek,
233         .release        = xprt_info_release,
234 };
235
236 void
237 rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
238 {
239         int len, id;
240         static atomic_t cur_id;
241         char            name[9]; /* 8 hex digits + NULL term */
242
243         if (!rpc_xprt_dir)
244                 return;
245
246         id = (unsigned int)atomic_inc_return(&cur_id);
247
248         len = snprintf(name, sizeof(name), "%x", id);
249         if (len >= sizeof(name))
250                 return;
251
252         /* make the per-client dir */
253         xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
254         if (!xprt->debugfs)
255                 return;
256
257         /* make tasks file */
258         if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
259                                  xprt, &xprt_info_fops)) {
260                 debugfs_remove_recursive(xprt->debugfs);
261                 xprt->debugfs = NULL;
262         }
263
264         atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);
265 }
266
267 void
268 rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
269 {
270         debugfs_remove_recursive(xprt->debugfs);
271         xprt->debugfs = NULL;
272 }
273
274 static int
275 fault_open(struct inode *inode, struct file *filp)
276 {
277         filp->private_data = kmalloc(128, GFP_KERNEL);
278         if (!filp->private_data)
279                 return -ENOMEM;
280         return 0;
281 }
282
283 static int
284 fault_release(struct inode *inode, struct file *filp)
285 {
286         kfree(filp->private_data);
287         return 0;
288 }
289
290 static ssize_t
291 fault_disconnect_read(struct file *filp, char __user *user_buf,
292                       size_t len, loff_t *offset)
293 {
294         char *buffer = (char *)filp->private_data;
295         size_t size;
296
297         size = sprintf(buffer, "%u\n", rpc_inject_disconnect);
298         return simple_read_from_buffer(user_buf, len, offset, buffer, size);
299 }
300
301 static ssize_t
302 fault_disconnect_write(struct file *filp, const char __user *user_buf,
303                        size_t len, loff_t *offset)
304 {
305         char buffer[16];
306
307         if (len >= sizeof(buffer))
308                 len = sizeof(buffer) - 1;
309         if (copy_from_user(buffer, user_buf, len))
310                 return -EFAULT;
311         buffer[len] = '\0';
312         if (kstrtouint(buffer, 10, &rpc_inject_disconnect))
313                 return -EINVAL;
314         return len;
315 }
316
317 static const struct file_operations fault_disconnect_fops = {
318         .owner          = THIS_MODULE,
319         .open           = fault_open,
320         .read           = fault_disconnect_read,
321         .write          = fault_disconnect_write,
322         .release        = fault_release,
323 };
324
325 static struct dentry *
326 inject_fault_dir(struct dentry *topdir)
327 {
328         struct dentry *faultdir;
329
330         faultdir = debugfs_create_dir("inject_fault", topdir);
331         if (!faultdir)
332                 return NULL;
333
334         if (!debugfs_create_file("disconnect", S_IFREG | S_IRUSR, faultdir,
335                                  NULL, &fault_disconnect_fops))
336                 return NULL;
337
338         return faultdir;
339 }
340
341 void __exit
342 sunrpc_debugfs_exit(void)
343 {
344         debugfs_remove_recursive(topdir);
345         topdir = NULL;
346         rpc_fault_dir = NULL;
347         rpc_clnt_dir = NULL;
348         rpc_xprt_dir = NULL;
349 }
350
351 void __init
352 sunrpc_debugfs_init(void)
353 {
354         topdir = debugfs_create_dir("sunrpc", NULL);
355         if (!topdir)
356                 return;
357
358         rpc_fault_dir = inject_fault_dir(topdir);
359         if (!rpc_fault_dir)
360                 goto out_remove;
361
362         rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
363         if (!rpc_clnt_dir)
364                 goto out_remove;
365
366         rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
367         if (!rpc_xprt_dir)
368                 goto out_remove;
369
370         return;
371 out_remove:
372         debugfs_remove_recursive(topdir);
373         topdir = NULL;
374         rpc_fault_dir = NULL;
375         rpc_clnt_dir = NULL;
376 }