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