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