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