xref: /freebsd/sys/contrib/openzfs/module/os/linux/spl/spl-proc.c (revision 3f0efe05432b1633991114ca4ca330102a561959)
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