1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3eda14cbcSMatt Macy * All rights reserved.
4eda14cbcSMatt Macy *
5eda14cbcSMatt Macy * Redistribution and use in source and binary forms, with or without
6eda14cbcSMatt Macy * modification, are permitted provided that the following conditions
7eda14cbcSMatt Macy * are met:
8eda14cbcSMatt Macy * 1. Redistributions of source code must retain the above copyright
9eda14cbcSMatt Macy * notice, this list of conditions and the following disclaimer.
10eda14cbcSMatt Macy * 2. Redistributions in binary form must reproduce the above copyright
11eda14cbcSMatt Macy * notice, this list of conditions and the following disclaimer in the
12eda14cbcSMatt Macy * documentation and/or other materials provided with the distribution.
13eda14cbcSMatt Macy *
14eda14cbcSMatt Macy * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15eda14cbcSMatt Macy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16eda14cbcSMatt Macy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17eda14cbcSMatt Macy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18eda14cbcSMatt Macy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19eda14cbcSMatt Macy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20eda14cbcSMatt Macy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21eda14cbcSMatt Macy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22eda14cbcSMatt Macy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23eda14cbcSMatt Macy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24eda14cbcSMatt Macy * SUCH DAMAGE.
25eac7052fSMatt Macy *
26eac7052fSMatt Macy * Links to Illumos.org for more information on kstat function:
27eac7052fSMatt Macy * [1] https://illumos.org/man/1M/kstat
28eac7052fSMatt Macy * [2] https://illumos.org/man/9f/kstat_create
29eda14cbcSMatt Macy */
30eda14cbcSMatt Macy
31eda14cbcSMatt Macy #include <sys/types.h>
32eda14cbcSMatt Macy #include <sys/param.h>
33eda14cbcSMatt Macy #include <sys/kernel.h>
34eda14cbcSMatt Macy #include <sys/systm.h>
35eda14cbcSMatt Macy #include <sys/malloc.h>
36eda14cbcSMatt Macy #include <sys/sysctl.h>
37eda14cbcSMatt Macy #include <sys/kstat.h>
38eac7052fSMatt Macy #include <sys/sbuf.h>
3915f0b8c3SMartin Matuska #include <sys/zone.h>
40eda14cbcSMatt Macy
41eda14cbcSMatt Macy static MALLOC_DEFINE(M_KSTAT, "kstat_data", "Kernel statistics");
42eda14cbcSMatt Macy
43eda14cbcSMatt Macy SYSCTL_ROOT_NODE(OID_AUTO, kstat, CTLFLAG_RW, 0, "Kernel statistics");
44eda14cbcSMatt Macy
45eda14cbcSMatt Macy void
__kstat_set_raw_ops(kstat_t * ksp,int (* headers)(char * buf,size_t size),int (* data)(char * buf,size_t size,void * data),void * (* addr)(kstat_t * ksp,loff_t index))46eda14cbcSMatt Macy __kstat_set_raw_ops(kstat_t *ksp,
47eda14cbcSMatt Macy int (*headers)(char *buf, size_t size),
48eda14cbcSMatt Macy int (*data)(char *buf, size_t size, void *data),
49eda14cbcSMatt Macy void *(*addr)(kstat_t *ksp, loff_t index))
50eda14cbcSMatt Macy {
51eda14cbcSMatt Macy ksp->ks_raw_ops.headers = headers;
52eda14cbcSMatt Macy ksp->ks_raw_ops.data = data;
53eda14cbcSMatt Macy ksp->ks_raw_ops.addr = addr;
54eda14cbcSMatt Macy }
55eda14cbcSMatt Macy
56c40487d4SMatt Macy void
__kstat_set_seq_raw_ops(kstat_t * ksp,int (* headers)(struct seq_file * f),int (* data)(char * buf,size_t size,void * data),void * (* addr)(kstat_t * ksp,loff_t index))57c40487d4SMatt Macy __kstat_set_seq_raw_ops(kstat_t *ksp,
58c40487d4SMatt Macy int (*headers)(struct seq_file *f),
59c40487d4SMatt Macy int (*data)(char *buf, size_t size, void *data),
60c40487d4SMatt Macy void *(*addr)(kstat_t *ksp, loff_t index))
61c40487d4SMatt Macy {
62c40487d4SMatt Macy ksp->ks_raw_ops.seq_headers = headers;
63c40487d4SMatt Macy ksp->ks_raw_ops.data = data;
64c40487d4SMatt Macy ksp->ks_raw_ops.addr = addr;
65c40487d4SMatt Macy }
66c40487d4SMatt Macy
67eda14cbcSMatt Macy static int
kstat_default_update(kstat_t * ksp,int rw)68eda14cbcSMatt Macy kstat_default_update(kstat_t *ksp, int rw)
69eda14cbcSMatt Macy {
7016038816SMartin Matuska ASSERT3P(ksp, !=, NULL);
71eda14cbcSMatt Macy
72eda14cbcSMatt Macy if (rw == KSTAT_WRITE)
73eda14cbcSMatt Macy return (EACCES);
74eda14cbcSMatt Macy
75eda14cbcSMatt Macy return (0);
76eda14cbcSMatt Macy }
77eda14cbcSMatt Macy
78eac7052fSMatt Macy static int
kstat_resize_raw(kstat_t * ksp)79eac7052fSMatt Macy kstat_resize_raw(kstat_t *ksp)
80eac7052fSMatt Macy {
81eac7052fSMatt Macy if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX)
82eac7052fSMatt Macy return (ENOMEM);
83eac7052fSMatt Macy
84eac7052fSMatt Macy free(ksp->ks_raw_buf, M_TEMP);
85eac7052fSMatt Macy ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX);
86eac7052fSMatt Macy ksp->ks_raw_buf = malloc(ksp->ks_raw_bufsize, M_TEMP, M_WAITOK);
87eac7052fSMatt Macy
88eac7052fSMatt Macy return (0);
89eac7052fSMatt Macy }
90eac7052fSMatt Macy
91eac7052fSMatt Macy static void *
kstat_raw_default_addr(kstat_t * ksp,loff_t n)92eac7052fSMatt Macy kstat_raw_default_addr(kstat_t *ksp, loff_t n)
93eac7052fSMatt Macy {
94eac7052fSMatt Macy if (n == 0)
95eac7052fSMatt Macy return (ksp->ks_data);
96eac7052fSMatt Macy return (NULL);
97eac7052fSMatt Macy }
98eac7052fSMatt Macy
99eac7052fSMatt Macy static int
kstat_sysctl(SYSCTL_HANDLER_ARGS)100eac7052fSMatt Macy kstat_sysctl(SYSCTL_HANDLER_ARGS)
101eac7052fSMatt Macy {
102eac7052fSMatt Macy kstat_t *ksp = arg1;
103eac7052fSMatt Macy kstat_named_t *ksent;
104eac7052fSMatt Macy uint64_t val;
105eac7052fSMatt Macy
106eac7052fSMatt Macy ksent = ksp->ks_data;
107eac7052fSMatt Macy /* Select the correct element */
108eac7052fSMatt Macy ksent += arg2;
109eac7052fSMatt Macy /* Update the aggsums before reading */
110eac7052fSMatt Macy (void) ksp->ks_update(ksp, KSTAT_READ);
111eac7052fSMatt Macy val = ksent->value.ui64;
112eac7052fSMatt Macy
113eac7052fSMatt Macy return (sysctl_handle_64(oidp, &val, 0, req));
114eac7052fSMatt Macy }
115eac7052fSMatt Macy
116eac7052fSMatt Macy static int
kstat_sysctl_string(SYSCTL_HANDLER_ARGS)117eac7052fSMatt Macy kstat_sysctl_string(SYSCTL_HANDLER_ARGS)
118eac7052fSMatt Macy {
119eac7052fSMatt Macy kstat_t *ksp = arg1;
120eac7052fSMatt Macy kstat_named_t *ksent = ksp->ks_data;
121eac7052fSMatt Macy char *val;
122eac7052fSMatt Macy uint32_t len = 0;
123eac7052fSMatt Macy
124eac7052fSMatt Macy /* Select the correct element */
125eac7052fSMatt Macy ksent += arg2;
126eac7052fSMatt Macy /* Update the aggsums before reading */
127eac7052fSMatt Macy (void) ksp->ks_update(ksp, KSTAT_READ);
128eac7052fSMatt Macy val = KSTAT_NAMED_STR_PTR(ksent);
129eac7052fSMatt Macy len = KSTAT_NAMED_STR_BUFLEN(ksent);
130eac7052fSMatt Macy val[len-1] = '\0';
131eac7052fSMatt Macy
132eac7052fSMatt Macy return (sysctl_handle_string(oidp, val, len, req));
133eac7052fSMatt Macy }
134eac7052fSMatt Macy
135eac7052fSMatt Macy static int
kstat_sysctl_dataset(SYSCTL_HANDLER_ARGS)13615f0b8c3SMartin Matuska kstat_sysctl_dataset(SYSCTL_HANDLER_ARGS)
13715f0b8c3SMartin Matuska {
13815f0b8c3SMartin Matuska kstat_t *ksp = arg1;
13915f0b8c3SMartin Matuska kstat_named_t *ksent;
14015f0b8c3SMartin Matuska kstat_named_t *ksent_ds;
14115f0b8c3SMartin Matuska uint64_t val;
14215f0b8c3SMartin Matuska char *ds_name;
14315f0b8c3SMartin Matuska uint32_t ds_len = 0;
14415f0b8c3SMartin Matuska
14515f0b8c3SMartin Matuska ksent_ds = ksent = ksp->ks_data;
14615f0b8c3SMartin Matuska ds_name = KSTAT_NAMED_STR_PTR(ksent_ds);
14715f0b8c3SMartin Matuska ds_len = KSTAT_NAMED_STR_BUFLEN(ksent_ds);
14815f0b8c3SMartin Matuska ds_name[ds_len-1] = '\0';
14915f0b8c3SMartin Matuska
15015f0b8c3SMartin Matuska if (!zone_dataset_visible(ds_name, NULL)) {
15115f0b8c3SMartin Matuska return (EPERM);
15215f0b8c3SMartin Matuska }
15315f0b8c3SMartin Matuska
15415f0b8c3SMartin Matuska /* Select the correct element */
15515f0b8c3SMartin Matuska ksent += arg2;
15615f0b8c3SMartin Matuska /* Update the aggsums before reading */
15715f0b8c3SMartin Matuska (void) ksp->ks_update(ksp, KSTAT_READ);
15815f0b8c3SMartin Matuska val = ksent->value.ui64;
15915f0b8c3SMartin Matuska
16015f0b8c3SMartin Matuska return (sysctl_handle_64(oidp, &val, 0, req));
16115f0b8c3SMartin Matuska }
16215f0b8c3SMartin Matuska
16315f0b8c3SMartin Matuska static int
kstat_sysctl_dataset_string(SYSCTL_HANDLER_ARGS)16415f0b8c3SMartin Matuska kstat_sysctl_dataset_string(SYSCTL_HANDLER_ARGS)
16515f0b8c3SMartin Matuska {
16615f0b8c3SMartin Matuska kstat_t *ksp = arg1;
16715f0b8c3SMartin Matuska kstat_named_t *ksent = ksp->ks_data;
16815f0b8c3SMartin Matuska char *val;
16915f0b8c3SMartin Matuska uint32_t len = 0;
17015f0b8c3SMartin Matuska
17115f0b8c3SMartin Matuska /* Select the correct element */
17215f0b8c3SMartin Matuska ksent += arg2;
17315f0b8c3SMartin Matuska val = KSTAT_NAMED_STR_PTR(ksent);
17415f0b8c3SMartin Matuska len = KSTAT_NAMED_STR_BUFLEN(ksent);
17515f0b8c3SMartin Matuska val[len-1] = '\0';
17615f0b8c3SMartin Matuska
17715f0b8c3SMartin Matuska if (!zone_dataset_visible(val, NULL)) {
17815f0b8c3SMartin Matuska return (EPERM);
17915f0b8c3SMartin Matuska }
18015f0b8c3SMartin Matuska
18115f0b8c3SMartin Matuska return (sysctl_handle_string(oidp, val, len, req));
18215f0b8c3SMartin Matuska }
18315f0b8c3SMartin Matuska
18415f0b8c3SMartin Matuska static int
kstat_sysctl_io(SYSCTL_HANDLER_ARGS)185eac7052fSMatt Macy kstat_sysctl_io(SYSCTL_HANDLER_ARGS)
186eac7052fSMatt Macy {
187*14c2e0a0SMartin Matuska struct sbuf sb;
188eac7052fSMatt Macy kstat_t *ksp = arg1;
189eac7052fSMatt Macy kstat_io_t *kip = ksp->ks_data;
190eac7052fSMatt Macy int rc;
191eac7052fSMatt Macy
192*14c2e0a0SMartin Matuska sbuf_new_for_sysctl(&sb, NULL, 0, req);
193*14c2e0a0SMartin Matuska
194eac7052fSMatt Macy /* Update the aggsums before reading */
195eac7052fSMatt Macy (void) ksp->ks_update(ksp, KSTAT_READ);
196eac7052fSMatt Macy
197eac7052fSMatt Macy /* though wlentime & friends are signed, they will never be negative */
198*14c2e0a0SMartin Matuska sbuf_printf(&sb,
199eac7052fSMatt Macy "%-8llu %-8llu %-8u %-8u %-8llu %-8llu "
200eac7052fSMatt Macy "%-8llu %-8llu %-8llu %-8llu %-8u %-8u\n",
201eac7052fSMatt Macy kip->nread, kip->nwritten,
202eac7052fSMatt Macy kip->reads, kip->writes,
203eac7052fSMatt Macy kip->wtime, kip->wlentime, kip->wlastupdate,
204eac7052fSMatt Macy kip->rtime, kip->rlentime, kip->rlastupdate,
205eac7052fSMatt Macy kip->wcnt, kip->rcnt);
206*14c2e0a0SMartin Matuska rc = sbuf_finish(&sb);
207*14c2e0a0SMartin Matuska sbuf_delete(&sb);
208eac7052fSMatt Macy return (rc);
209eac7052fSMatt Macy }
210eac7052fSMatt Macy
211eac7052fSMatt Macy static int
kstat_sysctl_raw(SYSCTL_HANDLER_ARGS)212eac7052fSMatt Macy kstat_sysctl_raw(SYSCTL_HANDLER_ARGS)
213eac7052fSMatt Macy {
214*14c2e0a0SMartin Matuska struct sbuf sb;
215eac7052fSMatt Macy void *data;
216eac7052fSMatt Macy kstat_t *ksp = arg1;
217eac7052fSMatt Macy void *(*addr_op)(kstat_t *ksp, loff_t index);
218c40487d4SMatt Macy int n, has_header, rc = 0;
219eac7052fSMatt Macy
220*14c2e0a0SMartin Matuska sbuf_new_for_sysctl(&sb, NULL, PAGE_SIZE, req);
221eac7052fSMatt Macy
222eac7052fSMatt Macy if (ksp->ks_raw_ops.addr)
223eac7052fSMatt Macy addr_op = ksp->ks_raw_ops.addr;
224eac7052fSMatt Macy else
225eac7052fSMatt Macy addr_op = kstat_raw_default_addr;
226eac7052fSMatt Macy
227eac7052fSMatt Macy mutex_enter(ksp->ks_lock);
228eac7052fSMatt Macy
229eac7052fSMatt Macy /* Update the aggsums before reading */
230eac7052fSMatt Macy (void) ksp->ks_update(ksp, KSTAT_READ);
231eac7052fSMatt Macy
232eac7052fSMatt Macy ksp->ks_raw_bufsize = PAGE_SIZE;
233eac7052fSMatt Macy ksp->ks_raw_buf = malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
234eac7052fSMatt Macy
235eac7052fSMatt Macy n = 0;
236c40487d4SMatt Macy has_header = (ksp->ks_raw_ops.headers ||
237c40487d4SMatt Macy ksp->ks_raw_ops.seq_headers);
238c40487d4SMatt Macy
239eac7052fSMatt Macy restart_headers:
240eac7052fSMatt Macy if (ksp->ks_raw_ops.headers) {
241eac7052fSMatt Macy rc = ksp->ks_raw_ops.headers(
242eac7052fSMatt Macy ksp->ks_raw_buf, ksp->ks_raw_bufsize);
243c40487d4SMatt Macy } else if (ksp->ks_raw_ops.seq_headers) {
244c40487d4SMatt Macy struct seq_file f;
245c40487d4SMatt Macy
246c40487d4SMatt Macy f.sf_buf = ksp->ks_raw_buf;
247c40487d4SMatt Macy f.sf_size = ksp->ks_raw_bufsize;
248c40487d4SMatt Macy rc = ksp->ks_raw_ops.seq_headers(&f);
249c40487d4SMatt Macy }
250c40487d4SMatt Macy if (has_header) {
251eac7052fSMatt Macy if (rc == ENOMEM && !kstat_resize_raw(ksp))
252eac7052fSMatt Macy goto restart_headers;
253*14c2e0a0SMartin Matuska if (rc == 0) {
254*14c2e0a0SMartin Matuska sbuf_cat(&sb, "\n");
255*14c2e0a0SMartin Matuska sbuf_cat(&sb, ksp->ks_raw_buf);
256*14c2e0a0SMartin Matuska }
257eac7052fSMatt Macy }
258eac7052fSMatt Macy
259eac7052fSMatt Macy while ((data = addr_op(ksp, n)) != NULL) {
260eac7052fSMatt Macy restart:
261eac7052fSMatt Macy if (ksp->ks_raw_ops.data) {
262eac7052fSMatt Macy rc = ksp->ks_raw_ops.data(ksp->ks_raw_buf,
263eac7052fSMatt Macy ksp->ks_raw_bufsize, data);
264eac7052fSMatt Macy if (rc == ENOMEM && !kstat_resize_raw(ksp))
265eac7052fSMatt Macy goto restart;
266eac7052fSMatt Macy if (rc == 0)
267*14c2e0a0SMartin Matuska sbuf_cat(&sb, ksp->ks_raw_buf);
268eac7052fSMatt Macy
269eac7052fSMatt Macy } else {
27016038816SMartin Matuska ASSERT3U(ksp->ks_ndata, ==, 1);
271*14c2e0a0SMartin Matuska sbuf_hexdump(&sb, ksp->ks_data,
272eac7052fSMatt Macy ksp->ks_data_size, NULL, 0);
273eac7052fSMatt Macy }
274eac7052fSMatt Macy n++;
275eac7052fSMatt Macy }
276eac7052fSMatt Macy free(ksp->ks_raw_buf, M_TEMP);
277eac7052fSMatt Macy mutex_exit(ksp->ks_lock);
278*14c2e0a0SMartin Matuska rc = sbuf_finish(&sb);
279*14c2e0a0SMartin Matuska sbuf_delete(&sb);
280eac7052fSMatt Macy return (rc);
281eac7052fSMatt Macy }
282eac7052fSMatt Macy
283eda14cbcSMatt Macy kstat_t *
__kstat_create(const char * module,int instance,const char * name,const char * class,uchar_t ks_type,uint_t ks_ndata,uchar_t flags)284eda14cbcSMatt Macy __kstat_create(const char *module, int instance, const char *name,
285eda14cbcSMatt Macy const char *class, uchar_t ks_type, uint_t ks_ndata, uchar_t flags)
286eda14cbcSMatt Macy {
287c40487d4SMatt Macy char buf[KSTAT_STRLEN];
288eda14cbcSMatt Macy struct sysctl_oid *root;
289eda14cbcSMatt Macy kstat_t *ksp;
290c40487d4SMatt Macy char *pool;
291eda14cbcSMatt Macy
292eda14cbcSMatt Macy KASSERT(instance == 0, ("instance=%d", instance));
293eda14cbcSMatt Macy if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
29416038816SMartin Matuska ASSERT3U(ks_ndata, ==, 1);
295eda14cbcSMatt Macy
296c40487d4SMatt Macy if (class == NULL)
297c40487d4SMatt Macy class = "misc";
298c40487d4SMatt Macy
299eda14cbcSMatt Macy /*
300c40487d4SMatt Macy * Allocate the main structure. We don't need to keep a copy of
301c40487d4SMatt Macy * module in here, because it is only used for sysctl node creation
302eda14cbcSMatt Macy * done in this function.
303eda14cbcSMatt Macy */
304eda14cbcSMatt Macy ksp = malloc(sizeof (*ksp), M_KSTAT, M_WAITOK|M_ZERO);
305eda14cbcSMatt Macy
306eda14cbcSMatt Macy ksp->ks_crtime = gethrtime();
307eda14cbcSMatt Macy ksp->ks_snaptime = ksp->ks_crtime;
308eda14cbcSMatt Macy ksp->ks_instance = instance;
309c40487d4SMatt Macy (void) strlcpy(ksp->ks_name, name, KSTAT_STRLEN);
310c40487d4SMatt Macy (void) strlcpy(ksp->ks_class, class, KSTAT_STRLEN);
311eda14cbcSMatt Macy ksp->ks_type = ks_type;
312eda14cbcSMatt Macy ksp->ks_flags = flags;
313eda14cbcSMatt Macy ksp->ks_update = kstat_default_update;
314eda14cbcSMatt Macy
315eac7052fSMatt Macy mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL);
316eac7052fSMatt Macy ksp->ks_lock = &ksp->ks_private_lock;
317eac7052fSMatt Macy
318eda14cbcSMatt Macy switch (ksp->ks_type) {
319eda14cbcSMatt Macy case KSTAT_TYPE_RAW:
320eda14cbcSMatt Macy ksp->ks_ndata = 1;
321eda14cbcSMatt Macy ksp->ks_data_size = ks_ndata;
322eda14cbcSMatt Macy break;
323eda14cbcSMatt Macy case KSTAT_TYPE_NAMED:
324eda14cbcSMatt Macy ksp->ks_ndata = ks_ndata;
325eda14cbcSMatt Macy ksp->ks_data_size = ks_ndata * sizeof (kstat_named_t);
326eda14cbcSMatt Macy break;
327eda14cbcSMatt Macy case KSTAT_TYPE_INTR:
328eda14cbcSMatt Macy ksp->ks_ndata = ks_ndata;
329eda14cbcSMatt Macy ksp->ks_data_size = ks_ndata * sizeof (kstat_intr_t);
330eda14cbcSMatt Macy break;
331eda14cbcSMatt Macy case KSTAT_TYPE_IO:
332eda14cbcSMatt Macy ksp->ks_ndata = ks_ndata;
333eda14cbcSMatt Macy ksp->ks_data_size = ks_ndata * sizeof (kstat_io_t);
334eda14cbcSMatt Macy break;
335eda14cbcSMatt Macy case KSTAT_TYPE_TIMER:
336eda14cbcSMatt Macy ksp->ks_ndata = ks_ndata;
337eda14cbcSMatt Macy ksp->ks_data_size = ks_ndata * sizeof (kstat_timer_t);
338eda14cbcSMatt Macy break;
339eda14cbcSMatt Macy default:
340eda14cbcSMatt Macy panic("Undefined kstat type %d\n", ksp->ks_type);
341eda14cbcSMatt Macy }
342eda14cbcSMatt Macy
343f9693befSMartin Matuska if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL)
344eda14cbcSMatt Macy ksp->ks_data = NULL;
345f9693befSMartin Matuska else
346eda14cbcSMatt Macy ksp->ks_data = kmem_zalloc(ksp->ks_data_size, KM_SLEEP);
347c40487d4SMatt Macy
348c40487d4SMatt Macy /*
349c40487d4SMatt Macy * Some kstats use a module name like "zfs/poolname" to distinguish a
350c40487d4SMatt Macy * set of kstats belonging to a specific pool. Split on '/' to add an
351c40487d4SMatt Macy * extra node for the pool name if needed.
352c40487d4SMatt Macy */
353c40487d4SMatt Macy (void) strlcpy(buf, module, KSTAT_STRLEN);
354c40487d4SMatt Macy module = buf;
355c40487d4SMatt Macy pool = strchr(module, '/');
356c40487d4SMatt Macy if (pool != NULL)
357c40487d4SMatt Macy *pool++ = '\0';
358c40487d4SMatt Macy
359eda14cbcSMatt Macy /*
360eda14cbcSMatt Macy * Create sysctl tree for those statistics:
361eda14cbcSMatt Macy *
362c40487d4SMatt Macy * kstat.<module>[.<pool>].<class>.<name>
363eda14cbcSMatt Macy */
364eda14cbcSMatt Macy sysctl_ctx_init(&ksp->ks_sysctl_ctx);
365eda14cbcSMatt Macy root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx,
366eda14cbcSMatt Macy SYSCTL_STATIC_CHILDREN(_kstat), OID_AUTO, module, CTLFLAG_RW, 0,
367eda14cbcSMatt Macy "");
368eda14cbcSMatt Macy if (root == NULL) {
369eda14cbcSMatt Macy printf("%s: Cannot create kstat.%s tree!\n", __func__, module);
370eda14cbcSMatt Macy sysctl_ctx_free(&ksp->ks_sysctl_ctx);
371eda14cbcSMatt Macy free(ksp, M_KSTAT);
372eda14cbcSMatt Macy return (NULL);
373eda14cbcSMatt Macy }
374c40487d4SMatt Macy if (pool != NULL) {
375c40487d4SMatt Macy root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx,
376c40487d4SMatt Macy SYSCTL_CHILDREN(root), OID_AUTO, pool, CTLFLAG_RW, 0, "");
377c40487d4SMatt Macy if (root == NULL) {
378c40487d4SMatt Macy printf("%s: Cannot create kstat.%s.%s tree!\n",
379c40487d4SMatt Macy __func__, module, pool);
380c40487d4SMatt Macy sysctl_ctx_free(&ksp->ks_sysctl_ctx);
381c40487d4SMatt Macy free(ksp, M_KSTAT);
382c40487d4SMatt Macy return (NULL);
383c40487d4SMatt Macy }
384c40487d4SMatt Macy }
385eda14cbcSMatt Macy root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root),
386eda14cbcSMatt Macy OID_AUTO, class, CTLFLAG_RW, 0, "");
387eda14cbcSMatt Macy if (root == NULL) {
388c40487d4SMatt Macy if (pool != NULL)
389c40487d4SMatt Macy printf("%s: Cannot create kstat.%s.%s.%s tree!\n",
390c40487d4SMatt Macy __func__, module, pool, class);
391c40487d4SMatt Macy else
392c40487d4SMatt Macy printf("%s: Cannot create kstat.%s.%s tree!\n",
393c40487d4SMatt Macy __func__, module, class);
394eda14cbcSMatt Macy sysctl_ctx_free(&ksp->ks_sysctl_ctx);
395eda14cbcSMatt Macy free(ksp, M_KSTAT);
396eda14cbcSMatt Macy return (NULL);
397eda14cbcSMatt Macy }
398eac7052fSMatt Macy if (ksp->ks_type == KSTAT_TYPE_NAMED) {
399eac7052fSMatt Macy root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx,
400eac7052fSMatt Macy SYSCTL_CHILDREN(root),
401eda14cbcSMatt Macy OID_AUTO, name, CTLFLAG_RW, 0, "");
402eda14cbcSMatt Macy if (root == NULL) {
403c40487d4SMatt Macy if (pool != NULL)
404c40487d4SMatt Macy printf("%s: Cannot create kstat.%s.%s.%s.%s "
405c40487d4SMatt Macy "tree!\n", __func__, module, pool, class,
406c40487d4SMatt Macy name);
407c40487d4SMatt Macy else
408c40487d4SMatt Macy printf("%s: Cannot create kstat.%s.%s.%s "
409c40487d4SMatt Macy "tree!\n", __func__, module, class, name);
410eda14cbcSMatt Macy sysctl_ctx_free(&ksp->ks_sysctl_ctx);
411eda14cbcSMatt Macy free(ksp, M_KSTAT);
412eda14cbcSMatt Macy return (NULL);
413eda14cbcSMatt Macy }
414eac7052fSMatt Macy
415eac7052fSMatt Macy }
416eda14cbcSMatt Macy ksp->ks_sysctl_root = root;
417eda14cbcSMatt Macy
418eda14cbcSMatt Macy return (ksp);
419eda14cbcSMatt Macy }
420eda14cbcSMatt Macy
421eac7052fSMatt Macy static void
kstat_install_named(kstat_t * ksp)422eac7052fSMatt Macy kstat_install_named(kstat_t *ksp)
423eda14cbcSMatt Macy {
424eda14cbcSMatt Macy kstat_named_t *ksent;
425eda14cbcSMatt Macy char *namelast;
426eda14cbcSMatt Macy int typelast;
427eda14cbcSMatt Macy
428eda14cbcSMatt Macy ksent = ksp->ks_data;
429eac7052fSMatt Macy
430eac7052fSMatt Macy VERIFY((ksp->ks_flags & KSTAT_FLAG_VIRTUAL) || ksent != NULL);
431eac7052fSMatt Macy
432eda14cbcSMatt Macy typelast = 0;
433eda14cbcSMatt Macy namelast = NULL;
434eac7052fSMatt Macy
435eda14cbcSMatt Macy for (int i = 0; i < ksp->ks_ndata; i++, ksent++) {
436eda14cbcSMatt Macy if (ksent->data_type != 0) {
437eda14cbcSMatt Macy typelast = ksent->data_type;
438eda14cbcSMatt Macy namelast = ksent->name;
439eda14cbcSMatt Macy }
440eda14cbcSMatt Macy switch (typelast) {
441eda14cbcSMatt Macy case KSTAT_DATA_CHAR:
442eda14cbcSMatt Macy /* Not Implemented */
443eda14cbcSMatt Macy break;
444eda14cbcSMatt Macy case KSTAT_DATA_INT32:
445eda14cbcSMatt Macy SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
446eda14cbcSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
447eda14cbcSMatt Macy OID_AUTO, namelast,
448c40487d4SMatt Macy CTLTYPE_S32 | CTLFLAG_RD | CTLFLAG_MPSAFE,
449c40487d4SMatt Macy ksp, i, kstat_sysctl, "I", namelast);
450eda14cbcSMatt Macy break;
451eda14cbcSMatt Macy case KSTAT_DATA_UINT32:
452eda14cbcSMatt Macy SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
453eda14cbcSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
454eda14cbcSMatt Macy OID_AUTO, namelast,
455c40487d4SMatt Macy CTLTYPE_U32 | CTLFLAG_RD | CTLFLAG_MPSAFE,
456c40487d4SMatt Macy ksp, i, kstat_sysctl, "IU", namelast);
457eda14cbcSMatt Macy break;
458eda14cbcSMatt Macy case KSTAT_DATA_INT64:
459eda14cbcSMatt Macy SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
460eda14cbcSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
461eda14cbcSMatt Macy OID_AUTO, namelast,
462c40487d4SMatt Macy CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
463c40487d4SMatt Macy ksp, i, kstat_sysctl, "Q", namelast);
464eda14cbcSMatt Macy break;
465eda14cbcSMatt Macy case KSTAT_DATA_UINT64:
46615f0b8c3SMartin Matuska if (strcmp(ksp->ks_class, "dataset") == 0) {
46715f0b8c3SMartin Matuska SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
46815f0b8c3SMartin Matuska SYSCTL_CHILDREN(ksp->ks_sysctl_root),
46915f0b8c3SMartin Matuska OID_AUTO, namelast,
47015f0b8c3SMartin Matuska CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
47115f0b8c3SMartin Matuska ksp, i, kstat_sysctl_dataset, "QU",
47215f0b8c3SMartin Matuska namelast);
47315f0b8c3SMartin Matuska } else {
474eda14cbcSMatt Macy SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
475eda14cbcSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
476eda14cbcSMatt Macy OID_AUTO, namelast,
477c40487d4SMatt Macy CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
478c40487d4SMatt Macy ksp, i, kstat_sysctl, "QU", namelast);
47915f0b8c3SMartin Matuska }
480eda14cbcSMatt Macy break;
481eda14cbcSMatt Macy case KSTAT_DATA_LONG:
482eda14cbcSMatt Macy SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
483eda14cbcSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
484eda14cbcSMatt Macy OID_AUTO, namelast,
485c40487d4SMatt Macy CTLTYPE_LONG | CTLFLAG_RD | CTLFLAG_MPSAFE,
486c40487d4SMatt Macy ksp, i, kstat_sysctl, "L", namelast);
487eda14cbcSMatt Macy break;
488eda14cbcSMatt Macy case KSTAT_DATA_ULONG:
489eda14cbcSMatt Macy SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
490eda14cbcSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
491eda14cbcSMatt Macy OID_AUTO, namelast,
492c40487d4SMatt Macy CTLTYPE_ULONG | CTLFLAG_RD | CTLFLAG_MPSAFE,
493c40487d4SMatt Macy ksp, i, kstat_sysctl, "LU", namelast);
494eda14cbcSMatt Macy break;
495eda14cbcSMatt Macy case KSTAT_DATA_STRING:
49615f0b8c3SMartin Matuska if (strcmp(ksp->ks_class, "dataset") == 0) {
497eda14cbcSMatt Macy SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
498eda14cbcSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
49915f0b8c3SMartin Matuska OID_AUTO, namelast, CTLTYPE_STRING |
50015f0b8c3SMartin Matuska CTLFLAG_RD | CTLFLAG_MPSAFE,
50115f0b8c3SMartin Matuska ksp, i, kstat_sysctl_dataset_string, "A",
50215f0b8c3SMartin Matuska namelast);
50315f0b8c3SMartin Matuska } else {
50415f0b8c3SMartin Matuska SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
50515f0b8c3SMartin Matuska SYSCTL_CHILDREN(ksp->ks_sysctl_root),
50615f0b8c3SMartin Matuska OID_AUTO, namelast, CTLTYPE_STRING |
50715f0b8c3SMartin Matuska CTLFLAG_RD | CTLFLAG_MPSAFE,
50815f0b8c3SMartin Matuska ksp, i, kstat_sysctl_string, "A",
50915f0b8c3SMartin Matuska namelast);
51015f0b8c3SMartin Matuska }
511eda14cbcSMatt Macy break;
512eda14cbcSMatt Macy default:
513eda14cbcSMatt Macy panic("unsupported type: %d", typelast);
514eda14cbcSMatt Macy }
515eda14cbcSMatt Macy }
516eac7052fSMatt Macy }
517eac7052fSMatt Macy
518eac7052fSMatt Macy void
kstat_install(kstat_t * ksp)519eac7052fSMatt Macy kstat_install(kstat_t *ksp)
520eac7052fSMatt Macy {
521eac7052fSMatt Macy struct sysctl_oid *root;
522eac7052fSMatt Macy
523eac7052fSMatt Macy if (ksp->ks_ndata == UINT32_MAX)
52416038816SMartin Matuska VERIFY3U(ksp->ks_type, ==, KSTAT_TYPE_RAW);
525eac7052fSMatt Macy
526eac7052fSMatt Macy switch (ksp->ks_type) {
527eac7052fSMatt Macy case KSTAT_TYPE_NAMED:
528eac7052fSMatt Macy return (kstat_install_named(ksp));
529eac7052fSMatt Macy case KSTAT_TYPE_RAW:
530eac7052fSMatt Macy if (ksp->ks_raw_ops.data) {
531eac7052fSMatt Macy root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
532eac7052fSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
533b19cdab3SRyan Moeller OID_AUTO, ksp->ks_name, CTLTYPE_STRING | CTLFLAG_RD
534b19cdab3SRyan Moeller | CTLFLAG_MPSAFE | CTLFLAG_SKIP,
535c40487d4SMatt Macy ksp, 0, kstat_sysctl_raw, "A", ksp->ks_name);
536eac7052fSMatt Macy } else {
537eac7052fSMatt Macy root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
538eac7052fSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
539b19cdab3SRyan Moeller OID_AUTO, ksp->ks_name, CTLTYPE_OPAQUE | CTLFLAG_RD
540b19cdab3SRyan Moeller | CTLFLAG_MPSAFE | CTLFLAG_SKIP,
541c40487d4SMatt Macy ksp, 0, kstat_sysctl_raw, "", ksp->ks_name);
542eac7052fSMatt Macy }
543eac7052fSMatt Macy break;
544eac7052fSMatt Macy case KSTAT_TYPE_IO:
545eac7052fSMatt Macy root = SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
546eac7052fSMatt Macy SYSCTL_CHILDREN(ksp->ks_sysctl_root),
547eac7052fSMatt Macy OID_AUTO, ksp->ks_name,
548c40487d4SMatt Macy CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
549c40487d4SMatt Macy ksp, 0, kstat_sysctl_io, "A", ksp->ks_name);
550eac7052fSMatt Macy break;
551eac7052fSMatt Macy case KSTAT_TYPE_TIMER:
552eac7052fSMatt Macy case KSTAT_TYPE_INTR:
553eac7052fSMatt Macy default:
554eac7052fSMatt Macy panic("unsupported kstat type %d\n", ksp->ks_type);
555eac7052fSMatt Macy }
55616038816SMartin Matuska VERIFY3P(root, !=, NULL);
557eac7052fSMatt Macy ksp->ks_sysctl_root = root;
558eda14cbcSMatt Macy }
559eda14cbcSMatt Macy
560eda14cbcSMatt Macy void
kstat_delete(kstat_t * ksp)561eda14cbcSMatt Macy kstat_delete(kstat_t *ksp)
562eda14cbcSMatt Macy {
563eda14cbcSMatt Macy
564eda14cbcSMatt Macy sysctl_ctx_free(&ksp->ks_sysctl_ctx);
565eac7052fSMatt Macy ksp->ks_lock = NULL;
566eac7052fSMatt Macy mutex_destroy(&ksp->ks_private_lock);
567f9693befSMartin Matuska if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
568f9693befSMartin Matuska kmem_free(ksp->ks_data, ksp->ks_data_size);
569eda14cbcSMatt Macy free(ksp, M_KSTAT);
570eda14cbcSMatt Macy }
571