]> git.proxmox.com Git - mirror_spl.git/blame - module/spl/spl-proc.c
Atomic64 compatibility for 32-bit systems without kernel support.
[mirror_spl.git] / module / spl / spl-proc.c
CommitLineData
715f6251 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 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 36static unsigned long table_min = 0;
37static unsigned long table_max = ~0;
404992e3 38#endif
39
40#ifdef CONFIG_SYSCTL
41static struct ctl_table_header *spl_header = NULL;
c30df9c8 42#endif /* CONFIG_SYSCTL */
43
c30df9c8 44static struct proc_dir_entry *proc_spl = NULL;
04a479f7 45#ifdef DEBUG_KMEM
c30df9c8 46static struct proc_dir_entry *proc_spl_kmem = NULL;
ff449ac4 47static struct proc_dir_entry *proc_spl_kmem_slab = NULL;
c30df9c8 48#endif /* DEBUG_KMEM */
c30df9c8 49struct proc_dir_entry *proc_spl_kstat = NULL;
57d1b188 50
57d86234 51#ifdef HAVE_CTL_UNNUMBERED
52
53#define CTL_SPL CTL_UNNUMBERED
54#define CTL_SPL_DEBUG CTL_UNNUMBERED
36b313da 55#define CTL_SPL_VM CTL_UNNUMBERED
57d86234 56#define CTL_SPL_MUTEX CTL_UNNUMBERED
57#define CTL_SPL_KMEM CTL_UNNUMBERED
58#define CTL_SPL_KSTAT CTL_UNNUMBERED
59
60#define CTL_VERSION CTL_UNNUMBERED /* Version */
61#define CTL_HOSTID CTL_UNNUMBERED /* Host id by /usr/bin/hostid */
62#define CTL_HW_SERIAL CTL_UNNUMBERED /* HW serial number by hostid */
d1ff2312 63#define CTL_KALLSYMS CTL_UNNUMBERED /* kallsyms_lookup_name addr */
57d86234 64
65#define CTL_DEBUG_SUBSYS CTL_UNNUMBERED /* Debug subsystem */
66#define CTL_DEBUG_MASK CTL_UNNUMBERED /* Debug mask */
67#define CTL_DEBUG_PRINTK CTL_UNNUMBERED /* All messages to console */
68#define CTL_DEBUG_MB CTL_UNNUMBERED /* Debug buffer size */
69#define CTL_DEBUG_BINARY CTL_UNNUMBERED /* Binary data in buffer */
70#define CTL_DEBUG_CATASTROPHE CTL_UNNUMBERED /* Set if BUG'd or panic'd */
71#define CTL_DEBUG_PANIC_ON_BUG CTL_UNNUMBERED /* Should panic on BUG */
72#define CTL_DEBUG_PATH CTL_UNNUMBERED /* Dump log location */
73#define CTL_DEBUG_DUMP CTL_UNNUMBERED /* Dump debug buffer to file */
74#define CTL_DEBUG_FORCE_BUG CTL_UNNUMBERED /* Hook to force a BUG */
75#define CTL_DEBUG_STACK_SIZE CTL_UNNUMBERED /* Max observed stack size */
76
77#define CTL_CONSOLE_RATELIMIT CTL_UNNUMBERED /* Ratelimit console messages */
78#define CTL_CONSOLE_MAX_DELAY_CS CTL_UNNUMBERED /* Max delay skip messages */
79#define CTL_CONSOLE_MIN_DELAY_CS CTL_UNNUMBERED /* Init delay skip messages */
80#define CTL_CONSOLE_BACKOFF CTL_UNNUMBERED /* Delay increase factor */
81
36b313da
BB
82#define CTL_VM_MINFREE CTL_UNNUMBERED /* Minimum free memory */
83#define CTL_VM_DESFREE CTL_UNNUMBERED /* Desired free memory */
84#define CTL_VM_LOTSFREE CTL_UNNUMBERED /* Lots of free memory */
85#define CTL_VM_NEEDFREE CTL_UNNUMBERED /* Need free memory */
86#define CTL_VM_SWAPFS_MINFREE CTL_UNNUMBERED /* Minimum swapfs memory */
36b313da 87#define CTL_VM_SWAPFS_RESERVE CTL_UNNUMBERED /* Reserved swapfs memory */
4ab13d3b
BB
88#define CTL_VM_AVAILRMEM CTL_UNNUMBERED /* Easily available memory */
89#define CTL_VM_FREEMEM CTL_UNNUMBERED /* Free memory */
90#define CTL_VM_PHYSMEM CTL_UNNUMBERED /* Total physical memory */
36b313da 91
57d86234 92#ifdef DEBUG_KMEM
93#define CTL_KMEM_KMEMUSED CTL_UNNUMBERED /* Alloc'd kmem bytes */
94#define CTL_KMEM_KMEMMAX CTL_UNNUMBERED /* Max alloc'd by kmem bytes */
95#define CTL_KMEM_VMEMUSED CTL_UNNUMBERED /* Alloc'd vmem bytes */
96#define CTL_KMEM_VMEMMAX CTL_UNNUMBERED /* Max alloc'd by vmem bytes */
97#define CTL_KMEM_ALLOC_FAILED CTL_UNNUMBERED /* Cache allocations failed */
98#endif
99
57d86234 100#else /* HAVE_CTL_UNNUMBERED */
101
36b313da
BB
102enum {
103 CTL_SPL = 0x87,
104 CTL_SPL_DEBUG = 0x88,
105 CTL_SPL_VM = 0x89,
106 CTL_SPL_MUTEX = 0x90,
107 CTL_SPL_KMEM = 0x91,
108 CTL_SPL_KSTAT = 0x92,
109};
9ab1ac14 110
57d1b188 111enum {
36b313da
BB
112 CTL_VERSION = 1, /* Version */
113 CTL_HOSTID, /* Host id reported by /usr/bin/hostid */
114 CTL_HW_SERIAL, /* Hardware serial number from hostid */
d1ff2312 115 CTL_KALLSYMS, /* Address of kallsyms_lookup_name */
36b313da
BB
116
117 CTL_DEBUG_SUBSYS, /* Debug subsystem */
118 CTL_DEBUG_MASK, /* Debug mask */
119 CTL_DEBUG_PRINTK, /* Force all messages to console */
120 CTL_DEBUG_MB, /* Debug buffer size */
121 CTL_DEBUG_BINARY, /* Include binary data in buffer */
122 CTL_DEBUG_CATASTROPHE, /* Set if we have BUG'd or panic'd */
123 CTL_DEBUG_PANIC_ON_BUG, /* Set if we should panic on BUG */
124 CTL_DEBUG_PATH, /* Dump log location */
125 CTL_DEBUG_DUMP, /* Dump debug buffer to file */
126 CTL_DEBUG_FORCE_BUG, /* Hook to force a BUG */
127 CTL_DEBUG_STACK_SIZE, /* Max observed stack size */
128
129 CTL_CONSOLE_RATELIMIT, /* Ratelimit console messages */
130 CTL_CONSOLE_MAX_DELAY_CS, /* Max delay which we skip messages */
131 CTL_CONSOLE_MIN_DELAY_CS, /* Init delay which we skip messages */
132 CTL_CONSOLE_BACKOFF, /* Delay increase factor */
133
134 CTL_VM_MINFREE, /* Minimum free memory threshold */
135 CTL_VM_DESFREE, /* Desired free memory threshold */
136 CTL_VM_LOTSFREE, /* Lots of free memory threshold */
137 CTL_VM_NEEDFREE, /* Need free memory deficit */
138 CTL_VM_SWAPFS_MINFREE, /* Minimum swapfs memory */
36b313da 139 CTL_VM_SWAPFS_RESERVE, /* Reserved swapfs memory */
4ab13d3b
BB
140 CTL_VM_AVAILRMEM, /* Easily available memory */
141 CTL_VM_FREEMEM, /* Free memory */
142 CTL_VM_PHYSMEM, /* Total physical memory */
9ab1ac14 143
57d1b188 144#ifdef DEBUG_KMEM
36b313da
BB
145 CTL_KMEM_KMEMUSED, /* Alloc'd kmem bytes */
146 CTL_KMEM_KMEMMAX, /* Max alloc'd by kmem bytes */
147 CTL_KMEM_VMEMUSED, /* Alloc'd vmem bytes */
148 CTL_KMEM_VMEMMAX, /* Max alloc'd by vmem bytes */
57d1b188 149#endif
57d1b188 150};
57d86234 151#endif /* HAVE_CTL_UNNUMBERED */
57d1b188 152
153static int
154proc_copyin_string(char *kbuffer, int kbuffer_size,
155 const char *ubuffer, int ubuffer_size)
156{
157 int size;
158
159 if (ubuffer_size > kbuffer_size)
160 return -EOVERFLOW;
161
162 if (copy_from_user((void *)kbuffer, (void *)ubuffer, ubuffer_size))
163 return -EFAULT;
164
165 /* strip trailing whitespace */
166 size = strnlen(kbuffer, ubuffer_size);
167 while (size-- >= 0)
168 if (!isspace(kbuffer[size]))
169 break;
170
171 /* empty string */
172 if (size < 0)
173 return -EINVAL;
174
175 /* no space to terminate */
176 if (size == kbuffer_size)
177 return -EOVERFLOW;
178
179 kbuffer[size + 1] = 0;
180 return 0;
181}
182
183static int
184proc_copyout_string(char *ubuffer, int ubuffer_size,
185 const char *kbuffer, char *append)
186{
187 /* NB if 'append' != NULL, it's a single character to append to the
188 * copied out string - usually "\n", for /proc entries and
189 * (i.e. a terminating zero byte) for sysctl entries
190 */
191 int size = MIN(strlen(kbuffer), ubuffer_size);
192
193 if (copy_to_user(ubuffer, kbuffer, size))
194 return -EFAULT;
195
196 if (append != NULL && size < ubuffer_size) {
197 if (copy_to_user(ubuffer + size, append, 1))
198 return -EFAULT;
199
200 size++;
201 }
202
203 return size;
204}
205
206static int
207proc_dobitmasks(struct ctl_table *table, int write, struct file *filp,
208 void __user *buffer, size_t *lenp, loff_t *ppos)
209{
210 unsigned long *mask = table->data;
211 int is_subsys = (mask == &spl_debug_subsys) ? 1 : 0;
212 int is_printk = (mask == &spl_debug_printk) ? 1 : 0;
213 int size = 512, rc;
214 char *str;
215 ENTRY;
216
217 str = kmem_alloc(size, KM_SLEEP);
218 if (str == NULL)
219 RETURN(-ENOMEM);
220
221 if (write) {
222 rc = proc_copyin_string(str, size, buffer, *lenp);
223 if (rc < 0)
224 RETURN(rc);
225
226 rc = spl_debug_str2mask(mask, str, is_subsys);
227 /* Always print BUG/ASSERT to console, so keep this mask */
228 if (is_printk)
229 *mask |= D_EMERG;
230
231 *ppos += *lenp;
232 } else {
233 rc = spl_debug_mask2str(str, size, *mask, is_subsys);
234 if (*ppos >= rc)
235 rc = 0;
236 else
237 rc = proc_copyout_string(buffer, *lenp,
238 str + *ppos, "\n");
239 if (rc >= 0) {
240 *lenp = rc;
241 *ppos += rc;
242 }
243 }
244
245 kmem_free(str, size);
246 RETURN(rc);
247}
248
249static int
250proc_debug_mb(struct ctl_table *table, int write, struct file *filp,
251 void __user *buffer, size_t *lenp, loff_t *ppos)
252{
253 char str[32];
254 int rc, len;
255 ENTRY;
256
257 if (write) {
258 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
259 if (rc < 0)
260 RETURN(rc);
261
262 rc = spl_debug_set_mb(simple_strtoul(str, NULL, 0));
263 *ppos += *lenp;
264 } else {
265 len = snprintf(str, sizeof(str), "%d", spl_debug_get_mb());
266 if (*ppos >= len)
267 rc = 0;
268 else
269 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
270
271 if (rc >= 0) {
272 *lenp = rc;
273 *ppos += rc;
274 }
275 }
276
277 RETURN(rc);
278}
279
280static int
281proc_dump_kernel(struct ctl_table *table, int write, struct file *filp,
282 void __user *buffer, size_t *lenp, loff_t *ppos)
283{
284 ENTRY;
285
286 if (write) {
7fea96c0 287 spl_debug_dumplog(0);
57d1b188 288 *ppos += *lenp;
289 } else {
290 *lenp = 0;
291 }
292
293 RETURN(0);
294}
295
296static int
297proc_force_bug(struct ctl_table *table, int write, struct file *filp,
298 void __user *buffer, size_t *lenp, loff_t *ppos)
299{
300 ENTRY;
301
302 if (write) {
2fae1b3d 303 CERROR("Crashing due to forced SBUG\n");
304 SBUG();
57d1b188 305 /* Unreachable */
306 } else {
307 *lenp = 0;
308 }
309
310 RETURN(0);
311}
312
313static int
314proc_console_max_delay_cs(struct ctl_table *table, int write, struct file *filp,
315 void __user *buffer, size_t *lenp, loff_t *ppos)
316{
317 int rc, max_delay_cs;
318 struct ctl_table dummy = *table;
319 long d;
320 ENTRY;
321
322 dummy.data = &max_delay_cs;
323 dummy.proc_handler = &proc_dointvec;
324
325 if (write) {
326 max_delay_cs = 0;
327 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
328 if (rc < 0)
329 RETURN(rc);
330
331 if (max_delay_cs <= 0)
332 RETURN(-EINVAL);
333
334 d = (max_delay_cs * HZ) / 100;
335 if (d == 0 || d < spl_console_min_delay)
336 RETURN(-EINVAL);
337
338 spl_console_max_delay = d;
339 } else {
340 max_delay_cs = (spl_console_max_delay * 100) / HZ;
341 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
342 }
343
344 RETURN(rc);
345}
346
347static int
348proc_console_min_delay_cs(struct ctl_table *table, int write, struct file *filp,
349 void __user *buffer, size_t *lenp, loff_t *ppos)
350{
351 int rc, min_delay_cs;
352 struct ctl_table dummy = *table;
353 long d;
354 ENTRY;
355
356 dummy.data = &min_delay_cs;
357 dummy.proc_handler = &proc_dointvec;
358
359 if (write) {
360 min_delay_cs = 0;
361 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
362 if (rc < 0)
363 RETURN(rc);
364
365 if (min_delay_cs <= 0)
366 RETURN(-EINVAL);
367
368 d = (min_delay_cs * HZ) / 100;
369 if (d == 0 || d > spl_console_max_delay)
370 RETURN(-EINVAL);
371
372 spl_console_min_delay = d;
373 } else {
374 min_delay_cs = (spl_console_min_delay * 100) / HZ;
375 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
376 }
377
378 RETURN(rc);
379}
380
381static int
382proc_console_backoff(struct ctl_table *table, int write, struct file *filp,
383 void __user *buffer, size_t *lenp, loff_t *ppos)
384{
385 int rc, backoff;
386 struct ctl_table dummy = *table;
387 ENTRY;
388
389 dummy.data = &backoff;
390 dummy.proc_handler = &proc_dointvec;
391
392 if (write) {
393 backoff = 0;
394 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
395 if (rc < 0)
396 RETURN(rc);
397
398 if (backoff <= 0)
399 RETURN(-EINVAL);
400
401 spl_console_backoff = backoff;
402 } else {
403 backoff = spl_console_backoff;
404 rc = proc_dointvec(&dummy, write, filp, buffer, lenp, ppos);
405 }
406
407 RETURN(rc);
408}
409
c6dc93d6 410#ifdef DEBUG_KMEM
57d1b188 411static int
d04c8a56
BB
412proc_domemused(struct ctl_table *table, int write, struct file *filp,
413 void __user *buffer, size_t *lenp, loff_t *ppos)
57d1b188 414{
415 int rc = 0;
416 unsigned long min = 0, max = ~0, val;
417 struct ctl_table dummy = *table;
418 ENTRY;
419
420 dummy.data = &val;
421 dummy.proc_handler = &proc_dointvec;
422 dummy.extra1 = &min;
423 dummy.extra2 = &max;
424
425 if (write) {
426 *ppos += *lenp;
427 } else {
d04c8a56 428# ifdef HAVE_ATOMIC64_T
550f1705 429 val = atomic64_read((atomic64_t *)table->data);
d04c8a56
BB
430# else
431 val = atomic_read((atomic_t *)table->data);
432# endif /* HAVE_ATOMIC64_T */
57d1b188 433 rc = proc_doulongvec_minmax(&dummy, write, filp,
434 buffer, lenp, ppos);
435 }
436
437 RETURN(rc);
438}
c6dc93d6 439#endif /* DEBUG_KMEM */
57d1b188 440
441static int
442proc_dohostid(struct ctl_table *table, int write, struct file *filp,
443 void __user *buffer, size_t *lenp, loff_t *ppos)
444{
445 int len, rc = 0;
937879f1 446 int32_t val;
57d1b188 447 char *end, str[32];
a0b5ae8a 448 ENTRY;
57d1b188 449
450 if (write) {
a0b5ae8a
RC
451 /* We can't use proc_doulongvec_minmax() in the write
452 * case hear because hostid while a hex value has no
453 * leading 0x which confuses the helper function. */
57d1b188 454 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
455 if (rc < 0)
456 RETURN(rc);
457
937879f1 458 val = simple_strtol(str, &end, 16);
a0b5ae8a
RC
459 if (str == end)
460 RETURN(-EINVAL);
57d1b188 461
a0b5ae8a
RC
462 spl_hostid = (long) val;
463 (void) snprintf(hw_serial, HW_HOSTID_LEN, "%u",
464 (val >= 0) ? val : -val);
465 hw_serial[HW_HOSTID_LEN - 1] = '\0';
57d1b188 466 *ppos += *lenp;
467 } else {
468 len = snprintf(str, sizeof(str), "%lx", spl_hostid);
469 if (*ppos >= len)
470 rc = 0;
471 else
472 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
473
474 if (rc >= 0) {
475 *lenp = rc;
476 *ppos += rc;
477 }
478 }
479
480 RETURN(rc);
481}
482
d1ff2312
BB
483#ifndef HAVE_KALLSYMS_LOOKUP_NAME
484static int
485proc_dokallsyms_lookup_name(struct ctl_table *table, int write,
486 struct file *filp, void __user *buffer,
487 size_t *lenp, loff_t *ppos) {
488 int len, rc = 0;
489 char *end, str[32];
490 ENTRY;
491
492 if (write) {
493 /* This may only be set once at module load time */
96dded38 494 if (spl_kallsyms_lookup_name_fn != SYMBOL_POISON)
d1ff2312
BB
495 RETURN(-EEXIST);
496
497 /* We can't use proc_doulongvec_minmax() in the write
498 * case hear because the address while a hex value has no
499 * leading 0x which confuses the helper function. */
500 rc = proc_copyin_string(str, sizeof(str), buffer, *lenp);
501 if (rc < 0)
502 RETURN(rc);
503
504 spl_kallsyms_lookup_name_fn =
505 (kallsyms_lookup_name_t)simple_strtoul(str, &end, 16);
506 if (str == end)
507 RETURN(-EINVAL);
508
509 *ppos += *lenp;
510 } else {
511 len = snprintf(str, sizeof(str), "%lx",
512 (unsigned long)spl_kallsyms_lookup_name_fn);
513 if (*ppos >= len)
514 rc = 0;
515 else
516 rc = proc_copyout_string(buffer,*lenp,str+*ppos,"\n");
517
518 if (rc >= 0) {
519 *lenp = rc;
520 *ppos += rc;
521 }
522 }
523
524 RETURN(rc);
525}
526#endif /* HAVE_KALLSYMS_LOOKUP_NAME */
527
4ab13d3b
BB
528static int
529proc_doavailrmem(struct ctl_table *table, int write, struct file *filp,
530 void __user *buffer, size_t *lenp, loff_t *ppos)
531{
532 int len, rc = 0;
533 char str[32];
534 ENTRY;
535
536 if (write) {
537 *ppos += *lenp;
538 } else {
539 len = snprintf(str, sizeof(str), "%lu", (unsigned long)availrmem);
540 if (*ppos >= len)
541 rc = 0;
542 else
543 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
544
545 if (rc >= 0) {
546 *lenp = rc;
547 *ppos += rc;
548 }
549 }
550
551 RETURN(rc);
552}
553
554static int
555proc_dofreemem(struct ctl_table *table, int write, struct file *filp,
556 void __user *buffer, size_t *lenp, loff_t *ppos)
557{
558 int len, rc = 0;
559 char str[32];
560 ENTRY;
561
562 if (write) {
563 *ppos += *lenp;
564 } else {
565 len = snprintf(str, sizeof(str), "%lu", (unsigned long)freemem);
566 if (*ppos >= len)
567 rc = 0;
568 else
569 rc = proc_copyout_string(buffer, *lenp, str + *ppos, "\n");
570
571 if (rc >= 0) {
572 *lenp = rc;
573 *ppos += rc;
574 }
575 }
576
577 RETURN(rc);
578}
579
ff449ac4 580#ifdef DEBUG_KMEM
581static void
582slab_seq_show_headers(struct seq_file *f)
583{
584 seq_printf(f, "%-36s\n", "name");
585}
586
587static int
588slab_seq_show(struct seq_file *f, void *p)
589{
590 spl_kmem_cache_t *skc = p;
591
592 ASSERT(skc->skc_magic == SKC_MAGIC);
593
594 spin_lock(&skc->skc_lock);
595 seq_printf(f, "%-36s ", skc->skc_name);
a1502d76 596 seq_printf(f, "%u %u %u - %lu %lu %lu - %lu %lu %lu - %lu %lu %lu\n",
ff449ac4 597 (unsigned)skc->skc_obj_size,
a1502d76 598 (unsigned)skc->skc_slab_objs,
ff449ac4 599 (unsigned)skc->skc_slab_size,
ff449ac4 600 (long unsigned)skc->skc_slab_fail,
601 (long unsigned)skc->skc_slab_create,
602 (long unsigned)skc->skc_slab_destroy,
603 (long unsigned)skc->skc_slab_total,
604 (long unsigned)skc->skc_slab_alloc,
605 (long unsigned)skc->skc_slab_max,
606 (long unsigned)skc->skc_obj_total,
607 (long unsigned)skc->skc_obj_alloc,
a1502d76 608 (long unsigned)skc->skc_obj_max);
ff449ac4 609
610 spin_unlock(&skc->skc_lock);
611
612 return 0;
613}
614
615static void *
616slab_seq_start(struct seq_file *f, loff_t *pos)
617{
618 struct list_head *p;
619 loff_t n = *pos;
620 ENTRY;
621
622 down_read(&spl_kmem_cache_sem);
623 if (!n)
624 slab_seq_show_headers(f);
625
626 p = spl_kmem_cache_list.next;
627 while (n--) {
628 p = p->next;
629 if (p == &spl_kmem_cache_list)
630 RETURN(NULL);
631 }
632
633 RETURN(list_entry(p, spl_kmem_cache_t, skc_list));
634}
635
636static void *
637slab_seq_next(struct seq_file *f, void *p, loff_t *pos)
638{
639 spl_kmem_cache_t *skc = p;
640 ENTRY;
641
642 ++*pos;
643 RETURN((skc->skc_list.next == &spl_kmem_cache_list) ?
644 NULL : list_entry(skc->skc_list.next, spl_kmem_cache_t, skc_list));
645}
646
647static void
648slab_seq_stop(struct seq_file *f, void *v)
649{
650 up_read(&spl_kmem_cache_sem);
651}
652
653static struct seq_operations slab_seq_ops = {
654 .show = slab_seq_show,
655 .start = slab_seq_start,
656 .next = slab_seq_next,
657 .stop = slab_seq_stop,
658};
659
660static int
661proc_slab_open(struct inode *inode, struct file *filp)
662{
663 return seq_open(filp, &slab_seq_ops);
664}
665
666static struct file_operations proc_slab_operations = {
667 .open = proc_slab_open,
668 .read = seq_read,
669 .llseek = seq_lseek,
670 .release = seq_release,
671};
672#endif /* DEBUG_KMEM */
673
9ab1ac14 674static struct ctl_table spl_debug_table[] = {
57d1b188 675 {
676 .ctl_name = CTL_DEBUG_SUBSYS,
9ab1ac14 677 .procname = "subsystem",
57d1b188 678 .data = &spl_debug_subsys,
679 .maxlen = sizeof(unsigned long),
680 .mode = 0644,
681 .proc_handler = &proc_dobitmasks
682 },
683 {
684 .ctl_name = CTL_DEBUG_MASK,
9ab1ac14 685 .procname = "mask",
57d1b188 686 .data = &spl_debug_mask,
687 .maxlen = sizeof(unsigned long),
688 .mode = 0644,
689 .proc_handler = &proc_dobitmasks
690 },
691 {
692 .ctl_name = CTL_DEBUG_PRINTK,
9ab1ac14 693 .procname = "printk",
57d1b188 694 .data = &spl_debug_printk,
695 .maxlen = sizeof(unsigned long),
696 .mode = 0644,
697 .proc_handler = &proc_dobitmasks
698 },
699 {
700 .ctl_name = CTL_DEBUG_MB,
9ab1ac14 701 .procname = "mb",
57d1b188 702 .mode = 0644,
703 .proc_handler = &proc_debug_mb,
704 },
705 {
706 .ctl_name = CTL_DEBUG_BINARY,
9ab1ac14 707 .procname = "binary",
57d1b188 708 .data = &spl_debug_binary,
709 .maxlen = sizeof(int),
710 .mode = 0644,
711 .proc_handler = &proc_dointvec,
712 },
713 {
714 .ctl_name = CTL_DEBUG_CATASTROPHE,
715 .procname = "catastrophe",
716 .data = &spl_debug_catastrophe,
717 .maxlen = sizeof(int),
718 .mode = 0444,
719 .proc_handler = &proc_dointvec,
720 },
721 {
722 .ctl_name = CTL_DEBUG_PANIC_ON_BUG,
723 .procname = "panic_on_bug",
724 .data = &spl_debug_panic_on_bug,
725 .maxlen = sizeof(int),
726 .mode = 0644,
727 .proc_handler = &proc_dointvec
728 },
729 {
730 .ctl_name = CTL_DEBUG_PATH,
9ab1ac14 731 .procname = "path",
57d1b188 732 .data = spl_debug_file_path,
733 .maxlen = sizeof(spl_debug_file_path),
734 .mode = 0644,
735 .proc_handler = &proc_dostring,
736 },
737 {
738 .ctl_name = CTL_DEBUG_DUMP,
9ab1ac14 739 .procname = "dump",
57d1b188 740 .mode = 0200,
741 .proc_handler = &proc_dump_kernel,
742 },
743 { .ctl_name = CTL_DEBUG_FORCE_BUG,
744 .procname = "force_bug",
745 .mode = 0200,
746 .proc_handler = &proc_force_bug,
747 },
748 {
749 .ctl_name = CTL_CONSOLE_RATELIMIT,
750 .procname = "console_ratelimit",
751 .data = &spl_console_ratelimit,
752 .maxlen = sizeof(int),
753 .mode = 0644,
754 .proc_handler = &proc_dointvec,
755 },
756 {
757 .ctl_name = CTL_CONSOLE_MAX_DELAY_CS,
758 .procname = "console_max_delay_centisecs",
759 .maxlen = sizeof(int),
760 .mode = 0644,
761 .proc_handler = &proc_console_max_delay_cs,
762 },
763 {
764 .ctl_name = CTL_CONSOLE_MIN_DELAY_CS,
765 .procname = "console_min_delay_centisecs",
766 .maxlen = sizeof(int),
767 .mode = 0644,
768 .proc_handler = &proc_console_min_delay_cs,
769 },
770 {
771 .ctl_name = CTL_CONSOLE_BACKOFF,
772 .procname = "console_backoff",
773 .maxlen = sizeof(int),
774 .mode = 0644,
775 .proc_handler = &proc_console_backoff,
776 },
777 {
9ab1ac14 778 .ctl_name = CTL_DEBUG_STACK_SIZE,
57d1b188 779 .procname = "stack_max",
780 .data = &spl_debug_stack,
781 .maxlen = sizeof(int),
782 .mode = 0444,
783 .proc_handler = &proc_dointvec,
784 },
9ab1ac14 785 {0},
786};
787
36b313da
BB
788static struct ctl_table spl_vm_table[] = {
789 {
790 .ctl_name = CTL_VM_MINFREE,
791 .procname = "minfree",
792 .data = &minfree,
793 .maxlen = sizeof(int),
794 .mode = 0644,
795 .proc_handler = &proc_dointvec,
796 },
797 {
798 .ctl_name = CTL_VM_DESFREE,
799 .procname = "desfree",
800 .data = &desfree,
801 .maxlen = sizeof(int),
802 .mode = 0644,
803 .proc_handler = &proc_dointvec,
804 },
805 {
806 .ctl_name = CTL_VM_LOTSFREE,
807 .procname = "lotsfree",
808 .data = &lotsfree,
809 .maxlen = sizeof(int),
810 .mode = 0644,
811 .proc_handler = &proc_dointvec,
812 },
813 {
814 .ctl_name = CTL_VM_NEEDFREE,
815 .procname = "needfree",
816 .data = &needfree,
817 .maxlen = sizeof(int),
818 .mode = 0444,
819 .proc_handler = &proc_dointvec,
820 },
821 {
822 .ctl_name = CTL_VM_SWAPFS_MINFREE,
823 .procname = "swapfs_minfree",
824 .data = &swapfs_minfree,
825 .maxlen = sizeof(int),
826 .mode = 0644,
827 .proc_handler = &proc_dointvec,
828 },
36b313da
BB
829 {
830 .ctl_name = CTL_VM_SWAPFS_RESERVE,
831 .procname = "swapfs_reserve",
832 .data = &swapfs_reserve,
833 .maxlen = sizeof(int),
834 .mode = 0644,
835 .proc_handler = &proc_dointvec,
836 },
837 {
838 .ctl_name = CTL_VM_AVAILRMEM,
839 .procname = "availrmem",
4ab13d3b
BB
840 .mode = 0444,
841 .proc_handler = &proc_doavailrmem,
842 },
843 {
844 .ctl_name = CTL_VM_FREEMEM,
845 .procname = "freemem",
846 .data = (void *)2,
847 .maxlen = sizeof(int),
848 .mode = 0444,
849 .proc_handler = &proc_dofreemem,
850 },
851 {
852 .ctl_name = CTL_VM_PHYSMEM,
853 .procname = "physmem",
854 .data = &physmem,
36b313da
BB
855 .maxlen = sizeof(int),
856 .mode = 0444,
857 .proc_handler = &proc_dointvec,
858 },
859 {0},
860};
861
57d1b188 862#ifdef DEBUG_KMEM
9ab1ac14 863static struct ctl_table spl_kmem_table[] = {
57d1b188 864 {
865 .ctl_name = CTL_KMEM_KMEMUSED,
866 .procname = "kmem_used",
867 .data = &kmem_alloc_used,
d04c8a56 868# ifdef HAVE_ATOMIC64_T
57d1b188 869 .maxlen = sizeof(atomic64_t),
d04c8a56
BB
870# else
871 .maxlen = sizeof(atomic_t),
872# endif /* HAVE_ATOMIC64_T */
57d1b188 873 .mode = 0444,
d04c8a56 874 .proc_handler = &proc_domemused,
57d1b188 875 },
876 {
877 .ctl_name = CTL_KMEM_KMEMMAX,
878 .procname = "kmem_max",
879 .data = &kmem_alloc_max,
880 .maxlen = sizeof(unsigned long),
881 .extra1 = &table_min,
882 .extra2 = &table_max,
883 .mode = 0444,
884 .proc_handler = &proc_doulongvec_minmax,
885 },
886 {
887 .ctl_name = CTL_KMEM_VMEMUSED,
888 .procname = "vmem_used",
889 .data = &vmem_alloc_used,
d04c8a56 890# ifdef HAVE_ATOMIC64_T
57d1b188 891 .maxlen = sizeof(atomic64_t),
d04c8a56
BB
892# else
893 .maxlen = sizeof(atomic_t),
894# endif /* HAVE_ATOMIC64_T */
57d1b188 895 .mode = 0444,
d04c8a56 896 .proc_handler = &proc_domemused,
57d1b188 897 },
898 {
899 .ctl_name = CTL_KMEM_VMEMMAX,
900 .procname = "vmem_max",
901 .data = &vmem_alloc_max,
902 .maxlen = sizeof(unsigned long),
903 .extra1 = &table_min,
904 .extra2 = &table_max,
905 .mode = 0444,
906 .proc_handler = &proc_doulongvec_minmax,
907 },
9ab1ac14 908 {0},
909};
04a479f7 910#endif /* DEBUG_KMEM */
911
04a479f7 912static struct ctl_table spl_kstat_table[] = {
913 {0},
914};
9ab1ac14 915
916static struct ctl_table spl_table[] = {
917 /* NB No .strategy entries have been provided since
918 * sysctl(8) prefers to go via /proc for portability.
919 */
920 {
921 .ctl_name = CTL_VERSION,
922 .procname = "version",
923 .data = spl_version,
924 .maxlen = sizeof(spl_version),
925 .mode = 0444,
926 .proc_handler = &proc_dostring,
927 },
57d1b188 928 {
929 .ctl_name = CTL_HOSTID,
930 .procname = "hostid",
931 .data = &spl_hostid,
932 .maxlen = sizeof(unsigned long),
933 .mode = 0644,
934 .proc_handler = &proc_dohostid,
935 },
936 {
937 .ctl_name = CTL_HW_SERIAL,
938 .procname = "hw_serial",
937879f1 939 .data = hw_serial,
940 .maxlen = sizeof(hw_serial),
57d1b188 941 .mode = 0444,
942 .proc_handler = &proc_dostring,
943 },
d1ff2312
BB
944#ifndef HAVE_KALLSYMS_LOOKUP_NAME
945 {
946 .ctl_name = CTL_KALLSYMS,
947 .procname = "kallsyms_lookup_name",
948 .data = &spl_kallsyms_lookup_name_fn,
949 .maxlen = sizeof(unsigned long),
950 .mode = 0644,
951 .proc_handler = &proc_dokallsyms_lookup_name,
952 },
953#endif
9ab1ac14 954 {
955 .ctl_name = CTL_SPL_DEBUG,
956 .procname = "debug",
957 .mode = 0555,
958 .child = spl_debug_table,
959 },
36b313da
BB
960 {
961 .ctl_name = CTL_SPL_VM,
962 .procname = "vm",
963 .mode = 0555,
964 .child = spl_vm_table,
965 },
9ab1ac14 966#ifdef DEBUG_KMEM
967 {
968 .ctl_name = CTL_SPL_KMEM,
969 .procname = "kmem",
970 .mode = 0555,
971 .child = spl_kmem_table,
972 },
04a479f7 973#endif
04a479f7 974 {
975 .ctl_name = CTL_SPL_KSTAT,
976 .procname = "kstat",
977 .mode = 0555,
978 .child = spl_kstat_table,
979 },
57d1b188 980 { 0 },
981};
982
9ab1ac14 983static struct ctl_table spl_dir[] = {
57d1b188 984 {
985 .ctl_name = CTL_SPL,
986 .procname = "spl",
987 .mode = 0555,
988 .child = spl_table,
989 },
57d86234 990 { 0 }
991};
992
993static struct ctl_table spl_root[] = {
994 {
995 .ctl_name = CTL_KERN,
996 .procname = "kernel",
997 .mode = 0555,
998 .child = spl_dir,
999 },
1000 { 0 }
57d1b188 1001};
1002
404992e3 1003static int
1004proc_dir_entry_match(int len, const char *name, struct proc_dir_entry *de)
1005{
1006 if (de->namelen != len)
1007 return 0;
1008
1009 return !memcmp(name, de->name, len);
1010}
1011
04a479f7 1012struct proc_dir_entry *
404992e3 1013proc_dir_entry_find(struct proc_dir_entry *root, const char *str)
1014{
1015 struct proc_dir_entry *de;
1016
1017 for (de = root->subdir; de; de = de->next)
1018 if (proc_dir_entry_match(strlen(str), str, de))
1019 return de;
1020
1021 return NULL;
1022}
1023
04a479f7 1024int
1025proc_dir_entries(struct proc_dir_entry *root)
1026{
1027 struct proc_dir_entry *de;
1028 int i = 0;
1029
1030 for (de = root->subdir; de; de = de->next)
1031 i++;
1032
1033 return i;
1034}
1035
57d1b188 1036int
1037proc_init(void)
1038{
404992e3 1039 int rc = 0;
57d1b188 1040 ENTRY;
1041
1042#ifdef CONFIG_SYSCTL
57d86234 1043 spl_header = spl_register_sysctl_table(spl_root, 0);
57d1b188 1044 if (spl_header == NULL)
1045 RETURN(-EUNATCH);
c30df9c8 1046#endif /* CONFIG_SYSCTL */
9ab1ac14 1047
c30df9c8 1048 proc_spl = proc_mkdir("spl", NULL);
1049 if (proc_spl == NULL)
404992e3 1050 GOTO(out, rc = -EUNATCH);
1051
04a479f7 1052#ifdef DEBUG_KMEM
c30df9c8 1053 proc_spl_kmem = proc_mkdir("kmem", proc_spl);
1054 if (proc_spl_kmem == NULL)
1055 GOTO(out, rc = -EUNATCH);
ff449ac4 1056
1057 proc_spl_kmem_slab = create_proc_entry("slab", 0444, proc_spl_kmem);
1058 if (proc_spl_kmem_slab == NULL)
1059 GOTO(out, rc = -EUNATCH);
1060
1061 proc_spl_kmem_slab->proc_fops = &proc_slab_operations;
04a479f7 1062#endif /* DEBUG_KMEM */
1063
c30df9c8 1064 proc_spl_kstat = proc_mkdir("kstat", proc_spl);
1065 if (proc_spl_kstat == NULL)
1066 GOTO(out, rc = -EUNATCH);
404992e3 1067out:
c30df9c8 1068 if (rc) {
1069 remove_proc_entry("kstat", proc_spl);
ff449ac4 1070#ifdef DEBUG_KMEM
1071 remove_proc_entry("slab", proc_spl_kmem);
c30df9c8 1072 remove_proc_entry("kmem", proc_spl);
055ffd98 1073#endif
a02118a8 1074 remove_proc_entry("spl", NULL);
c30df9c8 1075#ifdef CONFIG_SYSCTL
1076 spl_unregister_sysctl_table(spl_header);
404992e3 1077#endif /* CONFIG_SYSCTL */
c30df9c8 1078 }
c30df9c8 1079
404992e3 1080 RETURN(rc);
57d1b188 1081}
1082
1083void
1084proc_fini(void)
1085{
1086 ENTRY;
1087
c30df9c8 1088 remove_proc_entry("kstat", proc_spl);
ff449ac4 1089#ifdef DEBUG_KMEM
1090 remove_proc_entry("slab", proc_spl_kmem);
c30df9c8 1091 remove_proc_entry("kmem", proc_spl);
055ffd98 1092#endif
a02118a8 1093 remove_proc_entry("spl", NULL);
c30df9c8 1094
57d1b188 1095#ifdef CONFIG_SYSCTL
1096 ASSERT(spl_header != NULL);
57d86234 1097 spl_unregister_sysctl_table(spl_header);
c30df9c8 1098#endif /* CONFIG_SYSCTL */
1099
57d1b188 1100 EXIT;
1101}