sunrpc: add debugfs file for displaying client rpc_task queue
[cascardo/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_clnt_dir;
14
15 struct rpc_clnt_iter {
16         struct rpc_clnt *clnt;
17         loff_t          pos;
18 };
19
20 static int
21 tasks_show(struct seq_file *f, void *v)
22 {
23         u32 xid = 0;
24         struct rpc_task *task = v;
25         struct rpc_clnt *clnt = task->tk_client;
26         const char *rpc_waitq = "none";
27
28         if (RPC_IS_QUEUED(task))
29                 rpc_waitq = rpc_qname(task->tk_waitqueue);
30
31         if (task->tk_rqstp)
32                 xid = be32_to_cpu(task->tk_rqstp->rq_xid);
33
34         seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
35                 task->tk_pid, task->tk_flags, task->tk_status,
36                 clnt->cl_clid, xid, task->tk_timeout, task->tk_ops,
37                 clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
38                 task->tk_action, rpc_waitq);
39         return 0;
40 }
41
42 static void *
43 tasks_start(struct seq_file *f, loff_t *ppos)
44         __acquires(&clnt->cl_lock)
45 {
46         struct rpc_clnt_iter *iter = f->private;
47         loff_t pos = *ppos;
48         struct rpc_clnt *clnt = iter->clnt;
49         struct rpc_task *task;
50
51         iter->pos = pos + 1;
52         spin_lock(&clnt->cl_lock);
53         list_for_each_entry(task, &clnt->cl_tasks, tk_task)
54                 if (pos-- == 0)
55                         return task;
56         return NULL;
57 }
58
59 static void *
60 tasks_next(struct seq_file *f, void *v, loff_t *pos)
61 {
62         struct rpc_clnt_iter *iter = f->private;
63         struct rpc_clnt *clnt = iter->clnt;
64         struct rpc_task *task = v;
65         struct list_head *next = task->tk_task.next;
66
67         ++iter->pos;
68         ++*pos;
69
70         /* If there's another task on list, return it */
71         if (next == &clnt->cl_tasks)
72                 return NULL;
73         return list_entry(next, struct rpc_task, tk_task);
74 }
75
76 static void
77 tasks_stop(struct seq_file *f, void *v)
78         __releases(&clnt->cl_lock)
79 {
80         struct rpc_clnt_iter *iter = f->private;
81         struct rpc_clnt *clnt = iter->clnt;
82
83         spin_unlock(&clnt->cl_lock);
84 }
85
86 static const struct seq_operations tasks_seq_operations = {
87         .start  = tasks_start,
88         .next   = tasks_next,
89         .stop   = tasks_stop,
90         .show   = tasks_show,
91 };
92
93 static int tasks_open(struct inode *inode, struct file *filp)
94 {
95         int ret = seq_open_private(filp, &tasks_seq_operations,
96                                         sizeof(struct rpc_clnt_iter));
97
98         if (!ret) {
99                 struct seq_file *seq = filp->private_data;
100                 struct rpc_clnt_iter *iter = seq->private;
101
102                 iter->clnt = inode->i_private;
103
104                 if (!atomic_inc_not_zero(&iter->clnt->cl_count)) {
105                         seq_release_private(inode, filp);
106                         ret = -EINVAL;
107                 }
108         }
109
110         return ret;
111 }
112
113 static int
114 tasks_release(struct inode *inode, struct file *filp)
115 {
116         struct seq_file *seq = filp->private_data;
117         struct rpc_clnt_iter *iter = seq->private;
118
119         rpc_release_client(iter->clnt);
120         return seq_release_private(inode, filp);
121 }
122
123 static const struct file_operations tasks_fops = {
124         .owner          = THIS_MODULE,
125         .open           = tasks_open,
126         .read           = seq_read,
127         .llseek         = seq_lseek,
128         .release        = tasks_release,
129 };
130
131 int
132 rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
133 {
134         int len;
135         char name[9]; /* 8 for hex digits + NULL terminator */
136
137         /* Already registered? */
138         if (clnt->cl_debugfs)
139                 return 0;
140
141         len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
142         if (len >= sizeof(name))
143                 return -EINVAL;
144
145         /* make the per-client dir */
146         clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
147         if (!clnt->cl_debugfs)
148                 return -ENOMEM;
149
150         /* make tasks file */
151         if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
152                                  clnt, &tasks_fops)) {
153                 debugfs_remove_recursive(clnt->cl_debugfs);
154                 clnt->cl_debugfs = NULL;
155                 return -ENOMEM;
156         }
157
158         return 0;
159 }
160
161 void
162 rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
163 {
164         debugfs_remove_recursive(clnt->cl_debugfs);
165         clnt->cl_debugfs = NULL;
166 }
167
168 void __exit
169 sunrpc_debugfs_exit(void)
170 {
171         debugfs_remove_recursive(topdir);
172 }
173
174 int __init
175 sunrpc_debugfs_init(void)
176 {
177         topdir = debugfs_create_dir("sunrpc", NULL);
178         if (!topdir)
179                 goto out;
180
181         rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
182         if (!rpc_clnt_dir)
183                 goto out_remove;
184
185         return 0;
186 out_remove:
187         debugfs_remove_recursive(topdir);
188         topdir = NULL;
189 out:
190         return -ENOMEM;
191 }