Merge branch 'next' into for-linus
[sfrench/cifs-2.6.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_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 int
133 rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
134 {
135         int len, err;
136         char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
137
138         /* Already registered? */
139         if (clnt->cl_debugfs)
140                 return 0;
141
142         len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
143         if (len >= sizeof(name))
144                 return -EINVAL;
145
146         /* make the per-client dir */
147         clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
148         if (!clnt->cl_debugfs)
149                 return -ENOMEM;
150
151         /* make tasks file */
152         err = -ENOMEM;
153         if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
154                                  clnt, &tasks_fops))
155                 goto out_err;
156
157         err = -EINVAL;
158         rcu_read_lock();
159         len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
160                         rcu_dereference(clnt->cl_xprt)->debugfs->d_name.name);
161         rcu_read_unlock();
162         if (len >= sizeof(name))
163                 goto out_err;
164
165         err = -ENOMEM;
166         if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
167                 goto out_err;
168
169         return 0;
170 out_err:
171         debugfs_remove_recursive(clnt->cl_debugfs);
172         clnt->cl_debugfs = NULL;
173         return err;
174 }
175
176 void
177 rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
178 {
179         debugfs_remove_recursive(clnt->cl_debugfs);
180         clnt->cl_debugfs = NULL;
181 }
182
183 static int
184 xprt_info_show(struct seq_file *f, void *v)
185 {
186         struct rpc_xprt *xprt = f->private;
187
188         seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
189         seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
190         seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
191         seq_printf(f, "state: 0x%lx\n", xprt->state);
192         return 0;
193 }
194
195 static int
196 xprt_info_open(struct inode *inode, struct file *filp)
197 {
198         int ret;
199         struct rpc_xprt *xprt = inode->i_private;
200
201         ret = single_open(filp, xprt_info_show, xprt);
202
203         if (!ret) {
204                 if (!xprt_get(xprt)) {
205                         single_release(inode, filp);
206                         ret = -EINVAL;
207                 }
208         }
209         return ret;
210 }
211
212 static int
213 xprt_info_release(struct inode *inode, struct file *filp)
214 {
215         struct rpc_xprt *xprt = inode->i_private;
216
217         xprt_put(xprt);
218         return single_release(inode, filp);
219 }
220
221 static const struct file_operations xprt_info_fops = {
222         .owner          = THIS_MODULE,
223         .open           = xprt_info_open,
224         .read           = seq_read,
225         .llseek         = seq_lseek,
226         .release        = xprt_info_release,
227 };
228
229 int
230 rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
231 {
232         int len, id;
233         static atomic_t cur_id;
234         char            name[9]; /* 8 hex digits + NULL term */
235
236         id = (unsigned int)atomic_inc_return(&cur_id);
237
238         len = snprintf(name, sizeof(name), "%x", id);
239         if (len >= sizeof(name))
240                 return -EINVAL;
241
242         /* make the per-client dir */
243         xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
244         if (!xprt->debugfs)
245                 return -ENOMEM;
246
247         /* make tasks file */
248         if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
249                                  xprt, &xprt_info_fops)) {
250                 debugfs_remove_recursive(xprt->debugfs);
251                 xprt->debugfs = NULL;
252                 return -ENOMEM;
253         }
254
255         return 0;
256 }
257
258 void
259 rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
260 {
261         debugfs_remove_recursive(xprt->debugfs);
262         xprt->debugfs = NULL;
263 }
264
265 void __exit
266 sunrpc_debugfs_exit(void)
267 {
268         debugfs_remove_recursive(topdir);
269 }
270
271 int __init
272 sunrpc_debugfs_init(void)
273 {
274         topdir = debugfs_create_dir("sunrpc", NULL);
275         if (!topdir)
276                 goto out;
277
278         rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
279         if (!rpc_clnt_dir)
280                 goto out_remove;
281
282         rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
283         if (!rpc_xprt_dir)
284                 goto out_remove;
285
286         return 0;
287 out_remove:
288         debugfs_remove_recursive(topdir);
289         topdir = NULL;
290 out:
291         return -ENOMEM;
292 }