diff options
author | Jeff Layton <jlayton@primarydata.com> | 2014-11-26 14:44:43 -0500 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-11-27 13:14:51 -0500 |
commit | b4b9d2ccf0be61c69213f6ae4e33377c05194ef4 (patch) | |
tree | 54709e972d890b22b6de65124f87953534aaa9a3 /net | |
parent | ea5264138d240825a7e3f11d5945d568b74efb91 (diff) |
sunrpc: add debugfs file for displaying client rpc_task queue
It's possible to get a dump of the RPC task queue by writing a value to
/proc/sys/sunrpc/rpc_debug. If you write any value to that file, you get
a dump of the RPC client task list into the log buffer. This is a rather
inconvenient interface however, and makes it hard to get immediate info
about the task queue.
Add a new directory hierarchy under debugfs:
sunrpc/
rpc_clnt/
<clientid>/
Within each clientid directory we create a new "tasks" file that will
dump info similar to what shows up in the log buffer, but with a few
small differences -- we avoid printing raw kernel addresses in favor of
symbolic names and the XID is also displayed.
Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/Kconfig | 1 | ||||
-rw-r--r-- | net/sunrpc/Makefile | 1 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 10 | ||||
-rw-r--r-- | net/sunrpc/debugfs.c | 191 | ||||
-rw-r--r-- | net/sunrpc/sunrpc_syms.c | 8 |
5 files changed, 210 insertions, 1 deletions
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index 0754d0f466d2..fb78117b896c 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig | |||
@@ -35,6 +35,7 @@ config RPCSEC_GSS_KRB5 | |||
35 | config SUNRPC_DEBUG | 35 | config SUNRPC_DEBUG |
36 | bool "RPC: Enable dprintk debugging" | 36 | bool "RPC: Enable dprintk debugging" |
37 | depends on SUNRPC && SYSCTL | 37 | depends on SUNRPC && SYSCTL |
38 | select DEBUG_FS | ||
38 | help | 39 | help |
39 | This option enables a sysctl-based debugging interface | 40 | This option enables a sysctl-based debugging interface |
40 | that is be used by the 'rpcdebug' utility to turn on or off | 41 | that is be used by the 'rpcdebug' utility to turn on or off |
diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile index e5a7a1cac8f3..15e6f6c23c5d 100644 --- a/net/sunrpc/Makefile +++ b/net/sunrpc/Makefile | |||
@@ -14,6 +14,7 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \ | |||
14 | addr.o rpcb_clnt.o timer.o xdr.o \ | 14 | addr.o rpcb_clnt.o timer.o xdr.o \ |
15 | sunrpc_syms.o cache.o rpc_pipe.o \ | 15 | sunrpc_syms.o cache.o rpc_pipe.o \ |
16 | svc_xprt.o | 16 | svc_xprt.o |
17 | sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o | ||
17 | sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o bc_svc.o | 18 | sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o bc_svc.o |
18 | sunrpc-$(CONFIG_PROC_FS) += stats.o | 19 | sunrpc-$(CONFIG_PROC_FS) += stats.o |
19 | sunrpc-$(CONFIG_SYSCTL) += sysctl.o | 20 | sunrpc-$(CONFIG_SYSCTL) += sysctl.o |
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 36c64ef460cf..05da12a33945 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -305,6 +305,10 @@ static int rpc_client_register(struct rpc_clnt *clnt, | |||
305 | struct super_block *pipefs_sb; | 305 | struct super_block *pipefs_sb; |
306 | int err; | 306 | int err; |
307 | 307 | ||
308 | err = rpc_clnt_debugfs_register(clnt); | ||
309 | if (err) | ||
310 | return err; | ||
311 | |||
308 | pipefs_sb = rpc_get_sb_net(net); | 312 | pipefs_sb = rpc_get_sb_net(net); |
309 | if (pipefs_sb) { | 313 | if (pipefs_sb) { |
310 | err = rpc_setup_pipedir(pipefs_sb, clnt); | 314 | err = rpc_setup_pipedir(pipefs_sb, clnt); |
@@ -331,6 +335,7 @@ err_auth: | |||
331 | out: | 335 | out: |
332 | if (pipefs_sb) | 336 | if (pipefs_sb) |
333 | rpc_put_sb_net(net); | 337 | rpc_put_sb_net(net); |
338 | rpc_clnt_debugfs_unregister(clnt); | ||
334 | return err; | 339 | return err; |
335 | } | 340 | } |
336 | 341 | ||
@@ -670,6 +675,7 @@ int rpc_switch_client_transport(struct rpc_clnt *clnt, | |||
670 | 675 | ||
671 | rpc_unregister_client(clnt); | 676 | rpc_unregister_client(clnt); |
672 | __rpc_clnt_remove_pipedir(clnt); | 677 | __rpc_clnt_remove_pipedir(clnt); |
678 | rpc_clnt_debugfs_unregister(clnt); | ||
673 | 679 | ||
674 | /* | 680 | /* |
675 | * A new transport was created. "clnt" therefore | 681 | * A new transport was created. "clnt" therefore |
@@ -771,6 +777,7 @@ rpc_free_client(struct rpc_clnt *clnt) | |||
771 | rcu_dereference(clnt->cl_xprt)->servername); | 777 | rcu_dereference(clnt->cl_xprt)->servername); |
772 | if (clnt->cl_parent != clnt) | 778 | if (clnt->cl_parent != clnt) |
773 | parent = clnt->cl_parent; | 779 | parent = clnt->cl_parent; |
780 | rpc_clnt_debugfs_unregister(clnt); | ||
774 | rpc_clnt_remove_pipedir(clnt); | 781 | rpc_clnt_remove_pipedir(clnt); |
775 | rpc_unregister_client(clnt); | 782 | rpc_unregister_client(clnt); |
776 | rpc_free_iostats(clnt->cl_metrics); | 783 | rpc_free_iostats(clnt->cl_metrics); |
@@ -1397,7 +1404,8 @@ rpc_restart_call(struct rpc_task *task) | |||
1397 | EXPORT_SYMBOL_GPL(rpc_restart_call); | 1404 | EXPORT_SYMBOL_GPL(rpc_restart_call); |
1398 | 1405 | ||
1399 | #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) | 1406 | #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) |
1400 | static const char *rpc_proc_name(const struct rpc_task *task) | 1407 | const char |
1408 | *rpc_proc_name(const struct rpc_task *task) | ||
1401 | { | 1409 | { |
1402 | const struct rpc_procinfo *proc = task->tk_msg.rpc_proc; | 1410 | const struct rpc_procinfo *proc = task->tk_msg.rpc_proc; |
1403 | 1411 | ||
diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c new file mode 100644 index 000000000000..3d7745683ca3 --- /dev/null +++ b/net/sunrpc/debugfs.c | |||
@@ -0,0 +1,191 @@ | |||
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 | } | ||
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index f632e476ab6c..e37fbed87956 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c | |||
@@ -97,6 +97,11 @@ init_sunrpc(void) | |||
97 | err = register_rpc_pipefs(); | 97 | err = register_rpc_pipefs(); |
98 | if (err) | 98 | if (err) |
99 | goto out4; | 99 | goto out4; |
100 | |||
101 | err = sunrpc_debugfs_init(); | ||
102 | if (err) | ||
103 | goto out5; | ||
104 | |||
100 | #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) | 105 | #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) |
101 | rpc_register_sysctl(); | 106 | rpc_register_sysctl(); |
102 | #endif | 107 | #endif |
@@ -104,6 +109,8 @@ init_sunrpc(void) | |||
104 | init_socket_xprt(); /* clnt sock transport */ | 109 | init_socket_xprt(); /* clnt sock transport */ |
105 | return 0; | 110 | return 0; |
106 | 111 | ||
112 | out5: | ||
113 | unregister_rpc_pipefs(); | ||
107 | out4: | 114 | out4: |
108 | unregister_pernet_subsys(&sunrpc_net_ops); | 115 | unregister_pernet_subsys(&sunrpc_net_ops); |
109 | out3: | 116 | out3: |
@@ -120,6 +127,7 @@ cleanup_sunrpc(void) | |||
120 | rpcauth_remove_module(); | 127 | rpcauth_remove_module(); |
121 | cleanup_socket_xprt(); | 128 | cleanup_socket_xprt(); |
122 | svc_cleanup_xprt_sock(); | 129 | svc_cleanup_xprt_sock(); |
130 | sunrpc_debugfs_exit(); | ||
123 | unregister_rpc_pipefs(); | 131 | unregister_rpc_pipefs(); |
124 | rpc_destroy_mempool(); | 132 | rpc_destroy_mempool(); |
125 | unregister_pernet_subsys(&sunrpc_net_ops); | 133 | unregister_pernet_subsys(&sunrpc_net_ops); |