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