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