]> git.proxmox.com Git - mirror_spl.git/blame - modules/spl/spl-proc.c
First commit of lustre style internal debug support. These
[mirror_spl.git] / modules / spl / spl-proc.c
CommitLineData
57d1b188 1#include <linux/proc_fs.h>
2#include <linux/kmod.h>
3#include <linux/uaccess.h>
4#include <linux/ctype.h>
5#include <linux/sysctl.h>
6#include <sys/sysmacros.h>
7#include <sys/kmem.h>
8#include <sys/debug.h>
9#include "config.h"
10
11#ifdef DEBUG_SUBSYSTEM
12#undef DEBUG_SUBSYSTEM
13#endif
14
15#define DEBUG_SUBSYSTEM S_PROC
16
17static struct ctl_table_header *spl_header = NULL;
18static unsigned long table_min = 0;
19static unsigned long table_max = ~0;
20
21#define CTL_SPL 0x87
22enum {
23 CTL_DEBUG_SUBSYS = 1, /* Debug subsystem */
24 CTL_DEBUG_MASK, /* Debug mask */
25 CTL_DEBUG_PRINTK, /* Force all messages to console */
26 CTL_DEBUG_MB, /* Debug buffer size */
27 CTL_DEBUG_BINARY, /* Include binary data in buffer */
28 CTL_DEBUG_CATASTROPHE, /* Set if we have BUG'd or panic'd */
29 CTL_DEBUG_PANIC_ON_BUG, /* Set if we should panic on BUG */
30 CTL_DEBUG_PATH, /* Dump log location */
31 CTL_DEBUG_DUMP, /* Dump debug buffer to file */
32 CTL_DEBUG_FORCE_BUG, /* Hook to force a BUG */
33 CTL_CONSOLE_RATELIMIT, /* Ratelimit console messages */
34 CTL_CONSOLE_MAX_DELAY_CS, /* Max delay at which we skip messages */
35 CTL_CONSOLE_MIN_DELAY_CS, /* Init delay at which we skip messages */
36 CTL_CONSOLE_BACKOFF, /* Delay increase factor */
37 CTL_STACK_SIZE, /* Max observed stack size */
38#ifdef DEBUG_KMEM
39 CTL_KMEM_KMEMUSED, /* Crrently alloc'd kmem bytes */
40 CTL_KMEM_KMEMMAX, /* Max alloc'd by kmem bytes */
41 CTL_KMEM_VMEMUSED, /* Currently alloc'd vmem bytes */
42 CTL_KMEM_VMEMMAX, /* Max alloc'd by vmem bytes */
43#endif
44 CTL_HOSTID, /* Host id reported by /usr/bin/hostid */
45 CTL_HW_SERIAL, /* Hardware serial number from hostid */
46};
47
48static int
49proc_copyin_string(char *kbuffer, int kbuffer_size,
50 const char *ubuffer, int ubuffer_size)
51{
52 int size;
53
54 if (ubuffer_size > kbuffer_size)
55 return -EOVERFLOW;
56
57 if (copy_from_user((void *)kbuffer, (void *)ubuffer, ubuffer_size))
58 return -EFAULT;
59
60 /* strip trailing whitespace */
61 size = strnlen(kbuffer, ubuffer_size);
62 while (size-- >= 0)
63 if (!isspace(kbuffer[size]))
64 break;
65
66 /* empty string */
67 if (size < 0)
68 return -EINVAL;
69
70 /* no space to terminate */
71 if (size == kbuffer_size)
72 return -EOVERFLOW;
73
74 kbuffer[size + 1] = 0;
75 return 0;
76}
77
78static int
79proc_copyout_string(char *ubuffer, int ubuffer_size,
80 const char *kbuffer, char *append)
81{
82 /* NB if 'append' != NULL, it's a single character to append to the
83 * copied out string - usually "\n", for /proc entries and
84 * (i.e. a terminating zero byte) for sysctl entries
85 */
86 int size = MIN(strlen(kbuffer), ubuffer_size);
87
88 if (copy_to_user(ubuffer, kbuffer, size))
89 return -EFAULT;
90
91 if (append != NULL && size < ubuffer_size) {
92 if (copy_to_user(ubuffer + size, append, 1))
93 return -EFAULT;
94
95 size++;
96 }
97
98 return size;
99}
100
101static int
102proc_dobitmasks(struct ctl_table *table, int write, struct file *filp,
103 void __user *buffer, size_t *lenp, loff_t *ppos)
104{
105 unsigned long *mask = table->data;
106 int is_subsys = (mask == &spl_debug_subsys) ? 1 : 0;
107 int is_printk = (mask == &spl_debug_printk) ? 1 : 0;
108 int size = 512, rc;
109 char *str;
110 ENTRY;
111
112 str = kmem_alloc(size, KM_SLEEP);
113 if (str == NULL)
114 RETURN(-ENOMEM);
115
116 if (write) {
117 rc = proc_copyin_string(str, size, buffer, *lenp);
118 if (rc < 0)
119 RETURN(rc);
120
121 rc = spl_debug_str2mask(mask, str, is_subsys);
122 /* Always print BUG/ASSERT to console, so keep this mask */
123 if (is_printk)
124 *mask |= D_EMERG;
125
126 *ppos += *lenp;
127 } else {
128 rc = spl_debug_mask2str(str, size, *mask, is_subsys);
129 if (*ppos >= rc)
130 rc = 0;
131 else
132 rc = proc_copyout_string(buffer, *lenp,
133 str + *ppos, "\n");
134 if (rc >= 0) {
135 *lenp = rc;
136 *ppos += rc;
137 }
138 }
139
140 kmem_free(str, size);
141 RETURN(rc);
142}
143
144static int
145proc_debug_mb(struct ctl_table *table, int write, struct file *filp,
146 void __user *buffer, size_t *lenp, loff_t *ppos)
147{
148 char str[32];
149 int rc, len;
150 ENTRY;
151
152 if (write) {
153 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
154 if (rc < 0)
155 RETURN(rc);
156
157 rc = spl_debug_set_mb(simple_strtoul(str, NULL, 0));
158 *ppos += *lenp;
159 } else {
160 len = snprintf(str, sizeof(str), "%d", spl_debug_get_mb());
161 if (*ppos >= len)
162 rc = 0;
163 else
164 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
165
166 if (rc >= 0) {
167 *lenp = rc;
168 *ppos += rc;
169 }
170 }
171
172 RETURN(rc);
173}
174
175static int
176proc_dump_kernel(struct ctl_table *table, int write, struct file *filp,
177 void __user *buffer, size_t *lenp, loff_t *ppos)
178{
179 ENTRY;
180
181 if (write) {
182 spl_debug_dumplog();
183 *ppos += *lenp;
184 } else {
185 *lenp = 0;
186 }
187
188 RETURN(0);
189}
190
191static int
192proc_force_bug(struct ctl_table *table, int write, struct file *filp,
193 void __user *buffer, size_t *lenp, loff_t *ppos)
194{
195 ENTRY;
196
197 if (write) {
198 CERROR("Crashing due to forced BUG\n");
199 BUG();
200 /* Unreachable */
201 } else {
202 *lenp = 0;
203 }
204
205 RETURN(0);
206}
207
208static int
209proc_console_max_delay_cs(struct ctl_table *table, int write, struct file *filp,
210 void __user *buffer, size_t *lenp, loff_t *ppos)
211{
212 int rc, max_delay_cs;
213 struct ctl_table dummy = *table;
214 long d;
215 ENTRY;
216
217 dummy.data = &max_delay_cs;
218 dummy.proc_handler = &proc_dointvec;
219
220 if (write) {
221 max_delay_cs = 0;
222 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
223 if (rc < 0)
224 RETURN(rc);
225
226 if (max_delay_cs <= 0)
227 RETURN(-EINVAL);
228
229 d = (max_delay_cs * HZ) / 100;
230 if (d == 0 || d < spl_console_min_delay)
231 RETURN(-EINVAL);
232
233 spl_console_max_delay = d;
234 } else {
235 max_delay_cs = (spl_console_max_delay * 100) / HZ;
236 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
237 }
238
239 RETURN(rc);
240}
241
242static int
243proc_console_min_delay_cs(struct ctl_table *table, int write, struct file *filp,
244 void __user *buffer, size_t *lenp, loff_t *ppos)
245{
246 int rc, min_delay_cs;
247 struct ctl_table dummy = *table;
248 long d;
249 ENTRY;
250
251 dummy.data = &min_delay_cs;
252 dummy.proc_handler = &proc_dointvec;
253
254 if (write) {
255 min_delay_cs = 0;
256 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
257 if (rc < 0)
258 RETURN(rc);
259
260 if (min_delay_cs <= 0)
261 RETURN(-EINVAL);
262
263 d = (min_delay_cs * HZ) / 100;
264 if (d == 0 || d > spl_console_max_delay)
265 RETURN(-EINVAL);
266
267 spl_console_min_delay = d;
268 } else {
269 min_delay_cs = (spl_console_min_delay * 100) / HZ;
270 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
271 }
272
273 RETURN(rc);
274}
275
276static int
277proc_console_backoff(struct ctl_table *table, int write, struct file *filp,
278 void __user *buffer, size_t *lenp, loff_t *ppos)
279{
280 int rc, backoff;
281 struct ctl_table dummy = *table;
282 ENTRY;
283
284 dummy.data = &backoff;
285 dummy.proc_handler = &proc_dointvec;
286
287 if (write) {
288 backoff = 0;
289 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
290 if (rc < 0)
291 RETURN(rc);
292
293 if (backoff <= 0)
294 RETURN(-EINVAL);
295
296 spl_console_backoff = backoff;
297 } else {
298 backoff = spl_console_backoff;
299 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
300 }
301
302 RETURN(rc);
303}
304
305static int
306proc_doatomic64(struct ctl_table *table, int write, struct file *filp,
307 void __user *buffer, size_t *lenp, loff_t *ppos)
308{
309 int rc = 0;
310 unsigned long min = 0, max = ~0, val;
311 struct ctl_table dummy = *table;
312 ENTRY;
313
314 dummy.data = &val;
315 dummy.proc_handler = &proc_dointvec;
316 dummy.extra1 = &min;
317 dummy.extra2 = &max;
318
319 if (write) {
320 *ppos += *lenp;
321 } else {
322 val = atomic_read((atomic64_t *)table->data);
323 rc = proc_doulongvec_minmax(&dummy, write, filp,
324 buffer, lenp, ppos);
325 }
326
327 RETURN(rc);
328}
329
330static int
331proc_dohostid(struct ctl_table *table, int write, struct file *filp,
332 void __user *buffer, size_t *lenp, loff_t *ppos)
333{
334 int len, rc = 0;
335 unsigned long val;
336 char *end, str[32];
337 ENTRY;
338
339 if (write) {
340 /* We can't use proc_doulongvec_minmax() in the write
341 * case hear because hostid while a hex value has no
342 * leading 0x which confuses the helper function. */
343 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
344 if (rc < 0)
345 RETURN(rc);
346
347 val = simple_strtoul(str, &end, 16);
348 if (str == end)
349 RETURN(-EINVAL);
350
351 spl_hostid = val;
352 sprintf(spl_hw_serial, "%lu", ((long)val >= 0) ? val : -val);
353 *ppos += *lenp;
354 } else {
355 len = snprintf(str, sizeof(str), "%lx", spl_hostid);
356 if (*ppos >= len)
357 rc = 0;
358 else
359 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
360
361 if (rc >= 0) {
362 *lenp = rc;
363 *ppos += rc;
364 }
365 }
366
367 RETURN(rc);
368}
369
370static struct ctl_table spl_table[] = {
371 /* NB No .strategy entries have been provided since
372 * sysctl(8) prefers to go via /proc for portability.
373 */
374 {
375 .ctl_name = CTL_DEBUG_SUBSYS,
376 .procname = "debug_subsystem",
377 .data = &spl_debug_subsys,
378 .maxlen = sizeof(unsigned long),
379 .mode = 0644,
380 .proc_handler = &proc_dobitmasks
381 },
382 {
383 .ctl_name = CTL_DEBUG_MASK,
384 .procname = "debug_mask",
385 .data = &spl_debug_mask,
386 .maxlen = sizeof(unsigned long),
387 .mode = 0644,
388 .proc_handler = &proc_dobitmasks
389 },
390 {
391 .ctl_name = CTL_DEBUG_PRINTK,
392 .procname = "debug_printk",
393 .data = &spl_debug_printk,
394 .maxlen = sizeof(unsigned long),
395 .mode = 0644,
396 .proc_handler = &proc_dobitmasks
397 },
398 {
399 .ctl_name = CTL_DEBUG_MB,
400 .procname = "debug_mb",
401 .mode = 0644,
402 .proc_handler = &proc_debug_mb,
403 },
404 {
405 .ctl_name = CTL_DEBUG_BINARY,
406 .procname = "debug_binary",
407 .data = &spl_debug_binary,
408 .maxlen = sizeof(int),
409 .mode = 0644,
410 .proc_handler = &proc_dointvec,
411 },
412 {
413 .ctl_name = CTL_DEBUG_CATASTROPHE,
414 .procname = "catastrophe",
415 .data = &spl_debug_catastrophe,
416 .maxlen = sizeof(int),
417 .mode = 0444,
418 .proc_handler = &proc_dointvec,
419 },
420 {
421 .ctl_name = CTL_DEBUG_PANIC_ON_BUG,
422 .procname = "panic_on_bug",
423 .data = &spl_debug_panic_on_bug,
424 .maxlen = sizeof(int),
425 .mode = 0644,
426 .proc_handler = &proc_dointvec
427 },
428 {
429 .ctl_name = CTL_DEBUG_PATH,
430 .procname = "debug_path",
431 .data = spl_debug_file_path,
432 .maxlen = sizeof(spl_debug_file_path),
433 .mode = 0644,
434 .proc_handler = &proc_dostring,
435 },
436 {
437 .ctl_name = CTL_DEBUG_DUMP,
438 .procname = "debug_dump",
439 .mode = 0200,
440 .proc_handler = &proc_dump_kernel,
441 },
442 { .ctl_name = CTL_DEBUG_FORCE_BUG,
443 .procname = "force_bug",
444 .mode = 0200,
445 .proc_handler = &proc_force_bug,
446 },
447 {
448 .ctl_name = CTL_CONSOLE_RATELIMIT,
449 .procname = "console_ratelimit",
450 .data = &spl_console_ratelimit,
451 .maxlen = sizeof(int),
452 .mode = 0644,
453 .proc_handler = &proc_dointvec,
454 },
455 {
456 .ctl_name = CTL_CONSOLE_MAX_DELAY_CS,
457 .procname = "console_max_delay_centisecs",
458 .maxlen = sizeof(int),
459 .mode = 0644,
460 .proc_handler = &proc_console_max_delay_cs,
461 },
462 {
463 .ctl_name = CTL_CONSOLE_MIN_DELAY_CS,
464 .procname = "console_min_delay_centisecs",
465 .maxlen = sizeof(int),
466 .mode = 0644,
467 .proc_handler = &proc_console_min_delay_cs,
468 },
469 {
470 .ctl_name = CTL_CONSOLE_BACKOFF,
471 .procname = "console_backoff",
472 .maxlen = sizeof(int),
473 .mode = 0644,
474 .proc_handler = &proc_console_backoff,
475 },
476 {
477 .ctl_name = CTL_STACK_SIZE,
478 .procname = "stack_max",
479 .data = &spl_debug_stack,
480 .maxlen = sizeof(int),
481 .mode = 0444,
482 .proc_handler = &proc_dointvec,
483 },
484#ifdef DEBUG_KMEM
485 {
486 .ctl_name = CTL_KMEM_KMEMUSED,
487 .procname = "kmem_used",
488 .data = &kmem_alloc_used,
489 .maxlen = sizeof(atomic64_t),
490 .mode = 0444,
491 .proc_handler = &proc_doatomic64,
492 },
493 {
494 .ctl_name = CTL_KMEM_KMEMMAX,
495 .procname = "kmem_max",
496 .data = &kmem_alloc_max,
497 .maxlen = sizeof(unsigned long),
498 .extra1 = &table_min,
499 .extra2 = &table_max,
500 .mode = 0444,
501 .proc_handler = &proc_doulongvec_minmax,
502 },
503 {
504 .ctl_name = CTL_KMEM_VMEMUSED,
505 .procname = "vmem_used",
506 .data = &vmem_alloc_used,
507 .maxlen = sizeof(atomic64_t),
508 .mode = 0444,
509 .proc_handler = &proc_doatomic64,
510 },
511 {
512 .ctl_name = CTL_KMEM_VMEMMAX,
513 .procname = "vmem_max",
514 .data = &vmem_alloc_max,
515 .maxlen = sizeof(unsigned long),
516 .extra1 = &table_min,
517 .extra2 = &table_max,
518 .mode = 0444,
519 .proc_handler = &proc_doulongvec_minmax,
520 },
521#endif
522 {
523 .ctl_name = CTL_HOSTID,
524 .procname = "hostid",
525 .data = &spl_hostid,
526 .maxlen = sizeof(unsigned long),
527 .mode = 0644,
528 .proc_handler = &proc_dohostid,
529 },
530 {
531 .ctl_name = CTL_HW_SERIAL,
532 .procname = "hw_serial",
533 .data = spl_hw_serial,
534 .maxlen = sizeof(spl_hw_serial),
535 .mode = 0444,
536 .proc_handler = &proc_dostring,
537 },
538 { 0 },
539};
540
541static struct ctl_table spl_dir_table[] = {
542 {
543 .ctl_name = CTL_SPL,
544 .procname = "spl",
545 .mode = 0555,
546 .child = spl_table,
547 },
548 {0}
549};
550
551int
552proc_init(void)
553{
554 ENTRY;
555
556#ifdef CONFIG_SYSCTL
557 spl_header = register_sysctl_table(spl_dir_table, 0);
558 if (spl_header == NULL)
559 RETURN(-EUNATCH);
560#endif
561 RETURN(0);
562}
563
564void
565proc_fini(void)
566{
567 ENTRY;
568
569#ifdef CONFIG_SYSCTL
570 ASSERT(spl_header != NULL);
571 unregister_sysctl_table(spl_header);
572#endif
573 EXIT;
574}