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 * Copyright (c) 2024, Rob Norris <[email protected]>
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
proc_domemused(CONST_CTL_TABLE * table,int write,void __user * buffer,size_t * lenp,loff_t * ppos)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
proc_doslab(CONST_CTL_TABLE * table,int write,void __user * buffer,size_t * lenp,loff_t * ppos)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
proc_dohostid(CONST_CTL_TABLE * table,int write,void __user * buffer,size_t * lenp,loff_t * ppos)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
taskq_seq_show_headers(struct seq_file * f)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
taskq_seq_show_impl(struct seq_file * f,void * p,boolean_t allflag)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
taskq_all_seq_show(struct seq_file * f,void * p)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
taskq_seq_show(struct seq_file * f,void * p)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 *
taskq_seq_start(struct seq_file * f,loff_t * pos)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 *
taskq_seq_next(struct seq_file * f,void * p,loff_t * pos)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
slab_seq_show_headers(struct seq_file * f)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
slab_seq_show(struct seq_file * f,void * p)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 *
slab_seq_start(struct seq_file * f,loff_t * pos)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 *
slab_seq_next(struct seq_file * f,void * p,loff_t * pos)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
slab_seq_stop(struct seq_file * f,void * v)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
proc_slab_open(struct inode * inode,struct file * filp)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
taskq_seq_stop(struct seq_file * f,void * v)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
proc_taskq_all_open(struct inode * inode,struct file * filp)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
proc_taskq_open(struct inode * inode,struct file * filp)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
spl_proc_cleanup(void)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
spl_proc_init(void)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
spl_proc_fini(void)804 spl_proc_fini(void)
805 {
806 spl_proc_cleanup();
807 }
808