Home | History | Annotate | Line # | Download | only in kern
      1 /* $NetBSD: subr_evcnt.c,v 1.17 2021/04/17 00:05:31 mrg 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.17 2021/04/17 00:05:31 mrg 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 typedef char ev_strings[2*EVCNT_STRING_MAX];
    218 
    219 static size_t
    220 sysctl_fillevcnt(const struct evcnt *ev, struct evcnt_sysctl *evs,
    221 	size_t *copylenp)
    222 {
    223 	const bool allowaddr = get_expose_address(curproc);
    224 	const size_t copylen = offsetof(struct evcnt_sysctl, ev_strings)
    225 	    + ev->ev_grouplen + 1 + ev->ev_namelen + 1;
    226 	const size_t len = roundup2(copylen, sizeof(uint64_t));
    227 
    228 	if (evs != NULL) {
    229 		evs->ev_count = ev->ev_count;
    230 		COND_SET_VALUE(evs->ev_addr, PTRTOUINT64(ev), allowaddr);
    231 		COND_SET_VALUE(evs->ev_parent, PTRTOUINT64(ev->ev_parent),
    232 		    allowaddr);
    233 		evs->ev_type = ev->ev_type;
    234 		evs->ev_grouplen = ev->ev_grouplen;
    235 		evs->ev_namelen = ev->ev_namelen;
    236 		evs->ev_len = len / sizeof(uint64_t);
    237 		strcpy(evs->ev_strings, ev->ev_group);
    238 		strcpy(evs->ev_strings + ev->ev_grouplen + 1, ev->ev_name);
    239 	}
    240 
    241 	*copylenp = copylen;
    242 	return len;
    243 }
    244 
    245 static int
    246 sysctl_doevcnt(SYSCTLFN_ARGS)
    247 {
    248 	struct evcnt_sysctl *evs0 = NULL, *evs;
    249 	const size_t xevcnt_size = sizeof(*evs0) + sizeof(ev_strings);
    250 	const struct evcnt *ev;
    251 	int error;
    252 	int retries;
    253 	size_t needed, len;
    254 	char *dp;
    255 
    256         if (namelen == 1 && name[0] == CTL_QUERY)
    257                 return (sysctl_query(SYSCTLFN_CALL(rnode)));
    258 
    259 	if (namelen != 2)
    260 		return (EINVAL);
    261 
    262 	/*
    263 	 * We can filter on the type of evcnt.
    264 	 */
    265 	const int filter = name[0];
    266 	if (filter != EVCNT_TYPE_ANY
    267 	    && filter != EVCNT_TYPE_MISC
    268 	    && filter != EVCNT_TYPE_INTR
    269 	    && filter != EVCNT_TYPE_TRAP)
    270 		return (EINVAL);
    271 
    272 	const u_int count = name[1];
    273 	if (count != KERN_EVCNT_COUNT_ANY
    274 	    && count != KERN_EVCNT_COUNT_NONZERO)
    275 		return (EINVAL);
    276 
    277 	sysctl_unlock();
    278 
    279 	if (oldp != NULL)
    280 		evs0 = kmem_zalloc(xevcnt_size, KM_SLEEP);
    281 
    282 	retries = 100;
    283  retry:
    284 	dp = oldp;
    285 	len = (oldp != NULL) ? *oldlenp : 0;
    286 	evs = evs0;
    287 	error = 0;
    288 	needed = 0;
    289 
    290 	mutex_enter(&evcnt_lock);
    291 #ifdef __HAVE_LEGACY_INTRCNT
    292 	evcnt_update_intrcnt();
    293 #endif
    294 	TAILQ_FOREACH(ev, &allevents, ev_list) {
    295 		if (filter != EVCNT_TYPE_ANY && filter != ev->ev_type)
    296 			continue;
    297 		if (count == KERN_EVCNT_COUNT_NONZERO && ev->ev_count == 0)
    298 			continue;
    299 
    300 		/*
    301 		 * Prepare to copy.  If evs is NULL, fillevcnt will just
    302 		 * how big the item is.
    303 		 */
    304 		size_t copylen;
    305 		const size_t elem_size = sysctl_fillevcnt(ev, evs, &copylen);
    306 		needed += elem_size;
    307 
    308 		if (len < elem_size) {
    309 			evs = NULL;
    310 			continue;
    311 		}
    312 
    313 		KASSERT(evs != NULL);
    314 		KASSERT(evs->ev_grouplen != 0);
    315 		KASSERT(evs->ev_namelen != 0);
    316 		KASSERT(evs->ev_strings[0] != 0);
    317 
    318 		const uint32_t last_generation = evcnt_generation;
    319 		mutex_exit(&evcnt_lock);
    320 
    321 		/*
    322 		 * Only copy the actual number of bytes, not the rounded
    323 		 * number.  If we did the latter we'd have to zero them
    324 		 * first or we'd leak random kernel memory.
    325 		 */
    326 		error = copyout(evs, dp, copylen);
    327 
    328 		mutex_enter(&evcnt_lock);
    329 		if (error)
    330 			break;
    331 
    332 		if (__predict_false(last_generation != evcnt_generation)) {
    333 			/*
    334 			 * This sysctl node is only for statistics.
    335 			 * Retry; if the queue keeps changing, then
    336 			 * bail out.
    337 			 */
    338 			if (--retries == 0) {
    339 				error = EAGAIN;
    340 				break;
    341 			}
    342 			mutex_exit(&evcnt_lock);
    343 			goto retry;
    344 		}
    345 
    346 		/*
    347 		 * Now we deal with the pointer/len since we aren't going to
    348 		 * toss their values away.
    349 		 */
    350 		dp += elem_size;
    351 		len -= elem_size;
    352 	}
    353 	mutex_exit(&evcnt_lock);
    354 
    355 	if (evs0 != NULL)
    356 		kmem_free(evs0, xevcnt_size);
    357 
    358 	sysctl_relock();
    359 
    360 	*oldlenp = needed;
    361 	if (oldp == NULL)
    362 		*oldlenp += 1024;
    363 
    364 	return (error);
    365 }
    366 
    367 
    368 
    369 SYSCTL_SETUP(sysctl_evcnt_setup, "sysctl kern.evcnt subtree setup")
    370 {
    371 
    372 	sysctl_createv(clog, 0, NULL, NULL,
    373 		       CTLFLAG_PERMANENT,
    374 		       CTLTYPE_STRUCT, "evcnt",
    375 		       SYSCTL_DESCR("Kernel evcnt information"),
    376 		       sysctl_doevcnt, 0, NULL, 0,
    377 		       CTL_KERN, KERN_EVCNT, CTL_EOL);
    378 }
    379 
    380 #ifdef __HAVE_LEGACY_INTRCNT
    381 extern u_int intrcnt[], eintrcnt[];
    382 extern char intrnames[];
    383 static size_t nintr;
    384 struct evcnt *intr_evcnts;
    385 /*
    386  * Remove the following when the last intrcnt/intrnames user is cleaned up.
    387  */
    388 void
    389 evcnt_attach_legacy_intrcnt(void)
    390 {
    391 	size_t i;
    392 	const char *cp;
    393 
    394 	nintr = ((intptr_t)eintrcnt - (intptr_t)intrcnt) / sizeof(intrcnt[0]);
    395 	intr_evcnts = kmem_alloc(sizeof(struct evcnt) * nintr, KM_SLEEP);
    396 	for (cp = intrnames, i = 0; i < nintr; i++) {
    397 		evcnt_attach_dynamic(&intr_evcnts[i], EVCNT_TYPE_INTR,
    398 		    NULL, "cpu", cp);
    399 		cp += strlen(cp) + 1;
    400 	}
    401 }
    402 
    403 static void
    404 evcnt_update_intrcnt(void)
    405 {
    406 	size_t i;
    407 
    408 	KASSERT(nintr > 0);
    409 	KASSERT(intr_evcnts != NULL);
    410 
    411 	for (i = 0; i < nintr; i++) {
    412 		intr_evcnts[i].ev_count = intrcnt[i];
    413 	}
    414 }
    415 #endif
    416