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