]> git.proxmox.com Git - mirror_spl.git/blame - modules/spl/spl-proc.c
Fixes:
[mirror_spl.git] / modules / spl / spl-proc.c
CommitLineData
715f6251 1/*
2 * This file is part of the SPL: Solaris Porting Layer.
3 *
4 * Copyright (c) 2008 Lawrence Livermore National Security, LLC.
5 * Produced at Lawrence Livermore National Laboratory
6 * Written by:
7 * Brian Behlendorf <behlendorf1@llnl.gov>,
8 * Herb Wartens <wartens2@llnl.gov>,
9 * Jim Garlick <garlick@llnl.gov>
10 * UCRL-CODE-235197
11 *
12 * This is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 */
26
04a479f7 27#include <sys/proc.h>
57d1b188 28
29#ifdef DEBUG_SUBSYSTEM
30#undef DEBUG_SUBSYSTEM
31#endif
32
33#define DEBUG_SUBSYSTEM S_PROC
34
404992e3 35#ifdef DEBUG_KMEM
57d1b188 36static unsigned long table_min = 0;
37static unsigned long table_max = ~0;
404992e3 38#endif
39
40#ifdef CONFIG_SYSCTL
41static struct ctl_table_header *spl_header = NULL;
c30df9c8 42#endif /* CONFIG_SYSCTL */
43
57d86234 44#if defined(DEBUG_MUTEX) || defined(DEBUG_KMEM) || defined(DEBUG_KSTAT)
c30df9c8 45static struct proc_dir_entry *proc_spl = NULL;
404992e3 46#ifdef DEBUG_MUTEX
c30df9c8 47static struct proc_dir_entry *proc_spl_mutex = NULL;
48static struct proc_dir_entry *proc_spl_mutex_stats = NULL;
49#endif /* DEBUG_MUTEX */
04a479f7 50#ifdef DEBUG_KMEM
c30df9c8 51static struct proc_dir_entry *proc_spl_kmem = NULL;
52#endif /* DEBUG_KMEM */
04a479f7 53#ifdef DEBUG_KSTAT
c30df9c8 54struct proc_dir_entry *proc_spl_kstat = NULL;
55#endif /* DEBUG_KSTAT */
56#endif /* DEBUG_MUTEX || DEBUG_KMEM || DEBUG_KSTAT */
57d1b188 57
57d86234 58#ifdef HAVE_CTL_UNNUMBERED
59
60#define CTL_SPL CTL_UNNUMBERED
61#define CTL_SPL_DEBUG CTL_UNNUMBERED
62#define CTL_SPL_MUTEX CTL_UNNUMBERED
63#define CTL_SPL_KMEM CTL_UNNUMBERED
64#define CTL_SPL_KSTAT CTL_UNNUMBERED
65
66#define CTL_VERSION CTL_UNNUMBERED /* Version */
67#define CTL_HOSTID CTL_UNNUMBERED /* Host id by /usr/bin/hostid */
68#define CTL_HW_SERIAL CTL_UNNUMBERED /* HW serial number by hostid */
69
70#define CTL_DEBUG_SUBSYS CTL_UNNUMBERED /* Debug subsystem */
71#define CTL_DEBUG_MASK CTL_UNNUMBERED /* Debug mask */
72#define CTL_DEBUG_PRINTK CTL_UNNUMBERED /* All messages to console */
73#define CTL_DEBUG_MB CTL_UNNUMBERED /* Debug buffer size */
74#define CTL_DEBUG_BINARY CTL_UNNUMBERED /* Binary data in buffer */
75#define CTL_DEBUG_CATASTROPHE CTL_UNNUMBERED /* Set if BUG'd or panic'd */
76#define CTL_DEBUG_PANIC_ON_BUG CTL_UNNUMBERED /* Should panic on BUG */
77#define CTL_DEBUG_PATH CTL_UNNUMBERED /* Dump log location */
78#define CTL_DEBUG_DUMP CTL_UNNUMBERED /* Dump debug buffer to file */
79#define CTL_DEBUG_FORCE_BUG CTL_UNNUMBERED /* Hook to force a BUG */
80#define CTL_DEBUG_STACK_SIZE CTL_UNNUMBERED /* Max observed stack size */
81
82#define CTL_CONSOLE_RATELIMIT CTL_UNNUMBERED /* Ratelimit console messages */
83#define CTL_CONSOLE_MAX_DELAY_CS CTL_UNNUMBERED /* Max delay skip messages */
84#define CTL_CONSOLE_MIN_DELAY_CS CTL_UNNUMBERED /* Init delay skip messages */
85#define CTL_CONSOLE_BACKOFF CTL_UNNUMBERED /* Delay increase factor */
86
87#ifdef DEBUG_KMEM
88#define CTL_KMEM_KMEMUSED CTL_UNNUMBERED /* Alloc'd kmem bytes */
89#define CTL_KMEM_KMEMMAX CTL_UNNUMBERED /* Max alloc'd by kmem bytes */
90#define CTL_KMEM_VMEMUSED CTL_UNNUMBERED /* Alloc'd vmem bytes */
91#define CTL_KMEM_VMEMMAX CTL_UNNUMBERED /* Max alloc'd by vmem bytes */
92#define CTL_KMEM_ALLOC_FAILED CTL_UNNUMBERED /* Cache allocations failed */
93#endif
94
95#define CTL_MUTEX_STATS CTL_UNNUMBERED /* Global mutex statistics */
96#define CTL_MUTEX_STATS_PER CTL_UNNUMBERED /* Per mutex statistics */
97#define CTL_MUTEX_SPIN_MAX CTL_UNNUMBERED /* Max mutex spin iterations */
98
99#else /* HAVE_CTL_UNNUMBERED */
100
9ab1ac14 101#define CTL_SPL 0x87
102#define CTL_SPL_DEBUG 0x88
103#define CTL_SPL_MUTEX 0x89
104#define CTL_SPL_KMEM 0x90
04a479f7 105#define CTL_SPL_KSTAT 0x91
9ab1ac14 106
57d1b188 107enum {
3561541c 108 CTL_VERSION = 1, /* Version */
9ab1ac14 109 CTL_HOSTID, /* Host id reported by /usr/bin/hostid */
110 CTL_HW_SERIAL, /* Hardware serial number from hostid */
111
112 CTL_DEBUG_SUBSYS, /* Debug subsystem */
57d1b188 113 CTL_DEBUG_MASK, /* Debug mask */
114 CTL_DEBUG_PRINTK, /* Force all messages to console */
115 CTL_DEBUG_MB, /* Debug buffer size */
116 CTL_DEBUG_BINARY, /* Include binary data in buffer */
117 CTL_DEBUG_CATASTROPHE, /* Set if we have BUG'd or panic'd */
118 CTL_DEBUG_PANIC_ON_BUG, /* Set if we should panic on BUG */
119 CTL_DEBUG_PATH, /* Dump log location */
120 CTL_DEBUG_DUMP, /* Dump debug buffer to file */
121 CTL_DEBUG_FORCE_BUG, /* Hook to force a BUG */
9ab1ac14 122 CTL_DEBUG_STACK_SIZE, /* Max observed stack size */
123
124 CTL_CONSOLE_RATELIMIT, /* Ratelimit console messages */
57d1b188 125 CTL_CONSOLE_MAX_DELAY_CS, /* Max delay at which we skip messages */
126 CTL_CONSOLE_MIN_DELAY_CS, /* Init delay at which we skip messages */
127 CTL_CONSOLE_BACKOFF, /* Delay increase factor */
9ab1ac14 128
57d1b188 129#ifdef DEBUG_KMEM
57d86234 130 CTL_KMEM_KMEMUSED, /* Alloc'd kmem bytes */
57d1b188 131 CTL_KMEM_KMEMMAX, /* Max alloc'd by kmem bytes */
57d86234 132 CTL_KMEM_VMEMUSED, /* Alloc'd vmem bytes */
57d1b188 133 CTL_KMEM_VMEMMAX, /* Max alloc'd by vmem bytes */
5c2bb9b2 134 CTL_KMEM_ALLOC_FAILED, /* Cache allocation failed */
57d1b188 135#endif
9ab1ac14 136
137 CTL_MUTEX_STATS, /* Global mutex statistics */
138 CTL_MUTEX_STATS_PER, /* Per mutex statistics */
139 CTL_MUTEX_SPIN_MAX, /* Maximum mutex spin iterations */
57d1b188 140};
57d86234 141#endif /* HAVE_CTL_UNNUMBERED */
57d1b188 142
143static int
144proc_copyin_string(char *kbuffer, int kbuffer_size,
145 const char *ubuffer, int ubuffer_size)
146{
147 int size;
148
149 if (ubuffer_size > kbuffer_size)
150 return -EOVERFLOW;
151
152 if (copy_from_user((void *)kbuffer, (void *)ubuffer, ubuffer_size))
153 return -EFAULT;
154
155 /* strip trailing whitespace */
156 size = strnlen(kbuffer, ubuffer_size);
157 while (size-- >= 0)
158 if (!isspace(kbuffer[size]))
159 break;
160
161 /* empty string */
162 if (size < 0)
163 return -EINVAL;
164
165 /* no space to terminate */
166 if (size == kbuffer_size)
167 return -EOVERFLOW;
168
169 kbuffer[size + 1] = 0;
170 return 0;
171}
172
173static int
174proc_copyout_string(char *ubuffer, int ubuffer_size,
175 const char *kbuffer, char *append)
176{
177 /* NB if 'append' != NULL, it's a single character to append to the
178 * copied out string - usually "\n", for /proc entries and
179 * (i.e. a terminating zero byte) for sysctl entries
180 */
181 int size = MIN(strlen(kbuffer), ubuffer_size);
182
183 if (copy_to_user(ubuffer, kbuffer, size))
184 return -EFAULT;
185
186 if (append != NULL && size < ubuffer_size) {
187 if (copy_to_user(ubuffer + size, append, 1))
188 return -EFAULT;
189
190 size++;
191 }
192
193 return size;
194}
195
196static int
197proc_dobitmasks(struct ctl_table *table, int write, struct file *filp,
198 void __user *buffer, size_t *lenp, loff_t *ppos)
199{
200 unsigned long *mask = table->data;
201 int is_subsys = (mask == &spl_debug_subsys) ? 1 : 0;
202 int is_printk = (mask == &spl_debug_printk) ? 1 : 0;
203 int size = 512, rc;
204 char *str;
205 ENTRY;
206
207 str = kmem_alloc(size, KM_SLEEP);
208 if (str == NULL)
209 RETURN(-ENOMEM);
210
211 if (write) {
212 rc = proc_copyin_string(str, size, buffer, *lenp);
213 if (rc < 0)
214 RETURN(rc);
215
216 rc = spl_debug_str2mask(mask, str, is_subsys);
217 /* Always print BUG/ASSERT to console, so keep this mask */
218 if (is_printk)
219 *mask |= D_EMERG;
220
221 *ppos += *lenp;
222 } else {
223 rc = spl_debug_mask2str(str, size, *mask, is_subsys);
224 if (*ppos >= rc)
225 rc = 0;
226 else
227 rc = proc_copyout_string(buffer, *lenp,
228 str + *ppos, "\n");
229 if (rc >= 0) {
230 *lenp = rc;
231 *ppos += rc;
232 }
233 }
234
235 kmem_free(str, size);
236 RETURN(rc);
237}
238
239static int
240proc_debug_mb(struct ctl_table *table, int write, struct file *filp,
241 void __user *buffer, size_t *lenp, loff_t *ppos)
242{
243 char str[32];
244 int rc, len;
245 ENTRY;
246
247 if (write) {
248 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
249 if (rc < 0)
250 RETURN(rc);
251
252 rc = spl_debug_set_mb(simple_strtoul(str, NULL, 0));
253 *ppos += *lenp;
254 } else {
255 len = snprintf(str, sizeof(str), "%d", spl_debug_get_mb());
256 if (*ppos >= len)
257 rc = 0;
258 else
259 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
260
261 if (rc >= 0) {
262 *lenp = rc;
263 *ppos += rc;
264 }
265 }
266
267 RETURN(rc);
268}
269
270static int
271proc_dump_kernel(struct ctl_table *table, int write, struct file *filp,
272 void __user *buffer, size_t *lenp, loff_t *ppos)
273{
274 ENTRY;
275
276 if (write) {
7fea96c0 277 spl_debug_dumplog(0);
57d1b188 278 *ppos += *lenp;
279 } else {
280 *lenp = 0;
281 }
282
283 RETURN(0);
284}
285
286static int
287proc_force_bug(struct ctl_table *table, int write, struct file *filp,
288 void __user *buffer, size_t *lenp, loff_t *ppos)
289{
290 ENTRY;
291
292 if (write) {
2fae1b3d 293 CERROR("Crashing due to forced SBUG\n");
294 SBUG();
57d1b188 295 /* Unreachable */
296 } else {
297 *lenp = 0;
298 }
299
300 RETURN(0);
301}
302
303static int
304proc_console_max_delay_cs(struct ctl_table *table, int write, struct file *filp,
305 void __user *buffer, size_t *lenp, loff_t *ppos)
306{
307 int rc, max_delay_cs;
308 struct ctl_table dummy = *table;
309 long d;
310 ENTRY;
311
312 dummy.data = &max_delay_cs;
313 dummy.proc_handler = &proc_dointvec;
314
315 if (write) {
316 max_delay_cs = 0;
317 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
318 if (rc < 0)
319 RETURN(rc);
320
321 if (max_delay_cs <= 0)
322 RETURN(-EINVAL);
323
324 d = (max_delay_cs * HZ) / 100;
325 if (d == 0 || d < spl_console_min_delay)
326 RETURN(-EINVAL);
327
328 spl_console_max_delay = d;
329 } else {
330 max_delay_cs = (spl_console_max_delay * 100) / HZ;
331 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
332 }
333
334 RETURN(rc);
335}
336
337static int
338proc_console_min_delay_cs(struct ctl_table *table, int write, struct file *filp,
339 void __user *buffer, size_t *lenp, loff_t *ppos)
340{
341 int rc, min_delay_cs;
342 struct ctl_table dummy = *table;
343 long d;
344 ENTRY;
345
346 dummy.data = &min_delay_cs;
347 dummy.proc_handler = &proc_dointvec;
348
349 if (write) {
350 min_delay_cs = 0;
351 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
352 if (rc < 0)
353 RETURN(rc);
354
355 if (min_delay_cs <= 0)
356 RETURN(-EINVAL);
357
358 d = (min_delay_cs * HZ) / 100;
359 if (d == 0 || d > spl_console_max_delay)
360 RETURN(-EINVAL);
361
362 spl_console_min_delay = d;
363 } else {
364 min_delay_cs = (spl_console_min_delay * 100) / HZ;
365 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
366 }
367
368 RETURN(rc);
369}
370
371static int
372proc_console_backoff(struct ctl_table *table, int write, struct file *filp,
373 void __user *buffer, size_t *lenp, loff_t *ppos)
374{
375 int rc, backoff;
376 struct ctl_table dummy = *table;
377 ENTRY;
378
379 dummy.data = &backoff;
380 dummy.proc_handler = &proc_dointvec;
381
382 if (write) {
383 backoff = 0;
384 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
385 if (rc < 0)
386 RETURN(rc);
387
388 if (backoff <= 0)
389 RETURN(-EINVAL);
390
391 spl_console_backoff = backoff;
392 } else {
393 backoff = spl_console_backoff;
394 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
395 }
396
397 RETURN(rc);
398}
399
c6dc93d6 400#ifdef DEBUG_KMEM
57d1b188 401static int
402proc_doatomic64(struct ctl_table *table, int write, struct file *filp,
403 void __user *buffer, size_t *lenp, loff_t *ppos)
404{
405 int rc = 0;
406 unsigned long min = 0, max = ~0, val;
407 struct ctl_table dummy = *table;
408 ENTRY;
409
410 dummy.data = &val;
411 dummy.proc_handler = &proc_dointvec;
412 dummy.extra1 = &min;
413 dummy.extra2 = &max;
414
415 if (write) {
416 *ppos += *lenp;
417 } else {
418 val = atomic_read((atomic64_t *)table->data);
419 rc = proc_doulongvec_minmax(&dummy, write, filp,
420 buffer, lenp, ppos);
421 }
422
423 RETURN(rc);
424}
c6dc93d6 425#endif /* DEBUG_KMEM */
57d1b188 426
427static int
428proc_dohostid(struct ctl_table *table, int write, struct file *filp,
429 void __user *buffer, size_t *lenp, loff_t *ppos)
430{
431 int len, rc = 0;
937879f1 432 int32_t val;
57d1b188 433 char *end, str[32];
434 ENTRY;
435
436 if (write) {
437 /* We can't use proc_doulongvec_minmax() in the write
438 * case hear because hostid while a hex value has no
439 * leading 0x which confuses the helper function. */
440 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
441 if (rc < 0)
442 RETURN(rc);
443
937879f1 444 val = simple_strtol(str, &end, 16);
57d1b188 445 if (str == end)
446 RETURN(-EINVAL);
447
937879f1 448 spl_hostid = (long)val;
449 sprintf(hw_serial, "%u", (val >= 0) ? val : -val);
57d1b188 450 *ppos += *lenp;
451 } else {
452 len = snprintf(str, sizeof(str), "%lx", spl_hostid);
453 if (*ppos >= len)
454 rc = 0;
455 else
456 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
457
458 if (rc >= 0) {
459 *lenp = rc;
460 *ppos += rc;
461 }
462 }
463
464 RETURN(rc);
465}
466
9ab1ac14 467#ifdef DEBUG_MUTEX
468static void
469mutex_seq_show_headers(struct seq_file *f)
470{
471 seq_printf(f, "%-36s %-4s %-16s\t"
472 "e_tot\te_nh\te_sp\te_sl\tte_tot\tte_nh\n",
473 "name", "type", "owner");
474}
475
476static int
477mutex_seq_show(struct seq_file *f, void *p)
478{
479 kmutex_t *mp = p;
480 char t = 'X';
481 int i;
482
483 ASSERT(mp->km_magic == KM_MAGIC);
484
485 switch (mp->km_type) {
486 case MUTEX_DEFAULT: t = 'D'; break;
487 case MUTEX_SPIN: t = 'S'; break;
488 case MUTEX_ADAPTIVE: t = 'A'; break;
489 default:
490 SBUG();
491 }
492 seq_printf(f, "%-36s %c ", mp->km_name, t);
493 if (mp->km_owner)
494 seq_printf(f, "%p\t", mp->km_owner);
495 else
496 seq_printf(f, "%-16s\t", "<not held>");
497
498 for (i = 0; i < MUTEX_STATS_SIZE; i++)
499 seq_printf(f, "%d%c", mp->km_stats[i],
500 (i + 1 == MUTEX_STATS_SIZE) ? '\n' : '\t');
501
502 return 0;
503}
504
505static void *
506mutex_seq_start(struct seq_file *f, loff_t *pos)
507{
508 struct list_head *p;
509 loff_t n = *pos;
510 ENTRY;
511
404992e3 512 spin_lock(&mutex_stats_lock);
9ab1ac14 513 if (!n)
514 mutex_seq_show_headers(f);
515
516 p = mutex_stats_list.next;
517 while (n--) {
518 p = p->next;
519 if (p == &mutex_stats_list)
520 RETURN(NULL);
521 }
522
523 RETURN(list_entry(p, kmutex_t, km_list));
524}
525
526static void *
527mutex_seq_next(struct seq_file *f, void *p, loff_t *pos)
528{
529 kmutex_t *mp = p;
530 ENTRY;
531
532 ++*pos;
533 RETURN((mp->km_list.next == &mutex_stats_list) ?
534 NULL : list_entry(mp->km_list.next, kmutex_t, km_list));
535}
536
537static void
538mutex_seq_stop(struct seq_file *f, void *v)
539{
404992e3 540 spin_unlock(&mutex_stats_lock);
9ab1ac14 541}
542
543static struct seq_operations mutex_seq_ops = {
544 .show = mutex_seq_show,
545 .start = mutex_seq_start,
546 .next = mutex_seq_next,
547 .stop = mutex_seq_stop,
548};
549
550static int
551proc_mutex_open(struct inode *inode, struct file *filp)
552{
553 return seq_open(filp, &mutex_seq_ops);
554}
555
556static struct file_operations proc_mutex_operations = {
557 .open = proc_mutex_open,
558 .read = seq_read,
559 .llseek = seq_lseek,
560 .release = seq_release,
561};
562#endif /* DEBUG_MUTEX */
563
564static struct ctl_table spl_debug_table[] = {
57d1b188 565 {
566 .ctl_name = CTL_DEBUG_SUBSYS,
9ab1ac14 567 .procname = "subsystem",
57d1b188 568 .data = &spl_debug_subsys,
569 .maxlen = sizeof(unsigned long),
570 .mode = 0644,
571 .proc_handler = &proc_dobitmasks
572 },
573 {
574 .ctl_name = CTL_DEBUG_MASK,
9ab1ac14 575 .procname = "mask",
57d1b188 576 .data = &spl_debug_mask,
577 .maxlen = sizeof(unsigned long),
578 .mode = 0644,
579 .proc_handler = &proc_dobitmasks
580 },
581 {
582 .ctl_name = CTL_DEBUG_PRINTK,
9ab1ac14 583 .procname = "printk",
57d1b188 584 .data = &spl_debug_printk,
585 .maxlen = sizeof(unsigned long),
586 .mode = 0644,
587 .proc_handler = &proc_dobitmasks
588 },
589 {
590 .ctl_name = CTL_DEBUG_MB,
9ab1ac14 591 .procname = "mb",
57d1b188 592 .mode = 0644,
593 .proc_handler = &proc_debug_mb,
594 },
595 {
596 .ctl_name = CTL_DEBUG_BINARY,
9ab1ac14 597 .procname = "binary",
57d1b188 598 .data = &spl_debug_binary,
599 .maxlen = sizeof(int),
600 .mode = 0644,
601 .proc_handler = &proc_dointvec,
602 },
603 {
604 .ctl_name = CTL_DEBUG_CATASTROPHE,
605 .procname = "catastrophe",
606 .data = &spl_debug_catastrophe,
607 .maxlen = sizeof(int),
608 .mode = 0444,
609 .proc_handler = &proc_dointvec,
610 },
611 {
612 .ctl_name = CTL_DEBUG_PANIC_ON_BUG,
613 .procname = "panic_on_bug",
614 .data = &spl_debug_panic_on_bug,
615 .maxlen = sizeof(int),
616 .mode = 0644,
617 .proc_handler = &proc_dointvec
618 },
619 {
620 .ctl_name = CTL_DEBUG_PATH,
9ab1ac14 621 .procname = "path",
57d1b188 622 .data = spl_debug_file_path,
623 .maxlen = sizeof(spl_debug_file_path),
624 .mode = 0644,
625 .proc_handler = &proc_dostring,
626 },
627 {
628 .ctl_name = CTL_DEBUG_DUMP,
9ab1ac14 629 .procname = "dump",
57d1b188 630 .mode = 0200,
631 .proc_handler = &proc_dump_kernel,
632 },
633 { .ctl_name = CTL_DEBUG_FORCE_BUG,
634 .procname = "force_bug",
635 .mode = 0200,
636 .proc_handler = &proc_force_bug,
637 },
638 {
639 .ctl_name = CTL_CONSOLE_RATELIMIT,
640 .procname = "console_ratelimit",
641 .data = &spl_console_ratelimit,
642 .maxlen = sizeof(int),
643 .mode = 0644,
644 .proc_handler = &proc_dointvec,
645 },
646 {
647 .ctl_name = CTL_CONSOLE_MAX_DELAY_CS,
648 .procname = "console_max_delay_centisecs",
649 .maxlen = sizeof(int),
650 .mode = 0644,
651 .proc_handler = &proc_console_max_delay_cs,
652 },
653 {
654 .ctl_name = CTL_CONSOLE_MIN_DELAY_CS,
655 .procname = "console_min_delay_centisecs",
656 .maxlen = sizeof(int),
657 .mode = 0644,
658 .proc_handler = &proc_console_min_delay_cs,
659 },
660 {
661 .ctl_name = CTL_CONSOLE_BACKOFF,
662 .procname = "console_backoff",
663 .maxlen = sizeof(int),
664 .mode = 0644,
665 .proc_handler = &proc_console_backoff,
666 },
667 {
9ab1ac14 668 .ctl_name = CTL_DEBUG_STACK_SIZE,
57d1b188 669 .procname = "stack_max",
670 .data = &spl_debug_stack,
671 .maxlen = sizeof(int),
672 .mode = 0444,
673 .proc_handler = &proc_dointvec,
674 },
9ab1ac14 675 {0},
676};
677
678#ifdef DEBUG_MUTEX
679static struct ctl_table spl_mutex_table[] = {
680 {
681 .ctl_name = CTL_MUTEX_STATS,
682 .procname = "stats",
683 .data = &mutex_stats,
684 .maxlen = sizeof(int) * MUTEX_STATS_SIZE,
685 .mode = 0444,
686 .proc_handler = &proc_dointvec,
687 },
688 {
689 .ctl_name = CTL_MUTEX_SPIN_MAX,
690 .procname = "spin_max",
691 .data = &mutex_spin_max,
692 .maxlen = sizeof(int),
693 .mode = 0644,
694 .proc_handler = &proc_dointvec,
695 },
696 {0},
697};
698#endif /* DEBUG_MUTEX */
699
57d1b188 700#ifdef DEBUG_KMEM
9ab1ac14 701static struct ctl_table spl_kmem_table[] = {
57d1b188 702 {
703 .ctl_name = CTL_KMEM_KMEMUSED,
704 .procname = "kmem_used",
705 .data = &kmem_alloc_used,
706 .maxlen = sizeof(atomic64_t),
707 .mode = 0444,
708 .proc_handler = &proc_doatomic64,
709 },
710 {
711 .ctl_name = CTL_KMEM_KMEMMAX,
712 .procname = "kmem_max",
713 .data = &kmem_alloc_max,
714 .maxlen = sizeof(unsigned long),
715 .extra1 = &table_min,
716 .extra2 = &table_max,
717 .mode = 0444,
718 .proc_handler = &proc_doulongvec_minmax,
719 },
720 {
721 .ctl_name = CTL_KMEM_VMEMUSED,
722 .procname = "vmem_used",
723 .data = &vmem_alloc_used,
724 .maxlen = sizeof(atomic64_t),
725 .mode = 0444,
726 .proc_handler = &proc_doatomic64,
727 },
728 {
729 .ctl_name = CTL_KMEM_VMEMMAX,
730 .procname = "vmem_max",
731 .data = &vmem_alloc_max,
732 .maxlen = sizeof(unsigned long),
733 .extra1 = &table_min,
734 .extra2 = &table_max,
735 .mode = 0444,
736 .proc_handler = &proc_doulongvec_minmax,
737 },
5c2bb9b2 738 {
739 .ctl_name = CTL_KMEM_ALLOC_FAILED,
740 .procname = "kmem_alloc_failed",
741 .data = &kmem_cache_alloc_failed,
742 .maxlen = sizeof(atomic64_t),
743 .mode = 0444,
744 .proc_handler = &proc_doatomic64,
745 },
9ab1ac14 746 {0},
747};
04a479f7 748#endif /* DEBUG_KMEM */
749
750#ifdef DEBUG_KSTAT
751static struct ctl_table spl_kstat_table[] = {
752 {0},
753};
754#endif /* DEBUG_KSTAT */
9ab1ac14 755
756static struct ctl_table spl_table[] = {
757 /* NB No .strategy entries have been provided since
758 * sysctl(8) prefers to go via /proc for portability.
759 */
760 {
761 .ctl_name = CTL_VERSION,
762 .procname = "version",
763 .data = spl_version,
764 .maxlen = sizeof(spl_version),
765 .mode = 0444,
766 .proc_handler = &proc_dostring,
767 },
57d1b188 768 {
769 .ctl_name = CTL_HOSTID,
770 .procname = "hostid",
771 .data = &spl_hostid,
772 .maxlen = sizeof(unsigned long),
773 .mode = 0644,
774 .proc_handler = &proc_dohostid,
775 },
776 {
777 .ctl_name = CTL_HW_SERIAL,
778 .procname = "hw_serial",
937879f1 779 .data = hw_serial,
780 .maxlen = sizeof(hw_serial),
57d1b188 781 .mode = 0444,
782 .proc_handler = &proc_dostring,
783 },
9ab1ac14 784 {
785 .ctl_name = CTL_SPL_DEBUG,
786 .procname = "debug",
787 .mode = 0555,
788 .child = spl_debug_table,
789 },
790#ifdef DEBUG_MUTEX
791 {
792 .ctl_name = CTL_SPL_MUTEX,
793 .procname = "mutex",
794 .mode = 0555,
795 .child = spl_mutex_table,
796 },
797#endif
798#ifdef DEBUG_KMEM
799 {
800 .ctl_name = CTL_SPL_KMEM,
801 .procname = "kmem",
802 .mode = 0555,
803 .child = spl_kmem_table,
804 },
04a479f7 805#endif
806#ifdef DEBUG_KSTAT
807 {
808 .ctl_name = CTL_SPL_KSTAT,
809 .procname = "kstat",
810 .mode = 0555,
811 .child = spl_kstat_table,
812 },
9ab1ac14 813#endif
57d1b188 814 { 0 },
815};
816
9ab1ac14 817static struct ctl_table spl_dir[] = {
57d1b188 818 {
819 .ctl_name = CTL_SPL,
820 .procname = "spl",
821 .mode = 0555,
822 .child = spl_table,
823 },
57d86234 824 { 0 }
825};
826
827static struct ctl_table spl_root[] = {
828 {
829 .ctl_name = CTL_KERN,
830 .procname = "kernel",
831 .mode = 0555,
832 .child = spl_dir,
833 },
834 { 0 }
57d1b188 835};
836
404992e3 837static int
838proc_dir_entry_match(int len, const char *name, struct proc_dir_entry *de)
839{
840 if (de->namelen != len)
841 return 0;
842
843 return !memcmp(name, de->name, len);
844}
845
04a479f7 846struct proc_dir_entry *
404992e3 847proc_dir_entry_find(struct proc_dir_entry *root, const char *str)
848{
849 struct proc_dir_entry *de;
850
851 for (de = root->subdir; de; de = de->next)
852 if (proc_dir_entry_match(strlen(str), str, de))
853 return de;
854
855 return NULL;
856}
857
04a479f7 858int
859proc_dir_entries(struct proc_dir_entry *root)
860{
861 struct proc_dir_entry *de;
862 int i = 0;
863
864 for (de = root->subdir; de; de = de->next)
865 i++;
866
867 return i;
868}
869
57d1b188 870int
871proc_init(void)
872{
404992e3 873 int rc = 0;
57d1b188 874 ENTRY;
875
876#ifdef CONFIG_SYSCTL
57d86234 877 spl_header = spl_register_sysctl_table(spl_root, 0);
57d1b188 878 if (spl_header == NULL)
879 RETURN(-EUNATCH);
c30df9c8 880#endif /* CONFIG_SYSCTL */
9ab1ac14 881
57d86234 882#if defined(DEBUG_MUTEX) || defined(DEBUG_KMEM) || defined(DEBUG_KSTAT)
c30df9c8 883 proc_spl = proc_mkdir("spl", NULL);
884 if (proc_spl == NULL)
404992e3 885 GOTO(out, rc = -EUNATCH);
886
9ab1ac14 887#ifdef DEBUG_MUTEX
c30df9c8 888 proc_spl_mutex = proc_mkdir("mutex", proc_spl);
889 if (proc_spl_mutex == NULL)
404992e3 890 GOTO(out, rc = -EUNATCH);
891
c30df9c8 892 proc_spl_mutex_stats = create_proc_entry("stats_per", 0444,
893 proc_spl_mutex);
894 if (proc_spl_mutex_stats == NULL)
404992e3 895 GOTO(out, rc = -EUNATCH);
896
c30df9c8 897 proc_spl_mutex_stats->proc_fops = &proc_mutex_operations;
9ab1ac14 898#endif /* DEBUG_MUTEX */
04a479f7 899
900#ifdef DEBUG_KMEM
c30df9c8 901 proc_spl_kmem = proc_mkdir("kmem", proc_spl);
902 if (proc_spl_kmem == NULL)
903 GOTO(out, rc = -EUNATCH);
04a479f7 904#endif /* DEBUG_KMEM */
905
906#ifdef DEBUG_KSTAT
c30df9c8 907 proc_spl_kstat = proc_mkdir("kstat", proc_spl);
908 if (proc_spl_kstat == NULL)
909 GOTO(out, rc = -EUNATCH);
04a479f7 910#endif /* DEBUG_KSTAT */
911
404992e3 912out:
c30df9c8 913 if (rc) {
914 remove_proc_entry("kstat", proc_spl);
915 remove_proc_entry("kmem", proc_spl);
916 remove_proc_entry("stats_per", proc_spl_mutex);
917 remove_proc_entry("mutex", proc_spl);
918#ifdef CONFIG_SYSCTL
919 spl_unregister_sysctl_table(spl_header);
404992e3 920#endif /* CONFIG_SYSCTL */
c30df9c8 921 }
922#endif /* DEBUG_MUTEX || DEBUG_KMEM || DEBUG_KSTAT */
923
404992e3 924 RETURN(rc);
57d1b188 925}
926
927void
928proc_fini(void)
929{
930 ENTRY;
931
c30df9c8 932#if defined(DEBUG_MUTEX) || defined(DEBUG_KMEM) || defined(DEBUG_KSTAT)
933 remove_proc_entry("kstat", proc_spl);
934 remove_proc_entry("kmem", proc_spl);
935 remove_proc_entry("stats_per", proc_spl_mutex);
936 remove_proc_entry("mutex", proc_spl);
937#endif /* DEBUG_MUTEX || DEBUG_KMEM || DEBUG_KSTAT */
938
57d1b188 939#ifdef CONFIG_SYSCTL
940 ASSERT(spl_header != NULL);
57d86234 941 spl_unregister_sysctl_table(spl_header);
c30df9c8 942#endif /* CONFIG_SYSCTL */
943
57d1b188 944 EXIT;
945}