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