blob: 3d7745683ca3ca1a88161a9eb367269b8864a14e [file] [log] [blame]
Jeff Laytonb4b9d2c2014-11-26 14:44:43 -05001/**
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;
14
15struct rpc_clnt_iter {
16 struct rpc_clnt *clnt;
17 loff_t pos;
18};
19
20static int
21tasks_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
42static void *
43tasks_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
59static void *
60tasks_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
76static void
77tasks_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
86static 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
93static 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
113static int
114tasks_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
123static 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
131int
132rpc_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
161void
162rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
163{
164 debugfs_remove_recursive(clnt->cl_debugfs);
165 clnt->cl_debugfs = NULL;
166}
167
168void __exit
169sunrpc_debugfs_exit(void)
170{
171 debugfs_remove_recursive(topdir);
172}
173
174int __init
175sunrpc_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;
186out_remove:
187 debugfs_remove_recursive(topdir);
188 topdir = NULL;
189out:
190 return -ENOMEM;
191}