xref: /openbsd-src/sys/dev/kstat.c (revision e6b4f1cd126007d772122f03912de586e4a57655)
1*e6b4f1cdSmglocker /* $OpenBSD: kstat.c,v 1.5 2025/01/18 12:31:49 mglocker Exp $ */
2402315e8Sdlg 
3402315e8Sdlg /*
4402315e8Sdlg  * Copyright (c) 2020 David Gwynne <dlg@openbsd.org>
5402315e8Sdlg  *
6402315e8Sdlg  * Permission to use, copy, modify, and distribute this software for any
7402315e8Sdlg  * purpose with or without fee is hereby granted, provided that the above
8402315e8Sdlg  * copyright notice and this permission notice appear in all copies.
9402315e8Sdlg  *
10402315e8Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11402315e8Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12402315e8Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13402315e8Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14402315e8Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15402315e8Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16402315e8Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17402315e8Sdlg  */
18402315e8Sdlg 
19402315e8Sdlg #include <sys/param.h>
20402315e8Sdlg #include <sys/systm.h>
21402315e8Sdlg #include <sys/types.h>
22402315e8Sdlg #include <sys/malloc.h>
23402315e8Sdlg #include <sys/pool.h>
24402315e8Sdlg #include <sys/time.h>
25402315e8Sdlg 
26402315e8Sdlg /* for kstat_set_cpu */
27402315e8Sdlg #include <sys/proc.h>
28402315e8Sdlg #include <sys/sched.h>
29402315e8Sdlg 
30402315e8Sdlg #include <sys/kstat.h>
31402315e8Sdlg 
32402315e8Sdlg RBT_HEAD(kstat_id_tree, kstat);
33402315e8Sdlg 
34402315e8Sdlg static inline int
35402315e8Sdlg kstat_id_cmp(const struct kstat *a, const struct kstat *b)
36402315e8Sdlg {
37402315e8Sdlg 	if (a->ks_id > b->ks_id)
38402315e8Sdlg 		return (1);
39402315e8Sdlg 	if (a->ks_id < b->ks_id)
40402315e8Sdlg 		return (-1);
41402315e8Sdlg 
42402315e8Sdlg 	return (0);
43402315e8Sdlg }
44402315e8Sdlg 
45402315e8Sdlg RBT_PROTOTYPE(kstat_id_tree, kstat, ks_id_entry, kstat_id_cmp);
46402315e8Sdlg 
47402315e8Sdlg RBT_HEAD(kstat_pv_tree, kstat);
48402315e8Sdlg 
49402315e8Sdlg static inline int
50402315e8Sdlg kstat_pv_cmp(const struct kstat *a, const struct kstat *b)
51402315e8Sdlg {
52402315e8Sdlg 	int rv;
53402315e8Sdlg 
54402315e8Sdlg 	rv = strcmp(a->ks_provider, b->ks_provider);
55402315e8Sdlg 	if (rv != 0)
56402315e8Sdlg 		return (rv);
57402315e8Sdlg 
58402315e8Sdlg 	if (a->ks_instance > b->ks_instance)
59402315e8Sdlg 		return (1);
60402315e8Sdlg 	if (a->ks_instance < b->ks_instance)
61402315e8Sdlg 		return (-1);
62402315e8Sdlg 
63402315e8Sdlg 	rv = strcmp(a->ks_name, b->ks_name);
64402315e8Sdlg 	if (rv != 0)
65402315e8Sdlg 		return (rv);
66402315e8Sdlg 
67402315e8Sdlg 	if (a->ks_unit > b->ks_unit)
68402315e8Sdlg 		return (1);
69402315e8Sdlg 	if (a->ks_unit < b->ks_unit)
70402315e8Sdlg 		return (-1);
71402315e8Sdlg 
72402315e8Sdlg 	return (0);
73402315e8Sdlg }
74402315e8Sdlg 
75402315e8Sdlg RBT_PROTOTYPE(kstat_pv_tree, kstat, ks_pv_entry, kstat_pv_cmp);
76402315e8Sdlg 
77402315e8Sdlg RBT_HEAD(kstat_nm_tree, kstat);
78402315e8Sdlg 
79402315e8Sdlg static inline int
80402315e8Sdlg kstat_nm_cmp(const struct kstat *a, const struct kstat *b)
81402315e8Sdlg {
82402315e8Sdlg 	int rv;
83402315e8Sdlg 
84402315e8Sdlg 	rv = strcmp(a->ks_name, b->ks_name);
85402315e8Sdlg 	if (rv != 0)
86402315e8Sdlg 		return (rv);
87402315e8Sdlg 
88402315e8Sdlg 	if (a->ks_unit > b->ks_unit)
89402315e8Sdlg 		return (1);
90402315e8Sdlg 	if (a->ks_unit < b->ks_unit)
91402315e8Sdlg 		return (-1);
92402315e8Sdlg 
93402315e8Sdlg 	rv = strcmp(a->ks_provider, b->ks_provider);
94402315e8Sdlg 	if (rv != 0)
95402315e8Sdlg 		return (rv);
96402315e8Sdlg 
97402315e8Sdlg 	if (a->ks_instance > b->ks_instance)
98402315e8Sdlg 		return (1);
99402315e8Sdlg 	if (a->ks_instance < b->ks_instance)
100402315e8Sdlg 		return (-1);
101402315e8Sdlg 
102402315e8Sdlg 	return (0);
103402315e8Sdlg }
104402315e8Sdlg 
105402315e8Sdlg RBT_PROTOTYPE(kstat_nm_tree, kstat, ks_nm_entry, kstat_nm_cmp);
106402315e8Sdlg 
107402315e8Sdlg struct kstat_lock_ops {
108402315e8Sdlg 	void	(*enter)(void *);
109402315e8Sdlg 	void	(*leave)(void *);
110402315e8Sdlg };
111402315e8Sdlg 
112402315e8Sdlg #define kstat_enter(_ks) (_ks)->ks_lock_ops->enter((_ks)->ks_lock)
113402315e8Sdlg #define kstat_leave(_ks) (_ks)->ks_lock_ops->leave((_ks)->ks_lock)
114402315e8Sdlg 
115402315e8Sdlg const struct kstat_lock_ops kstat_rlock_ops = {
116402315e8Sdlg 	(void (*)(void *))rw_enter_read,
117402315e8Sdlg 	(void (*)(void *))rw_exit_read,
118402315e8Sdlg };
119402315e8Sdlg 
120402315e8Sdlg const struct kstat_lock_ops kstat_wlock_ops = {
121402315e8Sdlg 	(void (*)(void *))rw_enter_write,
122402315e8Sdlg 	(void (*)(void *))rw_exit_write,
123402315e8Sdlg };
124402315e8Sdlg 
125402315e8Sdlg const struct kstat_lock_ops kstat_mutex_ops = {
126402315e8Sdlg 	(void (*)(void *))mtx_enter,
127402315e8Sdlg 	(void (*)(void *))mtx_leave,
128402315e8Sdlg };
129402315e8Sdlg 
130402315e8Sdlg void kstat_cpu_enter(void *);
131402315e8Sdlg void kstat_cpu_leave(void *);
132402315e8Sdlg 
133402315e8Sdlg const struct kstat_lock_ops kstat_cpu_ops = {
134402315e8Sdlg 	kstat_cpu_enter,
135402315e8Sdlg 	kstat_cpu_leave,
136402315e8Sdlg };
137402315e8Sdlg 
138402315e8Sdlg struct rwlock		kstat_lock = RWLOCK_INITIALIZER("kstat");
139402315e8Sdlg 
140402315e8Sdlg /*
141402315e8Sdlg  * The global state is versioned so changes to the set of kstats
142402315e8Sdlg  * can be detected. This is an int so it can be read atomically on
143402315e8Sdlg  * any arch, which is a ridiculous optimisation, really.
144402315e8Sdlg  */
145402315e8Sdlg unsigned int		kstat_version = 0;
146402315e8Sdlg 
147402315e8Sdlg /*
148402315e8Sdlg  * kstat structures have a unique identifier so they can be found
149402315e8Sdlg  * quickly. Identifiers are 64bit in the hope that it won't wrap
150402315e8Sdlg  * during the runtime of a system. The identifiers start at 1 so that
151402315e8Sdlg  * 0 can be used as the first value for userland to iterate with.
152402315e8Sdlg  */
153402315e8Sdlg uint64_t			kstat_next_id = 1;
154402315e8Sdlg 
155402315e8Sdlg struct kstat_id_tree	kstat_id_tree = RBT_INITIALIZER();
156402315e8Sdlg struct kstat_pv_tree	kstat_pv_tree = RBT_INITIALIZER();
157402315e8Sdlg struct kstat_nm_tree	kstat_nm_tree = RBT_INITIALIZER();
158402315e8Sdlg struct pool		kstat_pool;
159402315e8Sdlg 
160402315e8Sdlg struct rwlock		kstat_default_lock = RWLOCK_INITIALIZER("kstatlk");
161402315e8Sdlg 
162402315e8Sdlg int	kstat_read(struct kstat *);
163402315e8Sdlg int	kstat_copy(struct kstat *, void *);
164402315e8Sdlg 
165402315e8Sdlg int
166402315e8Sdlg kstatattach(int num)
167402315e8Sdlg {
168402315e8Sdlg 	/* XXX install system stats here */
169402315e8Sdlg 	return (0);
170402315e8Sdlg }
171402315e8Sdlg 
172402315e8Sdlg int
173402315e8Sdlg kstatopen(dev_t dev, int flag, int mode, struct proc *p)
174402315e8Sdlg {
175402315e8Sdlg 	return (0);
176402315e8Sdlg }
177402315e8Sdlg 
178402315e8Sdlg int
179402315e8Sdlg kstatclose(dev_t dev, int flag, int mode, struct proc *p)
180402315e8Sdlg {
181402315e8Sdlg 	return (0);
182402315e8Sdlg }
183402315e8Sdlg 
184402315e8Sdlg int
185402315e8Sdlg kstatioc_enter(struct kstat_req *ksreq)
186402315e8Sdlg {
187402315e8Sdlg 	int error;
188402315e8Sdlg 
189402315e8Sdlg 	error = rw_enter(&kstat_lock, RW_READ | RW_INTR);
190402315e8Sdlg 	if (error != 0)
191402315e8Sdlg 		return (error);
192402315e8Sdlg 
193402315e8Sdlg 	if (!ISSET(ksreq->ks_rflags, KSTATIOC_F_IGNVER) &&
194402315e8Sdlg 	    ksreq->ks_version != kstat_version) {
195402315e8Sdlg 		error = EINVAL;
196402315e8Sdlg 		goto error;
197402315e8Sdlg 	}
198402315e8Sdlg 
199402315e8Sdlg 	return (0);
200402315e8Sdlg 
201402315e8Sdlg error:
202402315e8Sdlg 	rw_exit(&kstat_lock);
203402315e8Sdlg 	return (error);
204402315e8Sdlg }
205402315e8Sdlg 
206402315e8Sdlg int
207402315e8Sdlg kstatioc_leave(struct kstat_req *ksreq, struct kstat *ks)
208402315e8Sdlg {
209402315e8Sdlg 	void *buf = NULL;
210402315e8Sdlg 	size_t klen = 0, ulen = 0;
211402315e8Sdlg 	struct timespec updated;
212402315e8Sdlg 	int error = 0;
213402315e8Sdlg 
214402315e8Sdlg 	if (ks == NULL) {
215402315e8Sdlg 		error = ENOENT;
216402315e8Sdlg 		goto error;
217402315e8Sdlg 	}
218402315e8Sdlg 
219402315e8Sdlg 	switch (ks->ks_state) {
220402315e8Sdlg 	case KSTAT_S_CREATED:
221402315e8Sdlg 		ksreq->ks_updated = ks->ks_created;
222402315e8Sdlg 		ksreq->ks_interval.tv_sec = 0;
223402315e8Sdlg 		ksreq->ks_interval.tv_nsec = 0;
224402315e8Sdlg 		ksreq->ks_datalen = 0;
225402315e8Sdlg 		ksreq->ks_dataver = 0;
226402315e8Sdlg 		break;
227402315e8Sdlg 
228402315e8Sdlg 	case KSTAT_S_INSTALLED:
229402315e8Sdlg 		ksreq->ks_dataver = ks->ks_dataver;
230402315e8Sdlg 		ksreq->ks_interval = ks->ks_interval;
231402315e8Sdlg 
232402315e8Sdlg 		if (ksreq->ks_data == NULL) {
233402315e8Sdlg 			/* userland doesn't want actual data, so shortcut */
234402315e8Sdlg 			kstat_enter(ks);
235402315e8Sdlg 			ksreq->ks_datalen = ks->ks_datalen;
236402315e8Sdlg 			ksreq->ks_updated = ks->ks_updated;
237402315e8Sdlg 			kstat_leave(ks);
238402315e8Sdlg 			break;
239402315e8Sdlg 		}
240402315e8Sdlg 
241402315e8Sdlg 		klen = ks->ks_datalen; /* KSTAT_F_REALLOC */
242402315e8Sdlg 		buf = malloc(klen, M_TEMP, M_WAITOK|M_CANFAIL);
243402315e8Sdlg 		if (buf == NULL) {
244402315e8Sdlg 			error = ENOMEM;
245402315e8Sdlg 			goto error;
246402315e8Sdlg 		}
247402315e8Sdlg 
248402315e8Sdlg 		kstat_enter(ks);
249402315e8Sdlg 		error = (*ks->ks_read)(ks);
250402315e8Sdlg 		if (error == 0) {
251402315e8Sdlg 			updated = ks->ks_updated;
252402315e8Sdlg 
253402315e8Sdlg 			/* KSTAT_F_REALLOC */
254402315e8Sdlg 			KASSERTMSG(ks->ks_datalen == klen,
2559593dc34Smglocker 			    "kstat doesn't support resized data yet");
256402315e8Sdlg 
257402315e8Sdlg 			error = (*ks->ks_copy)(ks, buf);
258402315e8Sdlg 		}
259402315e8Sdlg 		kstat_leave(ks);
260402315e8Sdlg 
261402315e8Sdlg 		if (error != 0)
262402315e8Sdlg 			goto error;
263402315e8Sdlg 
264402315e8Sdlg 		ulen = ksreq->ks_datalen;
265402315e8Sdlg 		ksreq->ks_datalen = klen; /* KSTAT_F_REALLOC */
266402315e8Sdlg 		ksreq->ks_updated = updated;
267402315e8Sdlg 		break;
268402315e8Sdlg 	default:
269402315e8Sdlg 		panic("ks %p unexpected state %u", ks, ks->ks_state);
270402315e8Sdlg 	}
271402315e8Sdlg 
272402315e8Sdlg 	ksreq->ks_version = kstat_version;
273402315e8Sdlg 	ksreq->ks_id = ks->ks_id;
274402315e8Sdlg 
275402315e8Sdlg 	if (strlcpy(ksreq->ks_provider, ks->ks_provider,
276402315e8Sdlg 	    sizeof(ksreq->ks_provider)) >= sizeof(ksreq->ks_provider))
277402315e8Sdlg 		panic("kstat %p provider string has grown", ks);
278402315e8Sdlg 	ksreq->ks_instance = ks->ks_instance;
279402315e8Sdlg 	if (strlcpy(ksreq->ks_name, ks->ks_name,
280402315e8Sdlg 	    sizeof(ksreq->ks_name)) >= sizeof(ksreq->ks_name))
281402315e8Sdlg 		panic("kstat %p name string has grown", ks);
282402315e8Sdlg 	ksreq->ks_unit = ks->ks_unit;
283402315e8Sdlg 
284402315e8Sdlg 	ksreq->ks_created = ks->ks_created;
285402315e8Sdlg 	ksreq->ks_type = ks->ks_type;
286402315e8Sdlg 	ksreq->ks_state = ks->ks_state;
287402315e8Sdlg 
288402315e8Sdlg error:
289402315e8Sdlg 	rw_exit(&kstat_lock);
290402315e8Sdlg 
291402315e8Sdlg 	if (buf != NULL) {
292402315e8Sdlg 		if (error == 0)
293402315e8Sdlg 			error = copyout(buf, ksreq->ks_data, min(klen, ulen));
294402315e8Sdlg 
295402315e8Sdlg 		free(buf, M_TEMP, klen);
296402315e8Sdlg 	}
297402315e8Sdlg 
298402315e8Sdlg 	return (error);
299402315e8Sdlg }
300402315e8Sdlg 
301402315e8Sdlg int
302402315e8Sdlg kstatioc_find_id(struct kstat_req *ksreq)
303402315e8Sdlg {
304402315e8Sdlg 	struct kstat *ks, key;
305402315e8Sdlg 	int error;
306402315e8Sdlg 
307402315e8Sdlg 	error = kstatioc_enter(ksreq);
308402315e8Sdlg 	if (error != 0)
309402315e8Sdlg 		return (error);
310402315e8Sdlg 
311402315e8Sdlg 	key.ks_id = ksreq->ks_id;
312402315e8Sdlg 
313402315e8Sdlg 	ks = RBT_FIND(kstat_id_tree, &kstat_id_tree, &key);
314402315e8Sdlg 
315402315e8Sdlg 	return (kstatioc_leave(ksreq, ks));
316402315e8Sdlg }
317402315e8Sdlg 
318402315e8Sdlg int
319402315e8Sdlg kstatioc_nfind_id(struct kstat_req *ksreq)
320402315e8Sdlg {
321402315e8Sdlg 	struct kstat *ks, key;
322402315e8Sdlg 	int error;
323402315e8Sdlg 
324402315e8Sdlg 	error = kstatioc_enter(ksreq);
325402315e8Sdlg 	if (error != 0)
326402315e8Sdlg 		return (error);
327402315e8Sdlg 
328402315e8Sdlg 	key.ks_id = ksreq->ks_id;
329402315e8Sdlg 
330402315e8Sdlg 	ks = RBT_NFIND(kstat_id_tree, &kstat_id_tree, &key);
331402315e8Sdlg 
332402315e8Sdlg 	return (kstatioc_leave(ksreq, ks));
333402315e8Sdlg }
334402315e8Sdlg 
335402315e8Sdlg int
336402315e8Sdlg kstatioc_find_pv(struct kstat_req *ksreq)
337402315e8Sdlg {
338402315e8Sdlg 	struct kstat *ks, key;
339402315e8Sdlg 	int error;
340402315e8Sdlg 
341402315e8Sdlg 	error = kstatioc_enter(ksreq);
342402315e8Sdlg 	if (error != 0)
343402315e8Sdlg 		return (error);
344402315e8Sdlg 
345402315e8Sdlg 	key.ks_provider = ksreq->ks_provider;
346402315e8Sdlg 	key.ks_instance = ksreq->ks_instance;
347402315e8Sdlg 	key.ks_name = ksreq->ks_name;
348402315e8Sdlg 	key.ks_unit = ksreq->ks_unit;
349402315e8Sdlg 
350402315e8Sdlg 	ks = RBT_FIND(kstat_pv_tree, &kstat_pv_tree, &key);
351402315e8Sdlg 
352402315e8Sdlg 	return (kstatioc_leave(ksreq, ks));
353402315e8Sdlg }
354402315e8Sdlg 
355402315e8Sdlg int
356402315e8Sdlg kstatioc_nfind_pv(struct kstat_req *ksreq)
357402315e8Sdlg {
358402315e8Sdlg 	struct kstat *ks, key;
359402315e8Sdlg 	int error;
360402315e8Sdlg 
361402315e8Sdlg 	error = kstatioc_enter(ksreq);
362402315e8Sdlg 	if (error != 0)
363402315e8Sdlg 		return (error);
364402315e8Sdlg 
365402315e8Sdlg 	key.ks_provider = ksreq->ks_provider;
366402315e8Sdlg 	key.ks_instance = ksreq->ks_instance;
367402315e8Sdlg 	key.ks_name = ksreq->ks_name;
368402315e8Sdlg 	key.ks_unit = ksreq->ks_unit;
369402315e8Sdlg 
370402315e8Sdlg 	ks = RBT_NFIND(kstat_pv_tree, &kstat_pv_tree, &key);
371402315e8Sdlg 
372402315e8Sdlg 	return (kstatioc_leave(ksreq, ks));
373402315e8Sdlg }
374402315e8Sdlg 
375402315e8Sdlg int
376402315e8Sdlg kstatioc_find_nm(struct kstat_req *ksreq)
377402315e8Sdlg {
378402315e8Sdlg 	struct kstat *ks, key;
379402315e8Sdlg 	int error;
380402315e8Sdlg 
381402315e8Sdlg 	error = kstatioc_enter(ksreq);
382402315e8Sdlg 	if (error != 0)
383402315e8Sdlg 		return (error);
384402315e8Sdlg 
385402315e8Sdlg 	key.ks_name = ksreq->ks_name;
386402315e8Sdlg 	key.ks_unit = ksreq->ks_unit;
387402315e8Sdlg 	key.ks_provider = ksreq->ks_provider;
388402315e8Sdlg 	key.ks_instance = ksreq->ks_instance;
389402315e8Sdlg 
390402315e8Sdlg 	ks = RBT_FIND(kstat_nm_tree, &kstat_nm_tree, &key);
391402315e8Sdlg 
392402315e8Sdlg 	return (kstatioc_leave(ksreq, ks));
393402315e8Sdlg }
394402315e8Sdlg 
395402315e8Sdlg int
396402315e8Sdlg kstatioc_nfind_nm(struct kstat_req *ksreq)
397402315e8Sdlg {
398402315e8Sdlg 	struct kstat *ks, key;
399402315e8Sdlg 	int error;
400402315e8Sdlg 
401402315e8Sdlg 	error = kstatioc_enter(ksreq);
402402315e8Sdlg 	if (error != 0)
403402315e8Sdlg 		return (error);
404402315e8Sdlg 
405402315e8Sdlg 	key.ks_name = ksreq->ks_name;
406402315e8Sdlg 	key.ks_unit = ksreq->ks_unit;
407402315e8Sdlg 	key.ks_provider = ksreq->ks_provider;
408402315e8Sdlg 	key.ks_instance = ksreq->ks_instance;
409402315e8Sdlg 
410402315e8Sdlg 	ks = RBT_NFIND(kstat_nm_tree, &kstat_nm_tree, &key);
411402315e8Sdlg 
412402315e8Sdlg 	return (kstatioc_leave(ksreq, ks));
413402315e8Sdlg }
414402315e8Sdlg 
415402315e8Sdlg int
416402315e8Sdlg kstatioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
417402315e8Sdlg {
418402315e8Sdlg 	struct kstat_req *ksreq = (struct kstat_req *)data;
419402315e8Sdlg 	int error = 0;
420402315e8Sdlg 
421402315e8Sdlg 	KERNEL_UNLOCK();
422402315e8Sdlg 
423402315e8Sdlg 	switch (cmd) {
424402315e8Sdlg 	case KSTATIOC_VERSION:
425402315e8Sdlg 		*(unsigned int *)data = kstat_version;
426402315e8Sdlg 		break;
427402315e8Sdlg 
428402315e8Sdlg 	case KSTATIOC_FIND_ID:
429402315e8Sdlg 		error = kstatioc_find_id(ksreq);
430402315e8Sdlg 		break;
431402315e8Sdlg 	case KSTATIOC_NFIND_ID:
432402315e8Sdlg 		error = kstatioc_nfind_id(ksreq);
433402315e8Sdlg 		break;
434402315e8Sdlg 	case KSTATIOC_FIND_PROVIDER:
435402315e8Sdlg 		error = kstatioc_find_pv(ksreq);
436402315e8Sdlg 		break;
437402315e8Sdlg 	case KSTATIOC_NFIND_PROVIDER:
438402315e8Sdlg 		error = kstatioc_nfind_pv(ksreq);
439402315e8Sdlg 		break;
440402315e8Sdlg 	case KSTATIOC_FIND_NAME:
441402315e8Sdlg 		error = kstatioc_find_nm(ksreq);
442402315e8Sdlg 		break;
443402315e8Sdlg 	case KSTATIOC_NFIND_NAME:
444402315e8Sdlg 		error = kstatioc_nfind_nm(ksreq);
445402315e8Sdlg 		break;
446402315e8Sdlg 
447402315e8Sdlg 	default:
448402315e8Sdlg 		error = ENOTTY;
449402315e8Sdlg 		break;
450402315e8Sdlg 	}
451402315e8Sdlg 
452402315e8Sdlg 	KERNEL_LOCK();
453402315e8Sdlg 
454402315e8Sdlg 	return (error);
455402315e8Sdlg }
456402315e8Sdlg 
457402315e8Sdlg void
458402315e8Sdlg kstat_init(void)
459402315e8Sdlg {
460402315e8Sdlg 	static int initialized = 0;
461402315e8Sdlg 
462402315e8Sdlg 	if (initialized)
463402315e8Sdlg 		return;
464402315e8Sdlg 
465402315e8Sdlg 	pool_init(&kstat_pool, sizeof(struct kstat), 0, IPL_NONE,
466402315e8Sdlg 	    PR_WAITOK | PR_RWLOCK, "kstatmem", NULL);
467402315e8Sdlg 
468402315e8Sdlg 	initialized = 1;
469402315e8Sdlg }
470402315e8Sdlg 
471402315e8Sdlg int
472402315e8Sdlg kstat_strcheck(const char *str)
473402315e8Sdlg {
474402315e8Sdlg 	size_t i, l;
475402315e8Sdlg 
476402315e8Sdlg 	l = strlen(str);
477402315e8Sdlg 	if (l == 0 || l >= KSTAT_STRLEN)
478402315e8Sdlg 		return (-1);
479402315e8Sdlg 	for (i = 0; i < l; i++) {
480402315e8Sdlg 		int ch = str[i];
481402315e8Sdlg 		if (ch >= 'a' && ch <= 'z')
482402315e8Sdlg 			continue;
483402315e8Sdlg 		if (ch >= 'A' && ch <= 'Z')
484402315e8Sdlg 			continue;
485402315e8Sdlg 		if (ch >= '0' && ch <= '9')
486402315e8Sdlg 			continue;
487402315e8Sdlg 		switch (ch) {
488402315e8Sdlg 		case '-':
489402315e8Sdlg 		case '_':
490402315e8Sdlg 		case '.':
491402315e8Sdlg 			break;
492402315e8Sdlg 		default:
493402315e8Sdlg 			return (-1);
494402315e8Sdlg 		}
495402315e8Sdlg 	}
496402315e8Sdlg 
497402315e8Sdlg 	return (0);
498402315e8Sdlg }
499402315e8Sdlg 
500402315e8Sdlg struct kstat *
501402315e8Sdlg kstat_create(const char *provider, unsigned int instance,
502402315e8Sdlg     const char *name, unsigned int unit,
503402315e8Sdlg     unsigned int type, unsigned int flags)
504402315e8Sdlg {
505402315e8Sdlg 	struct kstat *ks, *oks;
506402315e8Sdlg 
507402315e8Sdlg 	if (kstat_strcheck(provider) == -1)
508402315e8Sdlg 		panic("invalid provider string");
509402315e8Sdlg 	if (kstat_strcheck(name) == -1)
510402315e8Sdlg 		panic("invalid name string");
511402315e8Sdlg 
512402315e8Sdlg 	kstat_init();
513402315e8Sdlg 
514402315e8Sdlg 	ks = pool_get(&kstat_pool, PR_WAITOK|PR_ZERO);
515402315e8Sdlg 
516402315e8Sdlg 	ks->ks_provider = provider;
517402315e8Sdlg 	ks->ks_instance = instance;
518402315e8Sdlg 	ks->ks_name = name;
519402315e8Sdlg 	ks->ks_unit = unit;
520402315e8Sdlg 	ks->ks_flags = flags;
521402315e8Sdlg 	ks->ks_type = type;
522402315e8Sdlg 	ks->ks_state = KSTAT_S_CREATED;
523402315e8Sdlg 
524402315e8Sdlg 	getnanouptime(&ks->ks_created);
525402315e8Sdlg 	ks->ks_updated = ks->ks_created;
526402315e8Sdlg 
527402315e8Sdlg 	ks->ks_lock = &kstat_default_lock;
528402315e8Sdlg 	ks->ks_lock_ops = &kstat_wlock_ops;
529402315e8Sdlg 	ks->ks_read = kstat_read;
530402315e8Sdlg 	ks->ks_copy = kstat_copy;
531402315e8Sdlg 
532402315e8Sdlg 	rw_enter_write(&kstat_lock);
533402315e8Sdlg 	ks->ks_id = kstat_next_id;
534402315e8Sdlg 
535402315e8Sdlg 	oks = RBT_INSERT(kstat_pv_tree, &kstat_pv_tree, ks);
536402315e8Sdlg 	if (oks == NULL) {
537402315e8Sdlg 		/* commit */
538402315e8Sdlg 		kstat_next_id++;
539402315e8Sdlg 		kstat_version++;
540402315e8Sdlg 
541402315e8Sdlg 		oks = RBT_INSERT(kstat_nm_tree, &kstat_nm_tree, ks);
542402315e8Sdlg 		if (oks != NULL)
543402315e8Sdlg 			panic("kstat name collision! (%llu)", ks->ks_id);
544402315e8Sdlg 
545402315e8Sdlg 		oks = RBT_INSERT(kstat_id_tree, &kstat_id_tree, ks);
546402315e8Sdlg 		if (oks != NULL)
547402315e8Sdlg 			panic("kstat id collision! (%llu)", ks->ks_id);
548402315e8Sdlg 	}
549402315e8Sdlg 	rw_exit_write(&kstat_lock);
550402315e8Sdlg 
551402315e8Sdlg 	if (oks != NULL) {
552402315e8Sdlg 		pool_put(&kstat_pool, ks);
553402315e8Sdlg 		return (NULL);
554402315e8Sdlg 	}
555402315e8Sdlg 
556402315e8Sdlg 	return (ks);
557402315e8Sdlg }
558402315e8Sdlg 
559402315e8Sdlg void
560402315e8Sdlg kstat_set_rlock(struct kstat *ks, struct rwlock *rwl)
561402315e8Sdlg {
562402315e8Sdlg 	KASSERT(ks->ks_state == KSTAT_S_CREATED);
563402315e8Sdlg 
564402315e8Sdlg 	ks->ks_lock = rwl;
565402315e8Sdlg 	ks->ks_lock_ops = &kstat_rlock_ops;
566402315e8Sdlg }
567402315e8Sdlg 
568402315e8Sdlg void
569402315e8Sdlg kstat_set_wlock(struct kstat *ks, struct rwlock *rwl)
570402315e8Sdlg {
571402315e8Sdlg 	KASSERT(ks->ks_state == KSTAT_S_CREATED);
572402315e8Sdlg 
573402315e8Sdlg 	ks->ks_lock = rwl;
574402315e8Sdlg 	ks->ks_lock_ops = &kstat_wlock_ops;
575402315e8Sdlg }
576402315e8Sdlg 
577402315e8Sdlg void
578402315e8Sdlg kstat_set_mutex(struct kstat *ks, struct mutex *mtx)
579402315e8Sdlg {
580402315e8Sdlg 	KASSERT(ks->ks_state == KSTAT_S_CREATED);
581402315e8Sdlg 
582402315e8Sdlg 	ks->ks_lock = mtx;
583402315e8Sdlg 	ks->ks_lock_ops = &kstat_mutex_ops;
584402315e8Sdlg }
585402315e8Sdlg 
586402315e8Sdlg void
587402315e8Sdlg kstat_cpu_enter(void *p)
588402315e8Sdlg {
589402315e8Sdlg 	struct cpu_info *ci = p;
590402315e8Sdlg 	sched_peg_curproc(ci);
591402315e8Sdlg }
592402315e8Sdlg 
593402315e8Sdlg void
594402315e8Sdlg kstat_cpu_leave(void *p)
595402315e8Sdlg {
596cf31dfdeSmpi 	sched_unpeg_curproc();
597402315e8Sdlg }
598402315e8Sdlg 
599402315e8Sdlg void
600402315e8Sdlg kstat_set_cpu(struct kstat *ks, struct cpu_info *ci)
601402315e8Sdlg {
602402315e8Sdlg 	KASSERT(ks->ks_state == KSTAT_S_CREATED);
603402315e8Sdlg 
604402315e8Sdlg 	ks->ks_lock = ci;
605402315e8Sdlg 	ks->ks_lock_ops = &kstat_cpu_ops;
606402315e8Sdlg }
607402315e8Sdlg 
608402315e8Sdlg int
609402315e8Sdlg kstat_read_nop(struct kstat *ks)
610402315e8Sdlg {
611402315e8Sdlg 	return (0);
612402315e8Sdlg }
613402315e8Sdlg 
614402315e8Sdlg void
615402315e8Sdlg kstat_install(struct kstat *ks)
616402315e8Sdlg {
617402315e8Sdlg 	if (!ISSET(ks->ks_flags, KSTAT_F_REALLOC)) {
618402315e8Sdlg 		KASSERTMSG(ks->ks_copy != NULL || ks->ks_data != NULL,
619402315e8Sdlg 		    "kstat %p %s:%u:%s:%u must provide ks_copy or ks_data", ks,
620402315e8Sdlg 		    ks->ks_provider, ks->ks_instance, ks->ks_name, ks->ks_unit);
621402315e8Sdlg 		KASSERT(ks->ks_datalen > 0);
622402315e8Sdlg 	}
623402315e8Sdlg 
624402315e8Sdlg 	rw_enter_write(&kstat_lock);
625402315e8Sdlg 	ks->ks_state = KSTAT_S_INSTALLED;
626402315e8Sdlg 	rw_exit_write(&kstat_lock);
627402315e8Sdlg }
628402315e8Sdlg 
629402315e8Sdlg void
630a94953c6Sdlg kstat_remove(struct kstat *ks)
631a94953c6Sdlg {
632a94953c6Sdlg 	rw_enter_write(&kstat_lock);
633a94953c6Sdlg 	KASSERTMSG(ks->ks_state == KSTAT_S_INSTALLED,
634a94953c6Sdlg 	    "kstat %p %s:%u:%s:%u is not installed", ks,
635a94953c6Sdlg 	    ks->ks_provider, ks->ks_instance, ks->ks_name, ks->ks_unit);
636a94953c6Sdlg 
637a94953c6Sdlg 	ks->ks_state = KSTAT_S_CREATED;
638a94953c6Sdlg 	rw_exit_write(&kstat_lock);
639a94953c6Sdlg }
640a94953c6Sdlg 
641a94953c6Sdlg void
642402315e8Sdlg kstat_destroy(struct kstat *ks)
643402315e8Sdlg {
644402315e8Sdlg 	rw_enter_write(&kstat_lock);
645402315e8Sdlg 	RBT_REMOVE(kstat_id_tree, &kstat_id_tree, ks);
646402315e8Sdlg 	RBT_REMOVE(kstat_pv_tree, &kstat_pv_tree, ks);
647402315e8Sdlg 	RBT_REMOVE(kstat_nm_tree, &kstat_nm_tree, ks);
648402315e8Sdlg 	kstat_version++;
649402315e8Sdlg 	rw_exit_write(&kstat_lock);
650402315e8Sdlg 
651402315e8Sdlg 	pool_put(&kstat_pool, ks);
652402315e8Sdlg }
653402315e8Sdlg 
654402315e8Sdlg int
655402315e8Sdlg kstat_read(struct kstat *ks)
656402315e8Sdlg {
657402315e8Sdlg 	getnanouptime(&ks->ks_updated);
658402315e8Sdlg 	return (0);
659402315e8Sdlg }
660402315e8Sdlg 
661402315e8Sdlg int
662402315e8Sdlg kstat_copy(struct kstat *ks, void *buf)
663402315e8Sdlg {
664402315e8Sdlg 	memcpy(buf, ks->ks_data, ks->ks_datalen);
665402315e8Sdlg 	return (0);
666402315e8Sdlg }
667402315e8Sdlg 
668402315e8Sdlg RBT_GENERATE(kstat_id_tree, kstat, ks_id_entry, kstat_id_cmp);
669402315e8Sdlg RBT_GENERATE(kstat_pv_tree, kstat, ks_pv_entry, kstat_pv_cmp);
670402315e8Sdlg RBT_GENERATE(kstat_nm_tree, kstat, ks_nm_entry, kstat_nm_cmp);
671402315e8Sdlg 
672402315e8Sdlg void
673402315e8Sdlg kstat_kv_init(struct kstat_kv *kv, const char *name, enum kstat_kv_type type)
674402315e8Sdlg {
675402315e8Sdlg 	memset(kv, 0, sizeof(*kv));
676402315e8Sdlg 	strlcpy(kv->kv_key, name, sizeof(kv->kv_key)); /* XXX truncated? */
677402315e8Sdlg 	kv->kv_type = type;
678402315e8Sdlg 	kv->kv_unit = KSTAT_KV_U_NONE;
679402315e8Sdlg }
680402315e8Sdlg 
681402315e8Sdlg void
682402315e8Sdlg kstat_kv_unit_init(struct kstat_kv *kv, const char *name,
683402315e8Sdlg     enum kstat_kv_type type, enum kstat_kv_unit unit)
684402315e8Sdlg {
685402315e8Sdlg 	switch (type) {
686402315e8Sdlg 	case KSTAT_KV_T_COUNTER64:
687402315e8Sdlg 	case KSTAT_KV_T_COUNTER32:
688*e6b4f1cdSmglocker 	case KSTAT_KV_T_COUNTER16:
689402315e8Sdlg 	case KSTAT_KV_T_UINT64:
690402315e8Sdlg 	case KSTAT_KV_T_INT64:
691402315e8Sdlg 	case KSTAT_KV_T_UINT32:
692402315e8Sdlg 	case KSTAT_KV_T_INT32:
693*e6b4f1cdSmglocker 	case KSTAT_KV_T_UINT16:
694*e6b4f1cdSmglocker 	case KSTAT_KV_T_INT16:
695402315e8Sdlg 		break;
696402315e8Sdlg 	default:
697402315e8Sdlg 		panic("kv unit init %s: unit for non-integer type", name);
698402315e8Sdlg 	}
699402315e8Sdlg 
700402315e8Sdlg 	memset(kv, 0, sizeof(*kv));
701402315e8Sdlg 	strlcpy(kv->kv_key, name, sizeof(kv->kv_key)); /* XXX truncated? */
702402315e8Sdlg 	kv->kv_type = type;
703402315e8Sdlg 	kv->kv_unit = unit;
704402315e8Sdlg }
705