]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/module/os/linux/spl/spl-proc.c
Upgrade to version 3.1.6
[FreeBSD/FreeBSD.git] / sys / contrib / openzfs / module / os / linux / spl / spl-proc.c
1 /*
2  *  Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3  *  Copyright (C) 2007 The Regents of the University of California.
4  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5  *  Written by Brian Behlendorf <behlendorf1@llnl.gov>.
6  *  UCRL-CODE-235197
7  *
8  *  This file is part of the SPL, Solaris Porting Layer.
9  *  For details, see <http://zfsonlinux.org/>.
10  *
11  *  The SPL is free software; you can redistribute it and/or modify it
12  *  under the terms of the GNU General Public License as published by the
13  *  Free Software Foundation; either version 2 of the License, or (at your
14  *  option) any later version.
15  *
16  *  The SPL is distributed in the hope that it will be useful, but WITHOUT
17  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
19  *  for more details.
20  *
21  *  You should have received a copy of the GNU General Public License along
22  *  with the SPL.  If not, see <http://www.gnu.org/licenses/>.
23  *
24  *  Solaris Porting Layer (SPL) Proc Implementation.
25  */
26
27 #include <sys/systeminfo.h>
28 #include <sys/kstat.h>
29 #include <sys/kmem.h>
30 #include <sys/kmem_cache.h>
31 #include <sys/vmem.h>
32 #include <sys/taskq.h>
33 #include <sys/proc.h>
34 #include <linux/ctype.h>
35 #include <linux/kmod.h>
36 #include <linux/seq_file.h>
37 #include <linux/uaccess.h>
38 #include <linux/version.h>
39
40 #if defined(CONSTIFY_PLUGIN) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
41 typedef struct ctl_table __no_const spl_ctl_table;
42 #else
43 typedef struct ctl_table spl_ctl_table;
44 #endif
45
46 static unsigned long table_min = 0;
47 static unsigned long table_max = ~0;
48
49 static struct ctl_table_header *spl_header = NULL;
50 static struct proc_dir_entry *proc_spl = NULL;
51 static struct proc_dir_entry *proc_spl_kmem = NULL;
52 static struct proc_dir_entry *proc_spl_kmem_slab = NULL;
53 static struct proc_dir_entry *proc_spl_taskq_all = NULL;
54 static struct proc_dir_entry *proc_spl_taskq = NULL;
55 struct proc_dir_entry *proc_spl_kstat = NULL;
56
57 static int
58 proc_copyin_string(char *kbuffer, int kbuffer_size, const char *ubuffer,
59     int ubuffer_size)
60 {
61         int size;
62
63         if (ubuffer_size > kbuffer_size)
64                 return (-EOVERFLOW);
65
66         if (copy_from_user((void *)kbuffer, (void *)ubuffer, ubuffer_size))
67                 return (-EFAULT);
68
69         /* strip trailing whitespace */
70         size = strnlen(kbuffer, ubuffer_size);
71         while (size-- >= 0)
72                 if (!isspace(kbuffer[size]))
73                         break;
74
75         /* empty string */
76         if (size < 0)
77                 return (-EINVAL);
78
79         /* no space to terminate */
80         if (size == kbuffer_size)
81                 return (-EOVERFLOW);
82
83         kbuffer[size + 1] = 0;
84         return (0);
85 }
86
87 static int
88 proc_copyout_string(char *ubuffer, int ubuffer_size, const char *kbuffer,
89     char *append)
90 {
91         /*
92          * NB if 'append' != NULL, it's a single character to append to the
93          * copied out string - usually "\n", for /proc entries and
94          * (i.e. a terminating zero byte) for sysctl entries
95          */
96         int size = MIN(strlen(kbuffer), ubuffer_size);
97
98         if (copy_to_user(ubuffer, kbuffer, size))
99                 return (-EFAULT);
100
101         if (append != NULL && size < ubuffer_size) {
102                 if (copy_to_user(ubuffer + size, append, 1))
103                         return (-EFAULT);
104
105                 size++;
106         }
107
108         return (size);
109 }
110
111 #ifdef DEBUG_KMEM
112 static int
113 proc_domemused(struct ctl_table *table, int write,
114     void __user *buffer, size_t *lenp, loff_t *ppos)
115 {
116         int rc = 0;
117         unsigned long min = 0, max = ~0, val;
118         spl_ctl_table dummy = *table;
119
120         dummy.data = &val;
121         dummy.proc_handler = &proc_dointvec;
122         dummy.extra1 = &min;
123         dummy.extra2 = &max;
124
125         if (write) {
126                 *ppos += *lenp;
127         } else {
128 #ifdef HAVE_ATOMIC64_T
129                 val = atomic64_read((atomic64_t *)table->data);
130 #else
131                 val = atomic_read((atomic_t *)table->data);
132 #endif /* HAVE_ATOMIC64_T */
133                 rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
134         }
135
136         return (rc);
137 }
138 #endif /* DEBUG_KMEM */
139
140 static int
141 proc_doslab(struct ctl_table *table, int write,
142     void __user *buffer, size_t *lenp, loff_t *ppos)
143 {
144         int rc = 0;
145         unsigned long min = 0, max = ~0, val = 0, mask;
146         spl_ctl_table dummy = *table;
147         spl_kmem_cache_t *skc = NULL;
148
149         dummy.data = &val;
150         dummy.proc_handler = &proc_dointvec;
151         dummy.extra1 = &min;
152         dummy.extra2 = &max;
153
154         if (write) {
155                 *ppos += *lenp;
156         } else {
157                 down_read(&spl_kmem_cache_sem);
158                 mask = (unsigned long)table->data;
159
160                 list_for_each_entry(skc, &spl_kmem_cache_list, skc_list) {
161
162                         /* Only use slabs of the correct kmem/vmem type */
163                         if (!(skc->skc_flags & mask))
164                                 continue;
165
166                         /* Sum the specified field for selected slabs */
167                         switch (mask & (KMC_TOTAL | KMC_ALLOC | KMC_MAX)) {
168                         case KMC_TOTAL:
169                                 val += skc->skc_slab_size * skc->skc_slab_total;
170                                 break;
171                         case KMC_ALLOC:
172                                 val += skc->skc_obj_size * skc->skc_obj_alloc;
173                                 break;
174                         case KMC_MAX:
175                                 val += skc->skc_obj_size * skc->skc_obj_max;
176                                 break;
177                         }
178                 }
179
180                 up_read(&spl_kmem_cache_sem);
181                 rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
182         }
183
184         return (rc);
185 }
186
187 static int
188 proc_dohostid(struct ctl_table *table, int write,
189     void __user *buffer, size_t *lenp, loff_t *ppos)
190 {
191         int len, rc = 0;
192         char *end, str[32];
193
194         if (write) {
195                 /*
196                  * We can't use proc_doulongvec_minmax() in the write
197                  * case here because hostid while a hex value has no
198                  * leading 0x which confuses the helper function.
199                  */
200                 rc = proc_copyin_string(str, sizeof (str), buffer, *lenp);
201                 if (rc < 0)
202                         return (rc);
203
204                 spl_hostid = simple_strtoul(str, &end, 16);
205                 if (str == end)
206                         return (-EINVAL);
207
208         } else {
209                 len = snprintf(str, sizeof (str), "%lx",
210                     (unsigned long) zone_get_hostid(NULL));
211                 if (*ppos >= len)
212                         rc = 0;
213                 else
214                         rc = proc_copyout_string(buffer,
215                             *lenp, str + *ppos, "\n");
216
217                 if (rc >= 0) {
218                         *lenp = rc;
219                         *ppos += rc;
220                 }
221         }
222
223         return (rc);
224 }
225
226 static void
227 taskq_seq_show_headers(struct seq_file *f)
228 {
229         seq_printf(f, "%-25s %5s %5s %5s %5s %5s %5s %12s %5s %10s\n",
230             "taskq", "act", "nthr", "spwn", "maxt", "pri",
231             "mina", "maxa", "cura", "flags");
232 }
233
234 /* indices into the lheads array below */
235 #define LHEAD_PEND      0
236 #define LHEAD_PRIO      1
237 #define LHEAD_DELAY     2
238 #define LHEAD_WAIT      3
239 #define LHEAD_ACTIVE    4
240 #define LHEAD_SIZE      5
241
242 /* BEGIN CSTYLED */
243 static unsigned int spl_max_show_tasks = 512;
244 module_param(spl_max_show_tasks, uint, 0644);
245 MODULE_PARM_DESC(spl_max_show_tasks, "Max number of tasks shown in taskq proc");
246 /* END CSTYLED */
247
248 static int
249 taskq_seq_show_impl(struct seq_file *f, void *p, boolean_t allflag)
250 {
251         taskq_t *tq = p;
252         taskq_thread_t *tqt = NULL;
253         spl_wait_queue_entry_t *wq;
254         struct task_struct *tsk;
255         taskq_ent_t *tqe;
256         char name[100];
257         struct list_head *lheads[LHEAD_SIZE], *lh;
258         static char *list_names[LHEAD_SIZE] =
259             {"pend", "prio", "delay", "wait", "active" };
260         int i, j, have_lheads = 0;
261         unsigned long wflags, flags;
262
263         spin_lock_irqsave_nested(&tq->tq_lock, flags, tq->tq_lock_class);
264         spin_lock_irqsave(&tq->tq_wait_waitq.lock, wflags);
265
266         /* get the various lists and check whether they're empty */
267         lheads[LHEAD_PEND] = &tq->tq_pend_list;
268         lheads[LHEAD_PRIO] = &tq->tq_prio_list;
269         lheads[LHEAD_DELAY] = &tq->tq_delay_list;
270 #ifdef HAVE_WAIT_QUEUE_HEAD_ENTRY
271         lheads[LHEAD_WAIT] = &tq->tq_wait_waitq.head;
272 #else
273         lheads[LHEAD_WAIT] = &tq->tq_wait_waitq.task_list;
274 #endif
275         lheads[LHEAD_ACTIVE] = &tq->tq_active_list;
276
277         for (i = 0; i < LHEAD_SIZE; ++i) {
278                 if (list_empty(lheads[i]))
279                         lheads[i] = NULL;
280                 else
281                         ++have_lheads;
282         }
283
284         /* early return in non-"all" mode if lists are all empty */
285         if (!allflag && !have_lheads) {
286                 spin_unlock_irqrestore(&tq->tq_wait_waitq.lock, wflags);
287                 spin_unlock_irqrestore(&tq->tq_lock, flags);
288                 return (0);
289         }
290
291         /* unlock the waitq quickly */
292         if (!lheads[LHEAD_WAIT])
293                 spin_unlock_irqrestore(&tq->tq_wait_waitq.lock, wflags);
294
295         /* show the base taskq contents */
296         snprintf(name, sizeof (name), "%s/%d", tq->tq_name, tq->tq_instance);
297         seq_printf(f, "%-25s ", name);
298         seq_printf(f, "%5d %5d %5d %5d %5d %5d %12d %5d %10x\n",
299             tq->tq_nactive, tq->tq_nthreads, tq->tq_nspawn,
300             tq->tq_maxthreads, tq->tq_pri, tq->tq_minalloc, tq->tq_maxalloc,
301             tq->tq_nalloc, tq->tq_flags);
302
303         /* show the active list */
304         if (lheads[LHEAD_ACTIVE]) {
305                 j = 0;
306                 list_for_each_entry(tqt, &tq->tq_active_list, tqt_active_list) {
307                         if (j == 0)
308                                 seq_printf(f, "\t%s:",
309                                     list_names[LHEAD_ACTIVE]);
310                         else if (j == 2) {
311                                 seq_printf(f, "\n\t       ");
312                                 j = 0;
313                         }
314                         seq_printf(f, " [%d]%pf(%ps)",
315                             tqt->tqt_thread->pid,
316                             tqt->tqt_task->tqent_func,
317                             tqt->tqt_task->tqent_arg);
318                         ++j;
319                 }
320                 seq_printf(f, "\n");
321         }
322
323         for (i = LHEAD_PEND; i <= LHEAD_WAIT; ++i)
324                 if (lheads[i]) {
325                         j = 0;
326                         list_for_each(lh, lheads[i]) {
327                                 if (spl_max_show_tasks != 0 &&
328                                     j >= spl_max_show_tasks) {
329                                         seq_printf(f, "\n\t(truncated)");
330                                         break;
331                                 }
332                                 /* show the wait waitq list */
333                                 if (i == LHEAD_WAIT) {
334 #ifdef HAVE_WAIT_QUEUE_HEAD_ENTRY
335                                         wq = list_entry(lh,
336                                             spl_wait_queue_entry_t, entry);
337 #else
338                                         wq = list_entry(lh,
339                                             spl_wait_queue_entry_t, task_list);
340 #endif
341                                         if (j == 0)
342                                                 seq_printf(f, "\t%s:",
343                                                     list_names[i]);
344                                         else if (j % 8 == 0)
345                                                 seq_printf(f, "\n\t     ");
346
347                                         tsk = wq->private;
348                                         seq_printf(f, " %d", tsk->pid);
349                                 /* pend, prio and delay lists */
350                                 } else {
351                                         tqe = list_entry(lh, taskq_ent_t,
352                                             tqent_list);
353                                         if (j == 0)
354                                                 seq_printf(f, "\t%s:",
355                                                     list_names[i]);
356                                         else if (j % 2 == 0)
357                                                 seq_printf(f, "\n\t     ");
358
359                                         seq_printf(f, " %pf(%ps)",
360                                             tqe->tqent_func,
361                                             tqe->tqent_arg);
362                                 }
363                                 ++j;
364                         }
365                         seq_printf(f, "\n");
366                 }
367         if (lheads[LHEAD_WAIT])
368                 spin_unlock_irqrestore(&tq->tq_wait_waitq.lock, wflags);
369         spin_unlock_irqrestore(&tq->tq_lock, flags);
370
371         return (0);
372 }
373
374 static int
375 taskq_all_seq_show(struct seq_file *f, void *p)
376 {
377         return (taskq_seq_show_impl(f, p, B_TRUE));
378 }
379
380 static int
381 taskq_seq_show(struct seq_file *f, void *p)
382 {
383         return (taskq_seq_show_impl(f, p, B_FALSE));
384 }
385
386 static void *
387 taskq_seq_start(struct seq_file *f, loff_t *pos)
388 {
389         struct list_head *p;
390         loff_t n = *pos;
391
392         down_read(&tq_list_sem);
393         if (!n)
394                 taskq_seq_show_headers(f);
395
396         p = tq_list.next;
397         while (n--) {
398                 p = p->next;
399                 if (p == &tq_list)
400                 return (NULL);
401         }
402
403         return (list_entry(p, taskq_t, tq_taskqs));
404 }
405
406 static void *
407 taskq_seq_next(struct seq_file *f, void *p, loff_t *pos)
408 {
409         taskq_t *tq = p;
410
411         ++*pos;
412         return ((tq->tq_taskqs.next == &tq_list) ?
413             NULL : list_entry(tq->tq_taskqs.next, taskq_t, tq_taskqs));
414 }
415
416 static void
417 slab_seq_show_headers(struct seq_file *f)
418 {
419         seq_printf(f,
420             "--------------------- cache ----------"
421             "---------------------------------------------  "
422             "----- slab ------  "
423             "---- object -----  "
424             "--- emergency ---\n");
425         seq_printf(f,
426             "name                                  "
427             "  flags      size     alloc slabsize  objsize  "
428             "total alloc   max  "
429             "total alloc   max  "
430             "dlock alloc   max\n");
431 }
432
433 static int
434 slab_seq_show(struct seq_file *f, void *p)
435 {
436         spl_kmem_cache_t *skc = p;
437
438         ASSERT(skc->skc_magic == SKC_MAGIC);
439
440         if (skc->skc_flags & KMC_SLAB) {
441                 /*
442                  * This cache is backed by a generic Linux kmem cache which
443                  * has its own accounting. For these caches we only track
444                  * the number of active allocated objects that exist within
445                  * the underlying Linux slabs. For the overall statistics of
446                  * the underlying Linux cache please refer to /proc/slabinfo.
447                  */
448                 spin_lock(&skc->skc_lock);
449                 uint64_t objs_allocated =
450                     percpu_counter_sum(&skc->skc_linux_alloc);
451                 seq_printf(f, "%-36s  ", skc->skc_name);
452                 seq_printf(f, "0x%05lx %9s %9lu %8s %8u  "
453                     "%5s %5s %5s  %5s %5lu %5s  %5s %5s %5s\n",
454                     (long unsigned)skc->skc_flags,
455                     "-",
456                     (long unsigned)(skc->skc_obj_size * objs_allocated),
457                     "-",
458                     (unsigned)skc->skc_obj_size,
459                     "-", "-", "-", "-",
460                     (long unsigned)objs_allocated,
461                     "-", "-", "-", "-");
462                 spin_unlock(&skc->skc_lock);
463                 return (0);
464         }
465
466         spin_lock(&skc->skc_lock);
467         seq_printf(f, "%-36s  ", skc->skc_name);
468         seq_printf(f, "0x%05lx %9lu %9lu %8u %8u  "
469             "%5lu %5lu %5lu  %5lu %5lu %5lu  %5lu %5lu %5lu\n",
470             (long unsigned)skc->skc_flags,
471             (long unsigned)(skc->skc_slab_size * skc->skc_slab_total),
472             (long unsigned)(skc->skc_obj_size * skc->skc_obj_alloc),
473             (unsigned)skc->skc_slab_size,
474             (unsigned)skc->skc_obj_size,
475             (long unsigned)skc->skc_slab_total,
476             (long unsigned)skc->skc_slab_alloc,
477             (long unsigned)skc->skc_slab_max,
478             (long unsigned)skc->skc_obj_total,
479             (long unsigned)skc->skc_obj_alloc,
480             (long unsigned)skc->skc_obj_max,
481             (long unsigned)skc->skc_obj_deadlock,
482             (long unsigned)skc->skc_obj_emergency,
483             (long unsigned)skc->skc_obj_emergency_max);
484         spin_unlock(&skc->skc_lock);
485         return (0);
486 }
487
488 static void *
489 slab_seq_start(struct seq_file *f, loff_t *pos)
490 {
491         struct list_head *p;
492         loff_t n = *pos;
493
494         down_read(&spl_kmem_cache_sem);
495         if (!n)
496                 slab_seq_show_headers(f);
497
498         p = spl_kmem_cache_list.next;
499         while (n--) {
500                 p = p->next;
501                 if (p == &spl_kmem_cache_list)
502                         return (NULL);
503         }
504
505         return (list_entry(p, spl_kmem_cache_t, skc_list));
506 }
507
508 static void *
509 slab_seq_next(struct seq_file *f, void *p, loff_t *pos)
510 {
511         spl_kmem_cache_t *skc = p;
512
513         ++*pos;
514         return ((skc->skc_list.next == &spl_kmem_cache_list) ?
515             NULL : list_entry(skc->skc_list.next, spl_kmem_cache_t, skc_list));
516 }
517
518 static void
519 slab_seq_stop(struct seq_file *f, void *v)
520 {
521         up_read(&spl_kmem_cache_sem);
522 }
523
524 static struct seq_operations slab_seq_ops = {
525         .show  = slab_seq_show,
526         .start = slab_seq_start,
527         .next  = slab_seq_next,
528         .stop  = slab_seq_stop,
529 };
530
531 static int
532 proc_slab_open(struct inode *inode, struct file *filp)
533 {
534         return (seq_open(filp, &slab_seq_ops));
535 }
536
537 static const kstat_proc_op_t proc_slab_operations = {
538 #ifdef HAVE_PROC_OPS_STRUCT
539         .proc_open      = proc_slab_open,
540         .proc_read      = seq_read,
541         .proc_lseek     = seq_lseek,
542         .proc_release   = seq_release,
543 #else
544         .open           = proc_slab_open,
545         .read           = seq_read,
546         .llseek         = seq_lseek,
547         .release        = seq_release,
548 #endif
549 };
550
551 static void
552 taskq_seq_stop(struct seq_file *f, void *v)
553 {
554         up_read(&tq_list_sem);
555 }
556
557 static struct seq_operations taskq_all_seq_ops = {
558         .show   = taskq_all_seq_show,
559         .start  = taskq_seq_start,
560         .next   = taskq_seq_next,
561         .stop   = taskq_seq_stop,
562 };
563
564 static struct seq_operations taskq_seq_ops = {
565         .show   = taskq_seq_show,
566         .start  = taskq_seq_start,
567         .next   = taskq_seq_next,
568         .stop   = taskq_seq_stop,
569 };
570
571 static int
572 proc_taskq_all_open(struct inode *inode, struct file *filp)
573 {
574         return (seq_open(filp, &taskq_all_seq_ops));
575 }
576
577 static int
578 proc_taskq_open(struct inode *inode, struct file *filp)
579 {
580         return (seq_open(filp, &taskq_seq_ops));
581 }
582
583 static const kstat_proc_op_t proc_taskq_all_operations = {
584 #ifdef HAVE_PROC_OPS_STRUCT
585         .proc_open      = proc_taskq_all_open,
586         .proc_read      = seq_read,
587         .proc_lseek     = seq_lseek,
588         .proc_release   = seq_release,
589 #else
590         .open           = proc_taskq_all_open,
591         .read           = seq_read,
592         .llseek         = seq_lseek,
593         .release        = seq_release,
594 #endif
595 };
596
597 static const kstat_proc_op_t proc_taskq_operations = {
598 #ifdef HAVE_PROC_OPS_STRUCT
599         .proc_open      = proc_taskq_open,
600         .proc_read      = seq_read,
601         .proc_lseek     = seq_lseek,
602         .proc_release   = seq_release,
603 #else
604         .open           = proc_taskq_open,
605         .read           = seq_read,
606         .llseek         = seq_lseek,
607         .release        = seq_release,
608 #endif
609 };
610
611 static struct ctl_table spl_kmem_table[] = {
612 #ifdef DEBUG_KMEM
613         {
614                 .procname       = "kmem_used",
615                 .data           = &kmem_alloc_used,
616 #ifdef HAVE_ATOMIC64_T
617                 .maxlen         = sizeof (atomic64_t),
618 #else
619                 .maxlen         = sizeof (atomic_t),
620 #endif /* HAVE_ATOMIC64_T */
621                 .mode           = 0444,
622                 .proc_handler   = &proc_domemused,
623         },
624         {
625                 .procname       = "kmem_max",
626                 .data           = &kmem_alloc_max,
627                 .maxlen         = sizeof (unsigned long),
628                 .extra1         = &table_min,
629                 .extra2         = &table_max,
630                 .mode           = 0444,
631                 .proc_handler   = &proc_doulongvec_minmax,
632         },
633 #endif /* DEBUG_KMEM */
634         {
635                 .procname       = "slab_kvmem_total",
636                 .data           = (void *)(KMC_KVMEM | KMC_TOTAL),
637                 .maxlen         = sizeof (unsigned long),
638                 .extra1         = &table_min,
639                 .extra2         = &table_max,
640                 .mode           = 0444,
641                 .proc_handler   = &proc_doslab,
642         },
643         {
644                 .procname       = "slab_kvmem_alloc",
645                 .data           = (void *)(KMC_KVMEM | KMC_ALLOC),
646                 .maxlen         = sizeof (unsigned long),
647                 .extra1         = &table_min,
648                 .extra2         = &table_max,
649                 .mode           = 0444,
650                 .proc_handler   = &proc_doslab,
651         },
652         {
653                 .procname       = "slab_kvmem_max",
654                 .data           = (void *)(KMC_KVMEM | KMC_MAX),
655                 .maxlen         = sizeof (unsigned long),
656                 .extra1         = &table_min,
657                 .extra2         = &table_max,
658                 .mode           = 0444,
659                 .proc_handler   = &proc_doslab,
660         },
661         {},
662 };
663
664 static struct ctl_table spl_kstat_table[] = {
665         {},
666 };
667
668 static struct ctl_table spl_table[] = {
669         /*
670          * NB No .strategy entries have been provided since
671          * sysctl(8) prefers to go via /proc for portability.
672          */
673         {
674                 .procname       = "gitrev",
675                 .data           = spl_gitrev,
676                 .maxlen         = sizeof (spl_gitrev),
677                 .mode           = 0444,
678                 .proc_handler   = &proc_dostring,
679         },
680         {
681                 .procname       = "hostid",
682                 .data           = &spl_hostid,
683                 .maxlen         = sizeof (unsigned long),
684                 .mode           = 0644,
685                 .proc_handler   = &proc_dohostid,
686         },
687         {
688                 .procname       = "kmem",
689                 .mode           = 0555,
690                 .child          = spl_kmem_table,
691         },
692         {
693                 .procname       = "kstat",
694                 .mode           = 0555,
695                 .child          = spl_kstat_table,
696         },
697         {},
698 };
699
700 static struct ctl_table spl_dir[] = {
701         {
702                 .procname       = "spl",
703                 .mode           = 0555,
704                 .child          = spl_table,
705         },
706         {}
707 };
708
709 static struct ctl_table spl_root[] = {
710         {
711         .procname = "kernel",
712         .mode = 0555,
713         .child = spl_dir,
714         },
715         {}
716 };
717
718 int
719 spl_proc_init(void)
720 {
721         int rc = 0;
722
723         spl_header = register_sysctl_table(spl_root);
724         if (spl_header == NULL)
725                 return (-EUNATCH);
726
727         proc_spl = proc_mkdir("spl", NULL);
728         if (proc_spl == NULL) {
729                 rc = -EUNATCH;
730                 goto out;
731         }
732
733         proc_spl_taskq_all = proc_create_data("taskq-all", 0444, proc_spl,
734             &proc_taskq_all_operations, NULL);
735         if (proc_spl_taskq_all == NULL) {
736                 rc = -EUNATCH;
737                 goto out;
738         }
739
740         proc_spl_taskq = proc_create_data("taskq", 0444, proc_spl,
741             &proc_taskq_operations, NULL);
742         if (proc_spl_taskq == NULL) {
743                 rc = -EUNATCH;
744                 goto out;
745         }
746
747         proc_spl_kmem = proc_mkdir("kmem", proc_spl);
748         if (proc_spl_kmem == NULL) {
749                 rc = -EUNATCH;
750                 goto out;
751         }
752
753         proc_spl_kmem_slab = proc_create_data("slab", 0444, proc_spl_kmem,
754             &proc_slab_operations, NULL);
755         if (proc_spl_kmem_slab == NULL) {
756                 rc = -EUNATCH;
757                 goto out;
758         }
759
760         proc_spl_kstat = proc_mkdir("kstat", proc_spl);
761         if (proc_spl_kstat == NULL) {
762                 rc = -EUNATCH;
763                 goto out;
764         }
765 out:
766         if (rc) {
767                 remove_proc_entry("kstat", proc_spl);
768                 remove_proc_entry("slab", proc_spl_kmem);
769                 remove_proc_entry("kmem", proc_spl);
770                 remove_proc_entry("taskq-all", proc_spl);
771                 remove_proc_entry("taskq", proc_spl);
772                 remove_proc_entry("spl", NULL);
773                 unregister_sysctl_table(spl_header);
774         }
775
776         return (rc);
777 }
778
779 void
780 spl_proc_fini(void)
781 {
782         remove_proc_entry("kstat", proc_spl);
783         remove_proc_entry("slab", proc_spl_kmem);
784         remove_proc_entry("kmem", proc_spl);
785         remove_proc_entry("taskq-all", proc_spl);
786         remove_proc_entry("taskq", proc_spl);
787         remove_proc_entry("spl", NULL);
788
789         ASSERT(spl_header != NULL);
790         unregister_sysctl_table(spl_header);
791 }