xref: /freebsd/sys/contrib/openzfs/module/os/linux/spl/spl-proc.c (revision 7a7741af18d6c8a804cc643cb7ecda9d730c6aa6)
1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy  *  Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3eda14cbcSMatt Macy  *  Copyright (C) 2007 The Regents of the University of California.
4eda14cbcSMatt Macy  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5eda14cbcSMatt Macy  *  Written by Brian Behlendorf <behlendorf1@llnl.gov>.
6eda14cbcSMatt Macy  *  UCRL-CODE-235197
7eda14cbcSMatt Macy  *
8eda14cbcSMatt Macy  *  This file is part of the SPL, Solaris Porting Layer.
9eda14cbcSMatt Macy  *
10eda14cbcSMatt Macy  *  The SPL is free software; you can redistribute it and/or modify it
11eda14cbcSMatt Macy  *  under the terms of the GNU General Public License as published by the
12eda14cbcSMatt Macy  *  Free Software Foundation; either version 2 of the License, or (at your
13eda14cbcSMatt Macy  *  option) any later version.
14eda14cbcSMatt Macy  *
15eda14cbcSMatt Macy  *  The SPL is distributed in the hope that it will be useful, but WITHOUT
16eda14cbcSMatt Macy  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17eda14cbcSMatt Macy  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18eda14cbcSMatt Macy  *  for more details.
19eda14cbcSMatt Macy  *
20eda14cbcSMatt Macy  *  You should have received a copy of the GNU General Public License along
21eda14cbcSMatt Macy  *  with the SPL.  If not, see <http://www.gnu.org/licenses/>.
22eda14cbcSMatt Macy  *
23eda14cbcSMatt Macy  *  Solaris Porting Layer (SPL) Proc Implementation.
24eda14cbcSMatt Macy  */
2529dc9349SMartin Matuska /*
2629dc9349SMartin Matuska  * Copyright (c) 2024, Rob Norris <robn@despairlabs.com>
2729dc9349SMartin Matuska  */
28eda14cbcSMatt Macy 
29eda14cbcSMatt Macy #include <sys/systeminfo.h>
30eda14cbcSMatt Macy #include <sys/kstat.h>
31eda14cbcSMatt Macy #include <sys/kmem.h>
32eda14cbcSMatt Macy #include <sys/kmem_cache.h>
33eda14cbcSMatt Macy #include <sys/vmem.h>
34eda14cbcSMatt Macy #include <sys/proc.h>
35eda14cbcSMatt Macy #include <linux/ctype.h>
36eda14cbcSMatt Macy #include <linux/kmod.h>
37eda14cbcSMatt Macy #include <linux/seq_file.h>
38eda14cbcSMatt Macy #include <linux/uaccess.h>
39eda14cbcSMatt Macy #include <linux/version.h>
40e92ffd9bSMartin Matuska #include "zfs_gitrev.h"
41eda14cbcSMatt Macy 
42*7a7741afSMartin Matuska #if defined(CONSTIFY_PLUGIN)
43eda14cbcSMatt Macy typedef struct ctl_table __no_const spl_ctl_table;
44eda14cbcSMatt Macy #else
45eda14cbcSMatt Macy typedef struct ctl_table spl_ctl_table;
46eda14cbcSMatt Macy #endif
47eda14cbcSMatt Macy 
4829dc9349SMartin Matuska #ifdef HAVE_PROC_HANDLER_CTL_TABLE_CONST
4929dc9349SMartin Matuska #define	CONST_CTL_TABLE		const struct ctl_table
5029dc9349SMartin Matuska #else
5129dc9349SMartin Matuska #define	CONST_CTL_TABLE		struct ctl_table
5229dc9349SMartin Matuska #endif
5329dc9349SMartin Matuska 
54eda14cbcSMatt Macy static unsigned long table_min = 0;
55eda14cbcSMatt Macy static unsigned long table_max = ~0;
56eda14cbcSMatt Macy 
57eda14cbcSMatt Macy static struct ctl_table_header *spl_header = NULL;
583159b89bSMartin Matuska #ifndef HAVE_REGISTER_SYSCTL_TABLE
593159b89bSMartin Matuska static struct ctl_table_header *spl_kmem = NULL;
603159b89bSMartin Matuska static struct ctl_table_header *spl_kstat = NULL;
613159b89bSMartin Matuska #endif
62eda14cbcSMatt Macy static struct proc_dir_entry *proc_spl = NULL;
63eda14cbcSMatt Macy static struct proc_dir_entry *proc_spl_kmem = NULL;
64eda14cbcSMatt Macy static struct proc_dir_entry *proc_spl_kmem_slab = NULL;
65eda14cbcSMatt Macy struct proc_dir_entry *proc_spl_kstat = NULL;
66eda14cbcSMatt Macy 
67eda14cbcSMatt Macy #ifdef DEBUG_KMEM
68eda14cbcSMatt Macy static int
proc_domemused(CONST_CTL_TABLE * table,int write,void __user * buffer,size_t * lenp,loff_t * ppos)6929dc9349SMartin Matuska proc_domemused(CONST_CTL_TABLE *table, int write,
70eda14cbcSMatt Macy     void __user *buffer, size_t *lenp, loff_t *ppos)
71eda14cbcSMatt Macy {
72eda14cbcSMatt Macy 	int rc = 0;
7316038816SMartin Matuska 	unsigned long val;
74eda14cbcSMatt Macy 	spl_ctl_table dummy = *table;
75eda14cbcSMatt Macy 
76eda14cbcSMatt Macy 	dummy.data = &val;
77eda14cbcSMatt Macy 	dummy.proc_handler = &proc_dointvec;
7816038816SMartin Matuska 	dummy.extra1 = &table_min;
7916038816SMartin Matuska 	dummy.extra2 = &table_max;
80eda14cbcSMatt Macy 
81eda14cbcSMatt Macy 	if (write) {
82eda14cbcSMatt Macy 		*ppos += *lenp;
83eda14cbcSMatt Macy 	} else {
84eda14cbcSMatt Macy #ifdef HAVE_ATOMIC64_T
85eda14cbcSMatt Macy 		val = atomic64_read((atomic64_t *)table->data);
86eda14cbcSMatt Macy #else
87eda14cbcSMatt Macy 		val = atomic_read((atomic_t *)table->data);
88eda14cbcSMatt Macy #endif /* HAVE_ATOMIC64_T */
89eda14cbcSMatt Macy 		rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
90eda14cbcSMatt Macy 	}
91eda14cbcSMatt Macy 
92eda14cbcSMatt Macy 	return (rc);
93eda14cbcSMatt Macy }
94eda14cbcSMatt Macy #endif /* DEBUG_KMEM */
95eda14cbcSMatt Macy 
96eda14cbcSMatt Macy static int
proc_doslab(CONST_CTL_TABLE * table,int write,void __user * buffer,size_t * lenp,loff_t * ppos)9729dc9349SMartin Matuska proc_doslab(CONST_CTL_TABLE *table, int write,
98eda14cbcSMatt Macy     void __user *buffer, size_t *lenp, loff_t *ppos)
99eda14cbcSMatt Macy {
100eda14cbcSMatt Macy 	int rc = 0;
10116038816SMartin Matuska 	unsigned long val = 0, mask;
102eda14cbcSMatt Macy 	spl_ctl_table dummy = *table;
103eda14cbcSMatt Macy 	spl_kmem_cache_t *skc = NULL;
104eda14cbcSMatt Macy 
105eda14cbcSMatt Macy 	dummy.data = &val;
106eda14cbcSMatt Macy 	dummy.proc_handler = &proc_dointvec;
10716038816SMartin Matuska 	dummy.extra1 = &table_min;
10816038816SMartin Matuska 	dummy.extra2 = &table_max;
109eda14cbcSMatt Macy 
110eda14cbcSMatt Macy 	if (write) {
111eda14cbcSMatt Macy 		*ppos += *lenp;
112eda14cbcSMatt Macy 	} else {
113eda14cbcSMatt Macy 		down_read(&spl_kmem_cache_sem);
114eda14cbcSMatt Macy 		mask = (unsigned long)table->data;
115eda14cbcSMatt Macy 
116eda14cbcSMatt Macy 		list_for_each_entry(skc, &spl_kmem_cache_list, skc_list) {
117eda14cbcSMatt Macy 
118eda14cbcSMatt Macy 			/* Only use slabs of the correct kmem/vmem type */
119eda14cbcSMatt Macy 			if (!(skc->skc_flags & mask))
120eda14cbcSMatt Macy 				continue;
121eda14cbcSMatt Macy 
122eda14cbcSMatt Macy 			/* Sum the specified field for selected slabs */
123eda14cbcSMatt Macy 			switch (mask & (KMC_TOTAL | KMC_ALLOC | KMC_MAX)) {
124eda14cbcSMatt Macy 			case KMC_TOTAL:
125eda14cbcSMatt Macy 				val += skc->skc_slab_size * skc->skc_slab_total;
126eda14cbcSMatt Macy 				break;
127eda14cbcSMatt Macy 			case KMC_ALLOC:
128eda14cbcSMatt Macy 				val += skc->skc_obj_size * skc->skc_obj_alloc;
129eda14cbcSMatt Macy 				break;
130eda14cbcSMatt Macy 			case KMC_MAX:
131eda14cbcSMatt Macy 				val += skc->skc_obj_size * skc->skc_obj_max;
132eda14cbcSMatt Macy 				break;
133eda14cbcSMatt Macy 			}
134eda14cbcSMatt Macy 		}
135eda14cbcSMatt Macy 
136eda14cbcSMatt Macy 		up_read(&spl_kmem_cache_sem);
137eda14cbcSMatt Macy 		rc = proc_doulongvec_minmax(&dummy, write, buffer, lenp, ppos);
138eda14cbcSMatt Macy 	}
139eda14cbcSMatt Macy 
140eda14cbcSMatt Macy 	return (rc);
141eda14cbcSMatt Macy }
142eda14cbcSMatt Macy 
143eda14cbcSMatt Macy static int
proc_dohostid(CONST_CTL_TABLE * table,int write,void __user * buffer,size_t * lenp,loff_t * ppos)14429dc9349SMartin Matuska proc_dohostid(CONST_CTL_TABLE *table, int write,
145eda14cbcSMatt Macy     void __user *buffer, size_t *lenp, loff_t *ppos)
146eda14cbcSMatt Macy {
147eda14cbcSMatt Macy 	char *end, str[32];
14816038816SMartin Matuska 	unsigned long hid;
14916038816SMartin Matuska 	spl_ctl_table dummy = *table;
15016038816SMartin Matuska 
15116038816SMartin Matuska 	dummy.data = str;
15216038816SMartin Matuska 	dummy.maxlen = sizeof (str) - 1;
15316038816SMartin Matuska 
15416038816SMartin Matuska 	if (!write)
15516038816SMartin Matuska 		snprintf(str, sizeof (str), "%lx",
15616038816SMartin Matuska 		    (unsigned long) zone_get_hostid(NULL));
15716038816SMartin Matuska 
15816038816SMartin Matuska 	/* always returns 0 */
15916038816SMartin Matuska 	proc_dostring(&dummy, write, buffer, lenp, ppos);
160eda14cbcSMatt Macy 
161eda14cbcSMatt Macy 	if (write) {
162eda14cbcSMatt Macy 		/*
163eda14cbcSMatt Macy 		 * We can't use proc_doulongvec_minmax() in the write
16416038816SMartin Matuska 		 * case here because hostid, while a hex value, has no
16516038816SMartin Matuska 		 * leading 0x, which confuses the helper function.
166eda14cbcSMatt Macy 		 */
167eda14cbcSMatt Macy 
16816038816SMartin Matuska 		hid = simple_strtoul(str, &end, 16);
169eda14cbcSMatt Macy 		if (str == end)
170eda14cbcSMatt Macy 			return (-EINVAL);
17116038816SMartin Matuska 		spl_hostid = hid;
172eda14cbcSMatt Macy 	}
173eda14cbcSMatt Macy 
17416038816SMartin Matuska 	return (0);
175eda14cbcSMatt Macy }
176eda14cbcSMatt Macy 
177eda14cbcSMatt Macy static void
slab_seq_show_headers(struct seq_file * f)178eda14cbcSMatt Macy slab_seq_show_headers(struct seq_file *f)
179eda14cbcSMatt Macy {
180eda14cbcSMatt Macy 	seq_printf(f,
181eda14cbcSMatt Macy 	    "--------------------- cache ----------"
182eda14cbcSMatt Macy 	    "---------------------------------------------  "
183eda14cbcSMatt Macy 	    "----- slab ------  "
184eda14cbcSMatt Macy 	    "---- object -----  "
185eda14cbcSMatt Macy 	    "--- emergency ---\n");
186eda14cbcSMatt Macy 	seq_printf(f,
187eda14cbcSMatt Macy 	    "name                                  "
188eda14cbcSMatt Macy 	    "  flags      size     alloc slabsize  objsize  "
189eda14cbcSMatt Macy 	    "total alloc   max  "
190eda14cbcSMatt Macy 	    "total alloc   max  "
191eda14cbcSMatt Macy 	    "dlock alloc   max\n");
192eda14cbcSMatt Macy }
193eda14cbcSMatt Macy 
194eda14cbcSMatt Macy static int
slab_seq_show(struct seq_file * f,void * p)195eda14cbcSMatt Macy slab_seq_show(struct seq_file *f, void *p)
196eda14cbcSMatt Macy {
197eda14cbcSMatt Macy 	spl_kmem_cache_t *skc = p;
198eda14cbcSMatt Macy 
199eda14cbcSMatt Macy 	ASSERT(skc->skc_magic == SKC_MAGIC);
200eda14cbcSMatt Macy 
201eda14cbcSMatt Macy 	if (skc->skc_flags & KMC_SLAB) {
202eda14cbcSMatt Macy 		/*
203eda14cbcSMatt Macy 		 * This cache is backed by a generic Linux kmem cache which
204eda14cbcSMatt Macy 		 * has its own accounting. For these caches we only track
205eda14cbcSMatt Macy 		 * the number of active allocated objects that exist within
206eda14cbcSMatt Macy 		 * the underlying Linux slabs. For the overall statistics of
207eda14cbcSMatt Macy 		 * the underlying Linux cache please refer to /proc/slabinfo.
208eda14cbcSMatt Macy 		 */
209eda14cbcSMatt Macy 		spin_lock(&skc->skc_lock);
210eda14cbcSMatt Macy 		uint64_t objs_allocated =
211eda14cbcSMatt Macy 		    percpu_counter_sum(&skc->skc_linux_alloc);
212eda14cbcSMatt Macy 		seq_printf(f, "%-36s  ", skc->skc_name);
213eda14cbcSMatt Macy 		seq_printf(f, "0x%05lx %9s %9lu %8s %8u  "
214eda14cbcSMatt Macy 		    "%5s %5s %5s  %5s %5lu %5s  %5s %5s %5s\n",
215eda14cbcSMatt Macy 		    (long unsigned)skc->skc_flags,
216eda14cbcSMatt Macy 		    "-",
217eda14cbcSMatt Macy 		    (long unsigned)(skc->skc_obj_size * objs_allocated),
218eda14cbcSMatt Macy 		    "-",
219eda14cbcSMatt Macy 		    (unsigned)skc->skc_obj_size,
220eda14cbcSMatt Macy 		    "-", "-", "-", "-",
221eda14cbcSMatt Macy 		    (long unsigned)objs_allocated,
222eda14cbcSMatt Macy 		    "-", "-", "-", "-");
223eda14cbcSMatt Macy 		spin_unlock(&skc->skc_lock);
224eda14cbcSMatt Macy 		return (0);
225eda14cbcSMatt Macy 	}
226eda14cbcSMatt Macy 
227eda14cbcSMatt Macy 	spin_lock(&skc->skc_lock);
228eda14cbcSMatt Macy 	seq_printf(f, "%-36s  ", skc->skc_name);
229eda14cbcSMatt Macy 	seq_printf(f, "0x%05lx %9lu %9lu %8u %8u  "
230eda14cbcSMatt Macy 	    "%5lu %5lu %5lu  %5lu %5lu %5lu  %5lu %5lu %5lu\n",
231eda14cbcSMatt Macy 	    (long unsigned)skc->skc_flags,
232eda14cbcSMatt Macy 	    (long unsigned)(skc->skc_slab_size * skc->skc_slab_total),
233eda14cbcSMatt Macy 	    (long unsigned)(skc->skc_obj_size * skc->skc_obj_alloc),
234eda14cbcSMatt Macy 	    (unsigned)skc->skc_slab_size,
235eda14cbcSMatt Macy 	    (unsigned)skc->skc_obj_size,
236eda14cbcSMatt Macy 	    (long unsigned)skc->skc_slab_total,
237eda14cbcSMatt Macy 	    (long unsigned)skc->skc_slab_alloc,
238eda14cbcSMatt Macy 	    (long unsigned)skc->skc_slab_max,
239eda14cbcSMatt Macy 	    (long unsigned)skc->skc_obj_total,
240eda14cbcSMatt Macy 	    (long unsigned)skc->skc_obj_alloc,
241eda14cbcSMatt Macy 	    (long unsigned)skc->skc_obj_max,
242eda14cbcSMatt Macy 	    (long unsigned)skc->skc_obj_deadlock,
243eda14cbcSMatt Macy 	    (long unsigned)skc->skc_obj_emergency,
244eda14cbcSMatt Macy 	    (long unsigned)skc->skc_obj_emergency_max);
245eda14cbcSMatt Macy 	spin_unlock(&skc->skc_lock);
246eda14cbcSMatt Macy 	return (0);
247eda14cbcSMatt Macy }
248eda14cbcSMatt Macy 
249eda14cbcSMatt Macy static void *
slab_seq_start(struct seq_file * f,loff_t * pos)250eda14cbcSMatt Macy slab_seq_start(struct seq_file *f, loff_t *pos)
251eda14cbcSMatt Macy {
252eda14cbcSMatt Macy 	struct list_head *p;
253eda14cbcSMatt Macy 	loff_t n = *pos;
254eda14cbcSMatt Macy 
255eda14cbcSMatt Macy 	down_read(&spl_kmem_cache_sem);
256eda14cbcSMatt Macy 	if (!n)
257eda14cbcSMatt Macy 		slab_seq_show_headers(f);
258eda14cbcSMatt Macy 
259eda14cbcSMatt Macy 	p = spl_kmem_cache_list.next;
260eda14cbcSMatt Macy 	while (n--) {
261eda14cbcSMatt Macy 		p = p->next;
262eda14cbcSMatt Macy 		if (p == &spl_kmem_cache_list)
263eda14cbcSMatt Macy 			return (NULL);
264eda14cbcSMatt Macy 	}
265eda14cbcSMatt Macy 
266eda14cbcSMatt Macy 	return (list_entry(p, spl_kmem_cache_t, skc_list));
267eda14cbcSMatt Macy }
268eda14cbcSMatt Macy 
269eda14cbcSMatt Macy static void *
slab_seq_next(struct seq_file * f,void * p,loff_t * pos)270eda14cbcSMatt Macy slab_seq_next(struct seq_file *f, void *p, loff_t *pos)
271eda14cbcSMatt Macy {
272eda14cbcSMatt Macy 	spl_kmem_cache_t *skc = p;
273eda14cbcSMatt Macy 
274eda14cbcSMatt Macy 	++*pos;
275eda14cbcSMatt Macy 	return ((skc->skc_list.next == &spl_kmem_cache_list) ?
276eda14cbcSMatt Macy 	    NULL : list_entry(skc->skc_list.next, spl_kmem_cache_t, skc_list));
277eda14cbcSMatt Macy }
278eda14cbcSMatt Macy 
279eda14cbcSMatt Macy static void
slab_seq_stop(struct seq_file * f,void * v)280eda14cbcSMatt Macy slab_seq_stop(struct seq_file *f, void *v)
281eda14cbcSMatt Macy {
282eda14cbcSMatt Macy 	up_read(&spl_kmem_cache_sem);
283eda14cbcSMatt Macy }
284eda14cbcSMatt Macy 
285e92ffd9bSMartin Matuska static const struct seq_operations slab_seq_ops = {
286eda14cbcSMatt Macy 	.show  = slab_seq_show,
287eda14cbcSMatt Macy 	.start = slab_seq_start,
288eda14cbcSMatt Macy 	.next  = slab_seq_next,
289eda14cbcSMatt Macy 	.stop  = slab_seq_stop,
290eda14cbcSMatt Macy };
291eda14cbcSMatt Macy 
292eda14cbcSMatt Macy static int
proc_slab_open(struct inode * inode,struct file * filp)293eda14cbcSMatt Macy proc_slab_open(struct inode *inode, struct file *filp)
294eda14cbcSMatt Macy {
295eda14cbcSMatt Macy 	return (seq_open(filp, &slab_seq_ops));
296eda14cbcSMatt Macy }
297eda14cbcSMatt Macy 
298eda14cbcSMatt Macy static const kstat_proc_op_t proc_slab_operations = {
299eda14cbcSMatt Macy #ifdef HAVE_PROC_OPS_STRUCT
300eda14cbcSMatt Macy 	.proc_open	= proc_slab_open,
301eda14cbcSMatt Macy 	.proc_read	= seq_read,
302eda14cbcSMatt Macy 	.proc_lseek	= seq_lseek,
303eda14cbcSMatt Macy 	.proc_release	= seq_release,
304eda14cbcSMatt Macy #else
305eda14cbcSMatt Macy 	.open		= proc_slab_open,
306eda14cbcSMatt Macy 	.read		= seq_read,
307eda14cbcSMatt Macy 	.llseek		= seq_lseek,
308eda14cbcSMatt Macy 	.release	= seq_release,
309eda14cbcSMatt Macy #endif
310eda14cbcSMatt Macy };
311eda14cbcSMatt Macy 
312eda14cbcSMatt Macy static struct ctl_table spl_kmem_table[] = {
313eda14cbcSMatt Macy #ifdef DEBUG_KMEM
314eda14cbcSMatt Macy 	{
315eda14cbcSMatt Macy 		.procname	= "kmem_used",
316eda14cbcSMatt Macy 		.data		= &kmem_alloc_used,
317eda14cbcSMatt Macy #ifdef HAVE_ATOMIC64_T
318eda14cbcSMatt Macy 		.maxlen		= sizeof (atomic64_t),
319eda14cbcSMatt Macy #else
320eda14cbcSMatt Macy 		.maxlen		= sizeof (atomic_t),
321eda14cbcSMatt Macy #endif /* HAVE_ATOMIC64_T */
322eda14cbcSMatt Macy 		.mode		= 0444,
323eda14cbcSMatt Macy 		.proc_handler	= &proc_domemused,
324eda14cbcSMatt Macy 	},
325eda14cbcSMatt Macy 	{
326eda14cbcSMatt Macy 		.procname	= "kmem_max",
327eda14cbcSMatt Macy 		.data		= &kmem_alloc_max,
328eda14cbcSMatt Macy 		.maxlen		= sizeof (unsigned long),
329eda14cbcSMatt Macy 		.extra1		= &table_min,
330eda14cbcSMatt Macy 		.extra2		= &table_max,
331eda14cbcSMatt Macy 		.mode		= 0444,
332eda14cbcSMatt Macy 		.proc_handler	= &proc_doulongvec_minmax,
333eda14cbcSMatt Macy 	},
334eda14cbcSMatt Macy #endif /* DEBUG_KMEM */
335eda14cbcSMatt Macy 	{
336eda14cbcSMatt Macy 		.procname	= "slab_kvmem_total",
337eda14cbcSMatt Macy 		.data		= (void *)(KMC_KVMEM | KMC_TOTAL),
338eda14cbcSMatt Macy 		.maxlen		= sizeof (unsigned long),
339eda14cbcSMatt Macy 		.extra1		= &table_min,
340eda14cbcSMatt Macy 		.extra2		= &table_max,
341eda14cbcSMatt Macy 		.mode		= 0444,
342eda14cbcSMatt Macy 		.proc_handler	= &proc_doslab,
343eda14cbcSMatt Macy 	},
344eda14cbcSMatt Macy 	{
345eda14cbcSMatt Macy 		.procname	= "slab_kvmem_alloc",
346eda14cbcSMatt Macy 		.data		= (void *)(KMC_KVMEM | KMC_ALLOC),
347eda14cbcSMatt Macy 		.maxlen		= sizeof (unsigned long),
348eda14cbcSMatt Macy 		.extra1		= &table_min,
349eda14cbcSMatt Macy 		.extra2		= &table_max,
350eda14cbcSMatt Macy 		.mode		= 0444,
351eda14cbcSMatt Macy 		.proc_handler	= &proc_doslab,
352eda14cbcSMatt Macy 	},
353eda14cbcSMatt Macy 	{
354eda14cbcSMatt Macy 		.procname	= "slab_kvmem_max",
355eda14cbcSMatt Macy 		.data		= (void *)(KMC_KVMEM | KMC_MAX),
356eda14cbcSMatt Macy 		.maxlen		= sizeof (unsigned long),
357eda14cbcSMatt Macy 		.extra1		= &table_min,
358eda14cbcSMatt Macy 		.extra2		= &table_max,
359eda14cbcSMatt Macy 		.mode		= 0444,
360eda14cbcSMatt Macy 		.proc_handler	= &proc_doslab,
361eda14cbcSMatt Macy 	},
362eda14cbcSMatt Macy 	{},
363eda14cbcSMatt Macy };
364eda14cbcSMatt Macy 
365eda14cbcSMatt Macy static struct ctl_table spl_kstat_table[] = {
366eda14cbcSMatt Macy 	{},
367eda14cbcSMatt Macy };
368eda14cbcSMatt Macy 
369eda14cbcSMatt Macy static struct ctl_table spl_table[] = {
370eda14cbcSMatt Macy 	/*
371eda14cbcSMatt Macy 	 * NB No .strategy entries have been provided since
372eda14cbcSMatt Macy 	 * sysctl(8) prefers to go via /proc for portability.
373eda14cbcSMatt Macy 	 */
374eda14cbcSMatt Macy 	{
375eda14cbcSMatt Macy 		.procname	= "gitrev",
376e92ffd9bSMartin Matuska 		.data		= (char *)ZFS_META_GITREV,
377e92ffd9bSMartin Matuska 		.maxlen		= sizeof (ZFS_META_GITREV),
378eda14cbcSMatt Macy 		.mode		= 0444,
379eda14cbcSMatt Macy 		.proc_handler	= &proc_dostring,
380eda14cbcSMatt Macy 	},
381eda14cbcSMatt Macy 	{
382eda14cbcSMatt Macy 		.procname	= "hostid",
383eda14cbcSMatt Macy 		.data		= &spl_hostid,
384eda14cbcSMatt Macy 		.maxlen		= sizeof (unsigned long),
385eda14cbcSMatt Macy 		.mode		= 0644,
386eda14cbcSMatt Macy 		.proc_handler	= &proc_dohostid,
387eda14cbcSMatt Macy 	},
388315ee00fSMartin Matuska #ifdef HAVE_REGISTER_SYSCTL_TABLE
389eda14cbcSMatt Macy 	{
390eda14cbcSMatt Macy 		.procname	= "kmem",
391eda14cbcSMatt Macy 		.mode		= 0555,
392eda14cbcSMatt Macy 		.child		= spl_kmem_table,
393eda14cbcSMatt Macy 	},
394eda14cbcSMatt Macy 	{
395eda14cbcSMatt Macy 		.procname	= "kstat",
396eda14cbcSMatt Macy 		.mode		= 0555,
397eda14cbcSMatt Macy 		.child		= spl_kstat_table,
398eda14cbcSMatt Macy 	},
399315ee00fSMartin Matuska #endif
400eda14cbcSMatt Macy 	{},
401eda14cbcSMatt Macy };
402eda14cbcSMatt Macy 
403315ee00fSMartin Matuska #ifdef HAVE_REGISTER_SYSCTL_TABLE
404eda14cbcSMatt Macy static struct ctl_table spl_dir[] = {
405eda14cbcSMatt Macy 	{
406eda14cbcSMatt Macy 		.procname	= "spl",
407eda14cbcSMatt Macy 		.mode		= 0555,
408eda14cbcSMatt Macy 		.child		= spl_table,
409eda14cbcSMatt Macy 	},
410eda14cbcSMatt Macy 	{}
411eda14cbcSMatt Macy };
412eda14cbcSMatt Macy 
413eda14cbcSMatt Macy static struct ctl_table spl_root[] = {
414eda14cbcSMatt Macy 	{
415eda14cbcSMatt Macy 		.procname	= "kernel",
416eda14cbcSMatt Macy 		.mode		= 0555,
417eda14cbcSMatt Macy 		.child		= spl_dir,
418eda14cbcSMatt Macy 	},
419eda14cbcSMatt Macy 	{}
420eda14cbcSMatt Macy };
421315ee00fSMartin Matuska #endif
422eda14cbcSMatt Macy 
spl_proc_cleanup(void)4232ad756a6SMartin Matuska static void spl_proc_cleanup(void)
4242ad756a6SMartin Matuska {
4252ad756a6SMartin Matuska 	remove_proc_entry("kstat", proc_spl);
4262ad756a6SMartin Matuska 	remove_proc_entry("slab", proc_spl_kmem);
4272ad756a6SMartin Matuska 	remove_proc_entry("kmem", proc_spl);
4282ad756a6SMartin Matuska 	remove_proc_entry("spl", NULL);
4292ad756a6SMartin Matuska 
4303159b89bSMartin Matuska #ifndef HAVE_REGISTER_SYSCTL_TABLE
4313159b89bSMartin Matuska 	if (spl_kstat) {
4323159b89bSMartin Matuska 		unregister_sysctl_table(spl_kstat);
4333159b89bSMartin Matuska 		spl_kstat = NULL;
4343159b89bSMartin Matuska 	}
4353159b89bSMartin Matuska 	if (spl_kmem) {
4363159b89bSMartin Matuska 		unregister_sysctl_table(spl_kmem);
4373159b89bSMartin Matuska 		spl_kmem = NULL;
4383159b89bSMartin Matuska 	}
4393159b89bSMartin Matuska #endif
4402ad756a6SMartin Matuska 	if (spl_header) {
4412ad756a6SMartin Matuska 		unregister_sysctl_table(spl_header);
4422ad756a6SMartin Matuska 		spl_header = NULL;
4432ad756a6SMartin Matuska 	}
4442ad756a6SMartin Matuska }
4452ad756a6SMartin Matuska 
44629dc9349SMartin Matuska #ifndef HAVE_REGISTER_SYSCTL_TABLE
44729dc9349SMartin Matuska 
44829dc9349SMartin Matuska /*
44929dc9349SMartin Matuska  * Traditionally, struct ctl_table arrays have been terminated by an "empty"
45029dc9349SMartin Matuska  * sentinel element (specifically, one with .procname == NULL).
45129dc9349SMartin Matuska  *
45229dc9349SMartin Matuska  * Linux 6.6 began migrating away from this, adding register_sysctl_sz() so
45329dc9349SMartin Matuska  * that callers could provide the size directly, and redefining
45429dc9349SMartin Matuska  * register_sysctl() to just call register_sysctl_sz() with the array size. It
45529dc9349SMartin Matuska  * retained support for the terminating element so that existing callers would
45629dc9349SMartin Matuska  * continue to work.
45729dc9349SMartin Matuska  *
45829dc9349SMartin Matuska  * Linux 6.11 removed support for the terminating element, instead interpreting
45929dc9349SMartin Matuska  * it as a real malformed element, and rejecting it.
46029dc9349SMartin Matuska  *
46129dc9349SMartin Matuska  * In order to continue support older kernels, we retain the terminating
46229dc9349SMartin Matuska  * sentinel element for our sysctl tables, but instead detect availability of
46329dc9349SMartin Matuska  * register_sysctl_sz(). If it exists, we pass it the array size -1, stopping
46429dc9349SMartin Matuska  * the kernel from trying to process the terminator. For pre-6.6 kernels that
46529dc9349SMartin Matuska  * don't have register_sysctl_sz(), we just use register_sysctl(), which can
46629dc9349SMartin Matuska  * handle the terminating element as it always has.
46729dc9349SMartin Matuska  */
46829dc9349SMartin Matuska #ifdef HAVE_REGISTER_SYSCTL_SZ
46929dc9349SMartin Matuska #define	spl_proc_register_sysctl(p, t)	\
47029dc9349SMartin Matuska 	register_sysctl_sz(p, t, ARRAY_SIZE(t)-1)
47129dc9349SMartin Matuska #else
47229dc9349SMartin Matuska #define	spl_proc_register_sysctl(p, t)	\
47329dc9349SMartin Matuska 	register_sysctl(p, t)
47429dc9349SMartin Matuska #endif
47529dc9349SMartin Matuska #endif
47629dc9349SMartin Matuska 
477eda14cbcSMatt Macy int
spl_proc_init(void)478eda14cbcSMatt Macy spl_proc_init(void)
479eda14cbcSMatt Macy {
480eda14cbcSMatt Macy 	int rc = 0;
481eda14cbcSMatt Macy 
482315ee00fSMartin Matuska #ifdef HAVE_REGISTER_SYSCTL_TABLE
483eda14cbcSMatt Macy 	spl_header = register_sysctl_table(spl_root);
484eda14cbcSMatt Macy 	if (spl_header == NULL)
485eda14cbcSMatt Macy 		return (-EUNATCH);
486315ee00fSMartin Matuska #else
48729dc9349SMartin Matuska 	spl_header = spl_proc_register_sysctl("kernel/spl", spl_table);
488315ee00fSMartin Matuska 	if (spl_header == NULL)
489315ee00fSMartin Matuska 		return (-EUNATCH);
490315ee00fSMartin Matuska 
49129dc9349SMartin Matuska 	spl_kmem = spl_proc_register_sysctl("kernel/spl/kmem", spl_kmem_table);
4923159b89bSMartin Matuska 	if (spl_kmem == NULL) {
493315ee00fSMartin Matuska 		rc = -EUNATCH;
494315ee00fSMartin Matuska 		goto out;
495315ee00fSMartin Matuska 	}
49629dc9349SMartin Matuska 	spl_kstat = spl_proc_register_sysctl("kernel/spl/kstat",
49729dc9349SMartin Matuska 	    spl_kstat_table);
4983159b89bSMartin Matuska 	if (spl_kstat == NULL) {
499315ee00fSMartin Matuska 		rc = -EUNATCH;
500315ee00fSMartin Matuska 		goto out;
501315ee00fSMartin Matuska 	}
502315ee00fSMartin Matuska #endif
503eda14cbcSMatt Macy 
504eda14cbcSMatt Macy 	proc_spl = proc_mkdir("spl", NULL);
505eda14cbcSMatt Macy 	if (proc_spl == NULL) {
506eda14cbcSMatt Macy 		rc = -EUNATCH;
507eda14cbcSMatt Macy 		goto out;
508eda14cbcSMatt Macy 	}
509eda14cbcSMatt Macy 
510eda14cbcSMatt Macy 	proc_spl_kmem = proc_mkdir("kmem", proc_spl);
511eda14cbcSMatt Macy 	if (proc_spl_kmem == NULL) {
512eda14cbcSMatt Macy 		rc = -EUNATCH;
513eda14cbcSMatt Macy 		goto out;
514eda14cbcSMatt Macy 	}
515eda14cbcSMatt Macy 
516eda14cbcSMatt Macy 	proc_spl_kmem_slab = proc_create_data("slab", 0444, proc_spl_kmem,
517eda14cbcSMatt Macy 	    &proc_slab_operations, NULL);
518eda14cbcSMatt Macy 	if (proc_spl_kmem_slab == NULL) {
519eda14cbcSMatt Macy 		rc = -EUNATCH;
520eda14cbcSMatt Macy 		goto out;
521eda14cbcSMatt Macy 	}
522eda14cbcSMatt Macy 
523eda14cbcSMatt Macy 	proc_spl_kstat = proc_mkdir("kstat", proc_spl);
524eda14cbcSMatt Macy 	if (proc_spl_kstat == NULL) {
525eda14cbcSMatt Macy 		rc = -EUNATCH;
526eda14cbcSMatt Macy 		goto out;
527eda14cbcSMatt Macy 	}
528eda14cbcSMatt Macy out:
5292ad756a6SMartin Matuska 	if (rc)
5302ad756a6SMartin Matuska 		spl_proc_cleanup();
531eda14cbcSMatt Macy 
532eda14cbcSMatt Macy 	return (rc);
533eda14cbcSMatt Macy }
534eda14cbcSMatt Macy 
535eda14cbcSMatt Macy void
spl_proc_fini(void)536eda14cbcSMatt Macy spl_proc_fini(void)
537eda14cbcSMatt Macy {
5382ad756a6SMartin Matuska 	spl_proc_cleanup();
539eda14cbcSMatt Macy }
540