Home | History | Annotate | Line # | Download | only in kern
subr_evcnt.c revision 1.16
      1 /* $NetBSD: subr_evcnt.c,v 1.16 2021/04/15 00:37:31 rin 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.16 2021/04/15 00:37:31 rin 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/sysctl.h>
     87 #include <sys/systm.h>
     88 
     89 /*
     90  * Everything related to __HAVE_LEGACY_INTRCNT can disappear once
     91  * no more ports are using old-style intrcnt/intrnames interrupt
     92  * accounting.  The follow files have __HAVE_LEGACY_INTRCNT code:
     93  *
     94  *   sys/kern/init_main.c
     95  *   sys/kern/subr_evcnt.c
     96  *   sys/sys/evcnt.h
     97  *   sys/arch/<port>/include/types.h
     98  */
     99 #ifdef _RUMPKERNEL
    100 /* RUMP doesn't need/want to know about intrcnts */
    101 #undef __HAVE_LEGACY_INTRCNT
    102 #endif
    103 
    104 #ifdef __HAVE_LEGACY_INTRCNT
    105 static void evcnt_update_intrcnt(void);
    106 #endif
    107 
    108 /* list of all events */
    109 struct evcntlist allevents = TAILQ_HEAD_INITIALIZER(allevents);
    110 static kmutex_t evcnt_lock __cacheline_aligned;
    111 static bool init_done;
    112 static uint32_t evcnt_generation;
    113 
    114 /*
    115  * We need a dummy object to stuff into the evcnt link set to
    116  * ensure that there always is at least one object in the set.
    117  */
    118 static struct evcnt dummy_static_evcnt;
    119 __link_set_add_bss(evcnts, dummy_static_evcnt);
    120 
    121 /*
    122  * Initialize event counters.  This does the attach procedure for
    123  * each of the static event counters in the "evcnts" link set.
    124  */
    125 void
    126 evcnt_init(void)
    127 {
    128 	__link_set_decl(evcnts, struct evcnt);
    129 	struct evcnt * const *evp;
    130 
    131 	KASSERT(!init_done);
    132 
    133 	mutex_init(&evcnt_lock, MUTEX_DEFAULT, IPL_NONE);
    134 
    135 	init_done = true;
    136 
    137 	__link_set_foreach(evp, evcnts) {
    138 		if (*evp == &dummy_static_evcnt)
    139 			continue;
    140 		evcnt_attach_static(*evp);
    141 	}
    142 }
    143 
    144 /*
    145  * Attach a statically-initialized event.  The type and string pointers
    146  * are already set up.
    147  */
    148 void
    149 evcnt_attach_static(struct evcnt *ev)
    150 {
    151 	int len;
    152 
    153 	KASSERTMSG(init_done,
    154 	    "%s: evcnt non initialized: group=<%s> name=<%s>",
    155 	    __func__, ev->ev_group, ev->ev_name);
    156 
    157 	len = strlen(ev->ev_group);
    158 #ifdef DIAGNOSTIC
    159 	if (len == 0 || len >= EVCNT_STRING_MAX) /* ..._MAX includes NUL */
    160 		panic("evcnt_attach_static: group length (%s)", ev->ev_group);
    161 #endif
    162 	ev->ev_grouplen = len;
    163 
    164 	len = strlen(ev->ev_name);
    165 #ifdef DIAGNOSTIC
    166 	if (len == 0 || len >= EVCNT_STRING_MAX) /* ..._MAX includes NUL */
    167 		panic("evcnt_attach_static: name length (%s)", ev->ev_name);
    168 #endif
    169 	ev->ev_namelen = len;
    170 
    171 	mutex_enter(&evcnt_lock);
    172 	TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
    173 	mutex_exit(&evcnt_lock);
    174 }
    175 
    176 /*
    177  * Attach a dynamically-initialized event.  Zero it, set up the type
    178  * and string pointers and then act like it was statically initialized.
    179  */
    180 void
    181 evcnt_attach_dynamic_nozero(struct evcnt *ev, int type,
    182     const struct evcnt *parent, const char *group, const char *name)
    183 {
    184 
    185 	ev->ev_type = type;
    186 	ev->ev_parent = parent;
    187 	ev->ev_group = group;
    188 	ev->ev_name = name;
    189 	evcnt_attach_static(ev);
    190 }
    191 /*
    192  * Attach a dynamically-initialized event.  Zero it, set up the type
    193  * and string pointers and then act like it was statically initialized.
    194  */
    195 void
    196 evcnt_attach_dynamic(struct evcnt *ev, int type, const struct evcnt *parent,
    197     const char *group, const char *name)
    198 {
    199 
    200 	memset(ev, 0, sizeof *ev);
    201 	evcnt_attach_dynamic_nozero(ev, type, parent, group, name);
    202 }
    203 
    204 /*
    205  * Detach an event.
    206  */
    207 void
    208 evcnt_detach(struct evcnt *ev)
    209 {
    210 
    211 	mutex_enter(&evcnt_lock);
    212 	TAILQ_REMOVE(&allevents, ev, ev_list);
    213 	evcnt_generation++;
    214 	mutex_exit(&evcnt_lock);
    215 }
    216 
    217 struct xevcnt_sysctl {
    218 	struct evcnt_sysctl evs;
    219 	char ev_strings[2*EVCNT_STRING_MAX];
    220 };
    221 
    222 static size_t
    223 sysctl_fillevcnt(const struct evcnt *ev, struct xevcnt_sysctl *xevs,
    224 	size_t *copylenp)
    225 {
    226 	const bool allowaddr = get_expose_address(curproc);
    227 	const size_t copylen = offsetof(struct evcnt_sysctl, ev_strings)
    228 	    + ev->ev_grouplen + 1 + ev->ev_namelen + 1;
    229 	const size_t len = roundup2(copylen, sizeof(uint64_t));
    230 
    231 	if (xevs != NULL) {
    232 		xevs->evs.ev_count = ev->ev_count;
    233 		COND_SET_VALUE(xevs->evs.ev_addr, PTRTOUINT64(ev), allowaddr);
    234 		COND_SET_VALUE(xevs->evs.ev_parent, PTRTOUINT64(ev->ev_parent),
    235 		    allowaddr);
    236 		xevs->evs.ev_type = ev->ev_type;
    237 		xevs->evs.ev_grouplen = ev->ev_grouplen;
    238 		xevs->evs.ev_namelen = ev->ev_namelen;
    239 		xevs->evs.ev_len = len / sizeof(uint64_t);
    240 		strcpy(xevs->evs.ev_strings, ev->ev_group);
    241 		strcpy(xevs->evs.ev_strings + ev->ev_grouplen + 1, ev->ev_name);
    242 	}
    243 
    244 	*copylenp = copylen;
    245 	return len;
    246 }
    247 
    248 static int
    249 sysctl_doevcnt(SYSCTLFN_ARGS)
    250 {
    251 	struct xevcnt_sysctl *xevs0 = NULL, *xevs;
    252 	const struct evcnt *ev;
    253 	int error;
    254 	int retries;
    255 	size_t needed, len;
    256 	char *dp;
    257 
    258         if (namelen == 1 && name[0] == CTL_QUERY)
    259                 return (sysctl_query(SYSCTLFN_CALL(rnode)));
    260 
    261 	if (namelen != 2)
    262 		return (EINVAL);
    263 
    264 	/*
    265 	 * We can filter on the type of evcnt.
    266 	 */
    267 	const int filter = name[0];
    268 	if (filter != EVCNT_TYPE_ANY
    269 	    && filter != EVCNT_TYPE_MISC
    270 	    && filter != EVCNT_TYPE_INTR
    271 	    && filter != EVCNT_TYPE_TRAP)
    272 		return (EINVAL);
    273 
    274 	const u_int count = name[1];
    275 	if (count != KERN_EVCNT_COUNT_ANY
    276 	    && count != KERN_EVCNT_COUNT_NONZERO)
    277 		return (EINVAL);
    278 
    279 	sysctl_unlock();
    280 
    281 	if (oldp != NULL && xevs0 == NULL)
    282 		xevs0 = kmem_zalloc(sizeof(*xevs0), KM_SLEEP);
    283 
    284 	retries = 100;
    285  retry:
    286 	dp = oldp;
    287 	len = (oldp != NULL) ? *oldlenp : 0;
    288 	xevs = xevs0;
    289 	error = 0;
    290 	needed = 0;
    291 
    292 	mutex_enter(&evcnt_lock);
    293 #ifdef __HAVE_LEGACY_INTRCNT
    294 	evcnt_update_intrcnt();
    295 #endif
    296 	TAILQ_FOREACH(ev, &allevents, ev_list) {
    297 		if (filter != EVCNT_TYPE_ANY && filter != ev->ev_type)
    298 			continue;
    299 		if (count == KERN_EVCNT_COUNT_NONZERO && ev->ev_count == 0)
    300 			continue;
    301 
    302 		/*
    303 		 * Prepare to copy.  If xevs is NULL, fillevcnt will just
    304 		 * how big the item is.
    305 		 */
    306 		size_t copylen;
    307 		const size_t elem_size = sysctl_fillevcnt(ev, xevs, &copylen);
    308 		needed += elem_size;
    309 
    310 		if (len < elem_size) {
    311 			xevs = NULL;
    312 			continue;
    313 		}
    314 
    315 		KASSERT(xevs != NULL);
    316 		KASSERT(xevs->evs.ev_grouplen != 0);
    317 		KASSERT(xevs->evs.ev_namelen != 0);
    318 		KASSERT(xevs->evs.ev_strings[0] != 0);
    319 
    320 		const uint32_t last_generation = evcnt_generation;
    321 		mutex_exit(&evcnt_lock);
    322 
    323 		/*
    324 		 * Only copy the actual number of bytes, not the rounded
    325 		 * number.  If we did the latter we'd have to zero them
    326 		 * first or we'd leak random kernel memory.
    327 		 */
    328 		error = copyout(xevs, dp, copylen);
    329 
    330 		mutex_enter(&evcnt_lock);
    331 		if (error)
    332 			break;
    333 
    334 		if (__predict_false(last_generation != evcnt_generation)) {
    335 			/*
    336 			 * This sysctl node is only for statistics.
    337 			 * Retry; if the queue keeps changing, then
    338 			 * bail out.
    339 			 */
    340 			if (--retries == 0) {
    341 				error = EAGAIN;
    342 				break;
    343 			}
    344 			mutex_exit(&evcnt_lock);
    345 			goto retry;
    346 		}
    347 
    348 		/*
    349 		 * Now we deal with the pointer/len since we aren't going to
    350 		 * toss their values away.
    351 		 */
    352 		dp += elem_size;
    353 		len -= elem_size;
    354 	}
    355 	mutex_exit(&evcnt_lock);
    356 
    357 	if (xevs0 != NULL)
    358 		kmem_free(xevs0, sizeof(*xevs0));
    359 
    360 	sysctl_relock();
    361 
    362 	*oldlenp = needed;
    363 	if (oldp == NULL)
    364 		*oldlenp += 1024;
    365 
    366 	return (error);
    367 }
    368 
    369 
    370 
    371 SYSCTL_SETUP(sysctl_evcnt_setup, "sysctl kern.evcnt subtree setup")
    372 {
    373 
    374 	sysctl_createv(clog, 0, NULL, NULL,
    375 		       CTLFLAG_PERMANENT,
    376 		       CTLTYPE_STRUCT, "evcnt",
    377 		       SYSCTL_DESCR("Kernel evcnt information"),
    378 		       sysctl_doevcnt, 0, NULL, 0,
    379 		       CTL_KERN, KERN_EVCNT, CTL_EOL);
    380 }
    381 
    382 #ifdef __HAVE_LEGACY_INTRCNT
    383 extern u_int intrcnt[], eintrcnt[];
    384 extern char intrnames[];
    385 static size_t nintr;
    386 struct evcnt *intr_evcnts;
    387 /*
    388  * Remove the following when the last intrcnt/intrnames user is cleaned up.
    389  */
    390 void
    391 evcnt_attach_legacy_intrcnt(void)
    392 {
    393 	size_t i;
    394 	const char *cp;
    395 
    396 	nintr = ((intptr_t)eintrcnt - (intptr_t)intrcnt) / sizeof(intrcnt[0]);
    397 	intr_evcnts = kmem_alloc(sizeof(struct evcnt) * nintr, KM_SLEEP);
    398 	for (cp = intrnames, i = 0; i < nintr; i++) {
    399 		evcnt_attach_dynamic(&intr_evcnts[i], EVCNT_TYPE_INTR,
    400 		    NULL, "cpu", cp);
    401 		cp += strlen(cp) + 1;
    402 	}
    403 }
    404 
    405 static void
    406 evcnt_update_intrcnt(void)
    407 {
    408 	size_t i;
    409 
    410 	KASSERT(nintr > 0);
    411 	KASSERT(intr_evcnts != NULL);
    412 
    413 	for (i = 0; i < nintr; i++) {
    414 		intr_evcnts[i].ev_count = intrcnt[i];
    415 	}
    416 }
    417 #endif
    418