diff options
Diffstat (limited to 'fs/orangefs/orangefs-debugfs.c')
-rw-r--r-- | fs/orangefs/orangefs-debugfs.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c new file mode 100644 index 000000000000..19670b8b4053 --- /dev/null +++ b/fs/orangefs/orangefs-debugfs.c | |||
@@ -0,0 +1,455 @@ | |||
1 | /* | ||
2 | * What: /sys/kernel/debug/orangefs/debug-help | ||
3 | * Date: June 2015 | ||
4 | * Contact: Mike Marshall <hubcap@omnibond.com> | ||
5 | * Description: | ||
6 | * List of client and kernel debug keywords. | ||
7 | * | ||
8 | * | ||
9 | * What: /sys/kernel/debug/orangefs/client-debug | ||
10 | * Date: June 2015 | ||
11 | * Contact: Mike Marshall <hubcap@omnibond.com> | ||
12 | * Description: | ||
13 | * Debug setting for "the client", the userspace | ||
14 | * helper for the kernel module. | ||
15 | * | ||
16 | * | ||
17 | * What: /sys/kernel/debug/orangefs/kernel-debug | ||
18 | * Date: June 2015 | ||
19 | * Contact: Mike Marshall <hubcap@omnibond.com> | ||
20 | * Description: | ||
21 | * Debug setting for the orangefs kernel module. | ||
22 | * | ||
23 | * Any of the keywords, or comma-separated lists | ||
24 | * of keywords, from debug-help can be catted to | ||
25 | * client-debug or kernel-debug. | ||
26 | * | ||
27 | * "none", "all" and "verbose" are special keywords | ||
28 | * for client-debug. Setting client-debug to "all" | ||
29 | * is kind of like trying to drink water from a | ||
30 | * fire hose, "verbose" triggers most of the same | ||
31 | * output except for the constant flow of output | ||
32 | * from the main wait loop. | ||
33 | * | ||
34 | * "none" and "all" are similar settings for kernel-debug | ||
35 | * no need for a "verbose". | ||
36 | */ | ||
37 | #include <linux/debugfs.h> | ||
38 | #include <linux/slab.h> | ||
39 | |||
40 | #include <linux/uaccess.h> | ||
41 | |||
42 | #include "orangefs-debugfs.h" | ||
43 | #include "protocol.h" | ||
44 | #include "orangefs-kernel.h" | ||
45 | |||
46 | static int orangefs_debug_disabled = 1; | ||
47 | |||
48 | static int orangefs_debug_help_open(struct inode *, struct file *); | ||
49 | |||
50 | const struct file_operations debug_help_fops = { | ||
51 | .open = orangefs_debug_help_open, | ||
52 | .read = seq_read, | ||
53 | .release = seq_release, | ||
54 | .llseek = seq_lseek, | ||
55 | }; | ||
56 | |||
57 | static void *help_start(struct seq_file *, loff_t *); | ||
58 | static void *help_next(struct seq_file *, void *, loff_t *); | ||
59 | static void help_stop(struct seq_file *, void *); | ||
60 | static int help_show(struct seq_file *, void *); | ||
61 | |||
62 | static const struct seq_operations help_debug_ops = { | ||
63 | .start = help_start, | ||
64 | .next = help_next, | ||
65 | .stop = help_stop, | ||
66 | .show = help_show, | ||
67 | }; | ||
68 | |||
69 | /* | ||
70 | * Used to protect data in ORANGEFS_KMOD_DEBUG_FILE and | ||
71 | * ORANGEFS_KMOD_DEBUG_FILE. | ||
72 | */ | ||
73 | static DEFINE_MUTEX(orangefs_debug_lock); | ||
74 | |||
75 | int orangefs_debug_open(struct inode *, struct file *); | ||
76 | |||
77 | static ssize_t orangefs_debug_read(struct file *, | ||
78 | char __user *, | ||
79 | size_t, | ||
80 | loff_t *); | ||
81 | |||
82 | static ssize_t orangefs_debug_write(struct file *, | ||
83 | const char __user *, | ||
84 | size_t, | ||
85 | loff_t *); | ||
86 | |||
87 | static const struct file_operations kernel_debug_fops = { | ||
88 | .open = orangefs_debug_open, | ||
89 | .read = orangefs_debug_read, | ||
90 | .write = orangefs_debug_write, | ||
91 | .llseek = generic_file_llseek, | ||
92 | }; | ||
93 | |||
94 | /* | ||
95 | * initialize kmod debug operations, create orangefs debugfs dir and | ||
96 | * ORANGEFS_KMOD_DEBUG_HELP_FILE. | ||
97 | */ | ||
98 | int orangefs_debugfs_init(void) | ||
99 | { | ||
100 | |||
101 | int rc = -ENOMEM; | ||
102 | |||
103 | debug_dir = debugfs_create_dir("orangefs", NULL); | ||
104 | if (!debug_dir) { | ||
105 | pr_info("%s: debugfs_create_dir failed.\n", __func__); | ||
106 | goto out; | ||
107 | } | ||
108 | |||
109 | help_file_dentry = debugfs_create_file(ORANGEFS_KMOD_DEBUG_HELP_FILE, | ||
110 | 0444, | ||
111 | debug_dir, | ||
112 | debug_help_string, | ||
113 | &debug_help_fops); | ||
114 | if (!help_file_dentry) { | ||
115 | pr_info("%s: debugfs_create_file failed.\n", __func__); | ||
116 | goto out; | ||
117 | } | ||
118 | |||
119 | orangefs_debug_disabled = 0; | ||
120 | rc = 0; | ||
121 | |||
122 | out: | ||
123 | |||
124 | return rc; | ||
125 | } | ||
126 | |||
127 | void orangefs_debugfs_cleanup(void) | ||
128 | { | ||
129 | if (debug_dir) | ||
130 | debugfs_remove_recursive(debug_dir); | ||
131 | } | ||
132 | |||
133 | /* open ORANGEFS_KMOD_DEBUG_HELP_FILE */ | ||
134 | static int orangefs_debug_help_open(struct inode *inode, struct file *file) | ||
135 | { | ||
136 | int rc = -ENODEV; | ||
137 | int ret; | ||
138 | |||
139 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
140 | "orangefs_debug_help_open: start\n"); | ||
141 | |||
142 | if (orangefs_debug_disabled) | ||
143 | goto out; | ||
144 | |||
145 | ret = seq_open(file, &help_debug_ops); | ||
146 | if (ret) | ||
147 | goto out; | ||
148 | |||
149 | ((struct seq_file *)(file->private_data))->private = inode->i_private; | ||
150 | |||
151 | rc = 0; | ||
152 | |||
153 | out: | ||
154 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
155 | "orangefs_debug_help_open: rc:%d:\n", | ||
156 | rc); | ||
157 | return rc; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * I think start always gets called again after stop. Start | ||
162 | * needs to return NULL when it is done. The whole "payload" | ||
163 | * in this case is a single (long) string, so by the second | ||
164 | * time we get to start (pos = 1), we're done. | ||
165 | */ | ||
166 | static void *help_start(struct seq_file *m, loff_t *pos) | ||
167 | { | ||
168 | void *payload = NULL; | ||
169 | |||
170 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_start: start\n"); | ||
171 | |||
172 | if (*pos == 0) | ||
173 | payload = m->private; | ||
174 | |||
175 | return payload; | ||
176 | } | ||
177 | |||
178 | static void *help_next(struct seq_file *m, void *v, loff_t *pos) | ||
179 | { | ||
180 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_next: start\n"); | ||
181 | |||
182 | return NULL; | ||
183 | } | ||
184 | |||
185 | static void help_stop(struct seq_file *m, void *p) | ||
186 | { | ||
187 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_stop: start\n"); | ||
188 | } | ||
189 | |||
190 | static int help_show(struct seq_file *m, void *v) | ||
191 | { | ||
192 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "help_show: start\n"); | ||
193 | |||
194 | seq_puts(m, v); | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * initialize the kernel-debug file. | ||
201 | */ | ||
202 | int orangefs_kernel_debug_init(void) | ||
203 | { | ||
204 | int rc = -ENOMEM; | ||
205 | struct dentry *ret; | ||
206 | char *k_buffer = NULL; | ||
207 | |||
208 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); | ||
209 | |||
210 | k_buffer = kzalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | ||
211 | if (!k_buffer) | ||
212 | goto out; | ||
213 | |||
214 | if (strlen(kernel_debug_string) + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { | ||
215 | strcpy(k_buffer, kernel_debug_string); | ||
216 | strcat(k_buffer, "\n"); | ||
217 | } else { | ||
218 | strcpy(k_buffer, "none\n"); | ||
219 | pr_info("%s: overflow 1!\n", __func__); | ||
220 | } | ||
221 | |||
222 | ret = debugfs_create_file(ORANGEFS_KMOD_DEBUG_FILE, | ||
223 | 0444, | ||
224 | debug_dir, | ||
225 | k_buffer, | ||
226 | &kernel_debug_fops); | ||
227 | if (!ret) { | ||
228 | pr_info("%s: failed to create %s.\n", | ||
229 | __func__, | ||
230 | ORANGEFS_KMOD_DEBUG_FILE); | ||
231 | goto out; | ||
232 | } | ||
233 | |||
234 | rc = 0; | ||
235 | |||
236 | out: | ||
237 | |||
238 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: rc:%d:\n", __func__, rc); | ||
239 | return rc; | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * initialize the client-debug file. | ||
244 | */ | ||
245 | int orangefs_client_debug_init(void) | ||
246 | { | ||
247 | |||
248 | int rc = -ENOMEM; | ||
249 | char *c_buffer = NULL; | ||
250 | |||
251 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: start\n", __func__); | ||
252 | |||
253 | c_buffer = kzalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | ||
254 | if (!c_buffer) | ||
255 | goto out; | ||
256 | |||
257 | if (strlen(client_debug_string) + 1 < ORANGEFS_MAX_DEBUG_STRING_LEN) { | ||
258 | strcpy(c_buffer, client_debug_string); | ||
259 | strcat(c_buffer, "\n"); | ||
260 | } else { | ||
261 | strcpy(c_buffer, "none\n"); | ||
262 | pr_info("%s: overflow! 2\n", __func__); | ||
263 | } | ||
264 | |||
265 | client_debug_dentry = debugfs_create_file(ORANGEFS_CLIENT_DEBUG_FILE, | ||
266 | 0444, | ||
267 | debug_dir, | ||
268 | c_buffer, | ||
269 | &kernel_debug_fops); | ||
270 | if (!client_debug_dentry) { | ||
271 | pr_info("%s: failed to create updated %s.\n", | ||
272 | __func__, | ||
273 | ORANGEFS_CLIENT_DEBUG_FILE); | ||
274 | goto out; | ||
275 | } | ||
276 | |||
277 | rc = 0; | ||
278 | |||
279 | out: | ||
280 | |||
281 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "%s: rc:%d:\n", __func__, rc); | ||
282 | return rc; | ||
283 | } | ||
284 | |||
285 | /* open ORANGEFS_KMOD_DEBUG_FILE or ORANGEFS_CLIENT_DEBUG_FILE.*/ | ||
286 | int orangefs_debug_open(struct inode *inode, struct file *file) | ||
287 | { | ||
288 | int rc = -ENODEV; | ||
289 | |||
290 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
291 | "%s: orangefs_debug_disabled: %d\n", | ||
292 | __func__, | ||
293 | orangefs_debug_disabled); | ||
294 | |||
295 | if (orangefs_debug_disabled) | ||
296 | goto out; | ||
297 | |||
298 | rc = 0; | ||
299 | mutex_lock(&orangefs_debug_lock); | ||
300 | file->private_data = inode->i_private; | ||
301 | mutex_unlock(&orangefs_debug_lock); | ||
302 | |||
303 | out: | ||
304 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
305 | "orangefs_debug_open: rc: %d\n", | ||
306 | rc); | ||
307 | return rc; | ||
308 | } | ||
309 | |||
310 | static ssize_t orangefs_debug_read(struct file *file, | ||
311 | char __user *ubuf, | ||
312 | size_t count, | ||
313 | loff_t *ppos) | ||
314 | { | ||
315 | char *buf; | ||
316 | int sprintf_ret; | ||
317 | ssize_t read_ret = -ENOMEM; | ||
318 | |||
319 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, "orangefs_debug_read: start\n"); | ||
320 | |||
321 | buf = kmalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | ||
322 | if (!buf) | ||
323 | goto out; | ||
324 | |||
325 | mutex_lock(&orangefs_debug_lock); | ||
326 | sprintf_ret = sprintf(buf, "%s", (char *)file->private_data); | ||
327 | mutex_unlock(&orangefs_debug_lock); | ||
328 | |||
329 | read_ret = simple_read_from_buffer(ubuf, count, ppos, buf, sprintf_ret); | ||
330 | |||
331 | kfree(buf); | ||
332 | |||
333 | out: | ||
334 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
335 | "orangefs_debug_read: ret: %zu\n", | ||
336 | read_ret); | ||
337 | |||
338 | return read_ret; | ||
339 | } | ||
340 | |||
341 | static ssize_t orangefs_debug_write(struct file *file, | ||
342 | const char __user *ubuf, | ||
343 | size_t count, | ||
344 | loff_t *ppos) | ||
345 | { | ||
346 | char *buf; | ||
347 | int rc = -EFAULT; | ||
348 | size_t silly = 0; | ||
349 | char *debug_string; | ||
350 | struct orangefs_kernel_op_s *new_op = NULL; | ||
351 | struct client_debug_mask c_mask = { NULL, 0, 0 }; | ||
352 | |||
353 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
354 | "orangefs_debug_write: %s\n", | ||
355 | file->f_path.dentry->d_name.name); | ||
356 | |||
357 | /* | ||
358 | * Thwart users who try to jamb a ridiculous number | ||
359 | * of bytes into the debug file... | ||
360 | */ | ||
361 | if (count > ORANGEFS_MAX_DEBUG_STRING_LEN + 1) { | ||
362 | silly = count; | ||
363 | count = ORANGEFS_MAX_DEBUG_STRING_LEN + 1; | ||
364 | } | ||
365 | |||
366 | buf = kzalloc(ORANGEFS_MAX_DEBUG_STRING_LEN, GFP_KERNEL); | ||
367 | if (!buf) | ||
368 | goto out; | ||
369 | |||
370 | if (copy_from_user(buf, ubuf, count - 1)) { | ||
371 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
372 | "%s: copy_from_user failed!\n", | ||
373 | __func__); | ||
374 | goto out; | ||
375 | } | ||
376 | |||
377 | /* | ||
378 | * Map the keyword string from userspace into a valid debug mask. | ||
379 | * The mapping process involves mapping the human-inputted string | ||
380 | * into a valid mask, and then rebuilding the string from the | ||
381 | * verified valid mask. | ||
382 | * | ||
383 | * A service operation is required to set a new client-side | ||
384 | * debug mask. | ||
385 | */ | ||
386 | if (!strcmp(file->f_path.dentry->d_name.name, | ||
387 | ORANGEFS_KMOD_DEBUG_FILE)) { | ||
388 | debug_string_to_mask(buf, &gossip_debug_mask, 0); | ||
389 | debug_mask_to_string(&gossip_debug_mask, 0); | ||
390 | debug_string = kernel_debug_string; | ||
391 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
392 | "New kernel debug string is %s\n", | ||
393 | kernel_debug_string); | ||
394 | } else { | ||
395 | /* Can't reset client debug mask if client is not running. */ | ||
396 | if (is_daemon_in_service()) { | ||
397 | pr_info("%s: Client not running :%d:\n", | ||
398 | __func__, | ||
399 | is_daemon_in_service()); | ||
400 | goto out; | ||
401 | } | ||
402 | |||
403 | debug_string_to_mask(buf, &c_mask, 1); | ||
404 | debug_mask_to_string(&c_mask, 1); | ||
405 | debug_string = client_debug_string; | ||
406 | |||
407 | new_op = op_alloc(ORANGEFS_VFS_OP_PARAM); | ||
408 | if (!new_op) { | ||
409 | pr_info("%s: op_alloc failed!\n", __func__); | ||
410 | goto out; | ||
411 | } | ||
412 | |||
413 | new_op->upcall.req.param.op = | ||
414 | ORANGEFS_PARAM_REQUEST_OP_TWO_MASK_VALUES; | ||
415 | new_op->upcall.req.param.type = ORANGEFS_PARAM_REQUEST_SET; | ||
416 | memset(new_op->upcall.req.param.s_value, | ||
417 | 0, | ||
418 | ORANGEFS_MAX_DEBUG_STRING_LEN); | ||
419 | sprintf(new_op->upcall.req.param.s_value, | ||
420 | "%llx %llx\n", | ||
421 | c_mask.mask1, | ||
422 | c_mask.mask2); | ||
423 | |||
424 | /* service_operation returns 0 on success... */ | ||
425 | rc = service_operation(new_op, | ||
426 | "orangefs_param", | ||
427 | ORANGEFS_OP_INTERRUPTIBLE); | ||
428 | |||
429 | if (rc) | ||
430 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
431 | "%s: service_operation failed! rc:%d:\n", | ||
432 | __func__, | ||
433 | rc); | ||
434 | |||
435 | op_release(new_op); | ||
436 | } | ||
437 | |||
438 | mutex_lock(&orangefs_debug_lock); | ||
439 | memset(file->f_inode->i_private, 0, ORANGEFS_MAX_DEBUG_STRING_LEN); | ||
440 | sprintf((char *)file->f_inode->i_private, "%s\n", debug_string); | ||
441 | mutex_unlock(&orangefs_debug_lock); | ||
442 | |||
443 | *ppos += count; | ||
444 | if (silly) | ||
445 | rc = silly; | ||
446 | else | ||
447 | rc = count; | ||
448 | |||
449 | out: | ||
450 | gossip_debug(GOSSIP_DEBUGFS_DEBUG, | ||
451 | "orangefs_debug_write: rc: %d\n", | ||
452 | rc); | ||
453 | kfree(buf); | ||
454 | return rc; | ||
455 | } | ||