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