Home | History | Annotate | Line # | Download | only in kern
      1 /* $NetBSD: subr_evcnt.c,v 1.18 2026/01/04 03:17:15 riastradh Exp $ */
      2 
      3 /*
      4  * Copyright (c) 1996, 2000 Christopher G. Demetriou
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *          This product includes software developed for the
     18  *          NetBSD Project.  See http://www.NetBSD.org/ for
     19  *          information about NetBSD.
     20  * 4. The name of the author may not be used to endorse or promote products
     21  *    derived from this software without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  *
     34  * --(license Id: LICENSE.proto,v 1.1 2000/06/13 21:40:26 cgd Exp )--
     35  */
     36 
     37 /*
     38  * Copyright (c) 1992, 1993
     39  *	The Regents of the University of California.  All rights reserved.
     40  *
     41  * This software was developed by the Computer Systems Engineering group
     42  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
     43  * contributed to Berkeley.
     44  *
     45  * All advertising materials mentioning features or use of this software
     46  * must display the following acknowledgement:
     47  *	This product includes software developed by the University of
     48  *	California, Lawrence Berkeley Laboratories.
     49  *
     50  * Redistribution and use in source and binary forms, with or without
     51  * modification, are permitted provided that the following conditions
     52  * are met:
     53  * 1. Redistributions of source code must retain the above copyright
     54  *    notice, this list of conditions and the following disclaimer.
     55  * 2. Redistributions in binary form must reproduce the above copyright
     56  *    notice, this list of conditions and the following disclaimer in the
     57  *    documentation and/or other materials provided with the distribution.
     58  * 3. Neither the name of the University nor the names of its contributors
     59  *    may be used to endorse or promote products derived from this software
     60  *    without specific prior written permission.
     61  *
     62  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     63  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     64  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     65  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     66  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     67  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     68  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     69  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     70  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     71  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     72  * SUCH DAMAGE.
     73  *
     74  * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp  (LBL)
     75  *
     76  *	@(#)subr_autoconf.c	8.3 (Berkeley) 5/17/94
     77  */
     78 
     79 #include <sys/cdefs.h>
     80 __KERNEL_RCSID(0, "$NetBSD: subr_evcnt.c,v 1.18 2026/01/04 03:17:15 riastradh Exp $");
     81 
     82 #include <sys/param.h>
     83 #include <sys/evcnt.h>
     84 #include <sys/kmem.h>
     85 #include <sys/mutex.h>
     86 #include <sys/sdt.h>
     87 #include <sys/sysctl.h>
     88 #include <sys/systm.h>
     89 
     90 /*
     91  * Everything related to __HAVE_LEGACY_INTRCNT can disappear once
     92  * no more ports are using old-style intrcnt/intrnames interrupt
     93  * accounting.  The follow files have __HAVE_LEGACY_INTRCNT code:
     94  *
     95  *   sys/kern/init_main.c
     96  *   sys/kern/subr_evcnt.c
     97  *   sys/sys/evcnt.h
     98  *   sys/arch/<port>/include/types.h
     99  */
    100 #ifdef _RUMPKERNEL
    101 /* RUMP doesn't need/want to know about intrcnts */
    102 #undef __HAVE_LEGACY_INTRCNT
    103 #endif
    104 
    105 #ifdef __HAVE_LEGACY_INTRCNT
    106 static void evcnt_update_intrcnt(void);
    107 #endif
    108 
    109 /* list of all events */
    110 struct evcntlist allevents = TAILQ_HEAD_INITIALIZER(allevents);
    111 static kmutex_t evcnt_lock __cacheline_aligned;
    112 static bool init_done;
    113 static uint32_t evcnt_generation;
    114 
    115 /*
    116  * We need a dummy object to stuff into the evcnt link set to
    117  * ensure that there always is at least one object in the set.
    118  */
    119 static struct evcnt dummy_static_evcnt;
    120 __link_set_add_bss(evcnts, dummy_static_evcnt);
    121 
    122 /*
    123  * Initialize event counters.  This does the attach procedure for
    124  * each of the static event counters in the "evcnts" link set.
    125  */
    126 void
    127 evcnt_init(void)
    128 {
    129 	__link_set_decl(evcnts, struct evcnt);
    130 	struct evcnt * const *evp;
    131 
    132 	KASSERT(!init_done);
    133 
    134 	mutex_init(&evcnt_lock, MUTEX_DEFAULT, IPL_NONE);
    135 
    136 	init_done = true;
    137 
    138 	__link_set_foreach(evp, evcnts) {
    139 		if (*evp == &dummy_static_evcnt)
    140 			continue;
    141 		evcnt_attach_static(*evp);
    142 	}
    143 }
    144 
    145 /*
    146  * Attach a statically-initialized event.  The type and string pointers
    147  * are already set up.
    148  */
    149 void
    150 evcnt_attach_static(struct evcnt *ev)
    151 {
    152 	int len;
    153 
    154 	KASSERTMSG(init_done,
    155 	    "%s: evcnt non initialized: group=<%s> name=<%s>",
    156 	    __func__, ev->ev_group, ev->ev_name);
    157 
    158 	len = strlen(ev->ev_group);
    159 #ifdef DIAGNOSTIC
    160 	if (len == 0 || len >= EVCNT_STRING_MAX) /* ..._MAX includes NUL */
    161 		panic("evcnt_attach_static: group length (%s)", ev->ev_group);
    162 #endif
    163 	ev->ev_grouplen = len;
    164 
    165 	len = strlen(ev->ev_name);
    166 #ifdef DIAGNOSTIC
    167 	if (len == 0 || len >= EVCNT_STRING_MAX) /* ..._MAX includes NUL */
    168 		panic("evcnt_attach_static: name length (%s)", ev->ev_name);
    169 #endif
    170 	ev->ev_namelen = len;
    171 
    172 	mutex_enter(&evcnt_lock);
    173 	TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
    174 	mutex_exit(&evcnt_lock);
    175 }
    176 
    177 /*
    178  * Attach a dynamically-initialized event.  Zero it, set up the type
    179  * and string pointers and then act like it was statically initialized.
    180  */
    181 void
    182 evcnt_attach_dynamic_nozero(struct evcnt *ev, int type,
    183     const struct evcnt *parent, const char *group, const char *name)
    184 {
    185 
    186 	ev->ev_type = type;
    187 	ev->ev_parent = parent;
    188 	ev->ev_group = group;
    189 	ev->ev_name = name;
    190 	evcnt_attach_static(ev);
    191 }
    192 /*
    193  * Attach a dynamically-initialized event.  Zero it, set up the type
    194  * and string pointers and then act like it was statically initialized.
    195  */
    196 void
    197 evcnt_attach_dynamic(struct evcnt *ev, int type, const struct evcnt *parent,
    198     const char *group, const char *name)
    199 {
    200 
    201 	memset(ev, 0, sizeof *ev);
    202 	evcnt_attach_dynamic_nozero(ev, type, parent, group, name);
    203 }
    204 
    205 /*
    206  * Detach an event.
    207  */
    208 void
    209 evcnt_detach(struct evcnt *ev)
    210 {
    211 
    212 	mutex_enter(&evcnt_lock);
    213 	TAILQ_REMOVE(&allevents, ev, ev_list);
    214 	evcnt_generation++;
    215 	mutex_exit(&evcnt_lock);
    216 }
    217 
    218 typedef char ev_strings[2*EVCNT_STRING_MAX];
    219 
    220 static size_t
    221 sysctl_fillevcnt(const struct evcnt *ev, struct evcnt_sysctl *evs,
    222 	size_t *copylenp)
    223 {
    224 	const bool allowaddr = get_expose_address(curproc);
    225 	const size_t copylen = offsetof(struct evcnt_sysctl, ev_strings)
    226 	    + ev->ev_grouplen + 1 + ev->ev_namelen + 1;
    227 	const size_t len = roundup2(copylen, sizeof(uint64_t));
    228 
    229 	if (evs != NULL) {
    230 		evs->ev_count = ev->ev_count;
    231 		COND_SET_VALUE(evs->ev_addr, PTRTOUINT64(ev), allowaddr);
    232 		COND_SET_VALUE(evs->ev_parent, PTRTOUINT64(ev->ev_parent),
    233 		    allowaddr);
    234 		evs->ev_type = ev->ev_type;
    235 		evs->ev_grouplen = ev->ev_grouplen;
    236 		evs->ev_namelen = ev->ev_namelen;
    237 		evs->ev_len = len / sizeof(uint64_t);
    238 		strcpy(evs->ev_strings, ev->ev_group);
    239 		strcpy(evs->ev_strings + ev->ev_grouplen + 1, ev->ev_name);
    240 	}
    241 
    242 	*copylenp = copylen;
    243 	return len;
    244 }
    245 
    246 static int
    247 sysctl_doevcnt(SYSCTLFN_ARGS)
    248 {
    249 	struct evcnt_sysctl *evs0 = NULL, *evs;
    250 	const size_t xevcnt_size = sizeof(*evs0) + sizeof(ev_strings);
    251 	const struct evcnt *ev;
    252 	int error;
    253 	int retries;
    254 	size_t needed, len;
    255 	char *dp;
    256 
    257         if (namelen == 1 && name[0] == CTL_QUERY)
    258                 return (sysctl_query(SYSCTLFN_CALL(rnode)));
    259 
    260 	if (namelen != 2)
    261 		return SET_ERROR(EINVAL);
    262 
    263 	/*
    264 	 * We can filter on the type of evcnt.
    265 	 */
    266 	const int filter = name[0];
    267 	if (filter != EVCNT_TYPE_ANY
    268 	    && filter != EVCNT_TYPE_MISC
    269 	    && filter != EVCNT_TYPE_INTR
    270 	    && filter != EVCNT_TYPE_TRAP)
    271 		return SET_ERROR(EINVAL);
    272 
    273 	const u_int count = name[1];
    274 	if (count != KERN_EVCNT_COUNT_ANY
    275 	    && count != KERN_EVCNT_COUNT_NONZERO)
    276 		return SET_ERROR(EINVAL);
    277 
    278 	sysctl_unlock();
    279 
    280 	if (oldp != NULL)
    281 		evs0 = kmem_zalloc(xevcnt_size, KM_SLEEP);
    282 
    283 	retries = 100;
    284  retry:
    285 	dp = oldp;
    286 	len = (oldp != NULL) ? *oldlenp : 0;
    287 	evs = evs0;
    288 	error = 0;
    289 	needed = 0;
    290 
    291 	mutex_enter(&evcnt_lock);
    292 #ifdef __HAVE_LEGACY_INTRCNT
    293 	evcnt_update_intrcnt();
    294 #endif
    295 	TAILQ_FOREACH(ev, &allevents, ev_list) {
    296 		if (filter != EVCNT_TYPE_ANY && filter != ev->ev_type)
    297 			continue;
    298 		if (count == KERN_EVCNT_COUNT_NONZERO && ev->ev_count == 0)
    299 			continue;
    300 
    301 		/*
    302 		 * Prepare to copy.  If evs is NULL, fillevcnt will just
    303 		 * how big the item is.
    304 		 */
    305 		size_t copylen;
    306 		const size_t elem_size = sysctl_fillevcnt(ev, evs, &copylen);
    307 		needed += elem_size;
    308 
    309 		if (len < elem_size) {
    310 			evs = NULL;
    311 			continue;
    312 		}
    313 
    314 		KASSERT(evs != NULL);
    315 		KASSERT(evs->ev_grouplen != 0);
    316 		KASSERT(evs->ev_namelen != 0);
    317 		KASSERT(evs->ev_strings[0] != 0);
    318 
    319 		const uint32_t last_generation = evcnt_generation;
    320 		mutex_exit(&evcnt_lock);
    321 
    322 		/*
    323 		 * Only copy the actual number of bytes, not the rounded
    324 		 * number.  If we did the latter we'd have to zero them
    325 		 * first or we'd leak random kernel memory.
    326 		 */
    327 		error = copyout(evs, dp, copylen);
    328 
    329 		mutex_enter(&evcnt_lock);
    330 		if (error)
    331 			break;
    332 
    333 		if (__predict_false(last_generation != evcnt_generation)) {
    334 			/*
    335 			 * This sysctl node is only for statistics.
    336 			 * Retry; if the queue keeps changing, then
    337 			 * bail out.
    338 			 */
    339 			if (--retries == 0) {
    340 				error = SET_ERROR(EAGAIN);
    341 				break;
    342 			}
    343 			mutex_exit(&evcnt_lock);
    344 			goto retry;
    345 		}
    346 
    347 		/*
    348 		 * Now we deal with the pointer/len since we aren't going to
    349 		 * toss their values away.
    350 		 */
    351 		dp += elem_size;
    352 		len -= elem_size;
    353 	}
    354 	mutex_exit(&evcnt_lock);
    355 
    356 	if (evs0 != NULL)
    357 		kmem_free(evs0, xevcnt_size);
    358 
    359 	sysctl_relock();
    360 
    361 	*oldlenp = needed;
    362 	if (oldp == NULL)
    363 		*oldlenp += 1024;
    364 
    365 	return error;
    366 }
    367 
    368 
    369 
    370 SYSCTL_SETUP(sysctl_evcnt_setup, "sysctl kern.evcnt subtree setup")
    371 {
    372 
    373 	sysctl_createv(clog, 0, NULL, NULL,
    374 		       CTLFLAG_PERMANENT,
    375 		       CTLTYPE_STRUCT, "evcnt",
    376 		       SYSCTL_DESCR("Kernel evcnt information"),
    377 		       sysctl_doevcnt, 0, NULL, 0,
    378 		       CTL_KERN, KERN_EVCNT, CTL_EOL);
    379 }
    380 
    381 #ifdef __HAVE_LEGACY_INTRCNT
    382 extern u_int intrcnt[], eintrcnt[];
    383 extern char intrnames[];
    384 static size_t nintr;
    385 struct evcnt *intr_evcnts;
    386 /*
    387  * Remove the following when the last intrcnt/intrnames user is cleaned up.
    388  */
    389 void
    390 evcnt_attach_legacy_intrcnt(void)
    391 {
    392 	size_t i;
    393 	const char *cp;
    394 
    395 	nintr = ((intptr_t)eintrcnt - (intptr_t)intrcnt) / sizeof(intrcnt[0]);
    396 	intr_evcnts = kmem_alloc(sizeof(struct evcnt) * nintr, KM_SLEEP);
    397 	for (cp = intrnames, i = 0; i < nintr; i++) {
    398 		evcnt_attach_dynamic(&intr_evcnts[i], EVCNT_TYPE_INTR,
    399 		    NULL, "cpu", cp);
    400 		cp += strlen(cp) + 1;
    401 	}
    402 }
    403 
    404 static void
    405 evcnt_update_intrcnt(void)
    406 {
    407 	size_t i;
    408 
    409 	KASSERT(nintr > 0);
    410 	KASSERT(intr_evcnts != NULL);
    411 
    412 	for (i = 0; i < nintr; i++) {
    413 		intr_evcnts[i].ev_count = intrcnt[i];
    414 	}
    415 }
    416 #endif
    417