Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys Add a sysctl to retrieve evcnts from the kernel. You ca...
details: https://anonhg.NetBSD.org/src/rev/284de030cfc9
branches: trunk
changeset: 761487:284de030cfc9
user: matt <matt%NetBSD.org@localhost>
date: Sat Jan 29 17:35:23 2011 +0000
description:
Add a sysctl to retrieve evcnts from the kernel. You can tell it to
limit to a specific type and/or all or just evcnts with non-zero counts.
diffstat:
sys/kern/subr_evcnt.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++--
sys/sys/sysctl.h | 30 +++++++-
2 files changed, 204 insertions(+), 11 deletions(-)
diffs (286 lines):
diff -r ae206b07810c -r 284de030cfc9 sys/kern/subr_evcnt.c
--- a/sys/kern/subr_evcnt.c Sat Jan 29 16:56:26 2011 +0000
+++ b/sys/kern/subr_evcnt.c Sat Jan 29 17:35:23 2011 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_evcnt.c,v 1.8 2011/01/18 08:16:43 matt Exp $ */
+/* $NetBSD: subr_evcnt.c,v 1.9 2011/01/29 17:35:24 matt Exp $ */
/*
* Copyright (c) 1996, 2000 Christopher G. Demetriou
@@ -77,17 +77,20 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_evcnt.c,v 1.8 2011/01/18 08:16:43 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_evcnt.c,v 1.9 2011/01/29 17:35:24 matt Exp $");
#include <sys/param.h>
-#include <sys/device.h>
+#include <sys/evcnt.h>
+#include <sys/kmem.h>
#include <sys/mutex.h>
+#include <sys/sysctl.h>
#include <sys/systm.h>
/* list of all events */
struct evcntlist allevents = TAILQ_HEAD_INITIALIZER(allevents);
-static kmutex_t evmtx;
+static kmutex_t evcnt_lock __cacheline_aligned;
static bool init_done;
+static uint32_t evcnt_generation;
/*
* We need a dummy object to stuff into the evcnt link set to
@@ -108,7 +111,7 @@
KASSERT(!init_done);
- mutex_init(&evmtx, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&evcnt_lock, MUTEX_DEFAULT, IPL_NONE);
init_done = true;
@@ -146,9 +149,9 @@
#endif
ev->ev_namelen = len;
- mutex_enter(&evmtx);
+ mutex_enter(&evcnt_lock);
TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
- mutex_exit(&evmtx);
+ mutex_exit(&evcnt_lock);
}
/*
@@ -186,7 +189,171 @@
evcnt_detach(struct evcnt *ev)
{
- mutex_enter(&evmtx);
+ mutex_enter(&evcnt_lock);
TAILQ_REMOVE(&allevents, ev, ev_list);
- mutex_exit(&evmtx);
+ evcnt_generation++;
+ mutex_exit(&evcnt_lock);
+}
+
+struct xevcnt_sysctl {
+ struct evcnt_sysctl evs;
+ char ev_strings[2*EVCNT_STRING_MAX];
+};
+
+static size_t
+sysctl_fillevcnt(const struct evcnt *ev, struct xevcnt_sysctl *xevs,
+ size_t *copylenp)
+{
+ const size_t copylen = offsetof(struct evcnt_sysctl, ev_strings)
+ + ev->ev_grouplen + 1 + ev->ev_namelen + 1;
+ const size_t len = roundup2(copylen, sizeof(uint64_t));
+ if (xevs != NULL) {
+ xevs->evs.ev_count = ev->ev_count;
+ xevs->evs.ev_addr = PTRTOUINT64(ev);
+ xevs->evs.ev_parent = PTRTOUINT64(ev->ev_parent);
+ xevs->evs.ev_type = ev->ev_type;
+ xevs->evs.ev_grouplen = ev->ev_grouplen;
+ xevs->evs.ev_namelen = ev->ev_namelen;
+ xevs->evs.ev_len = len / sizeof(uint64_t);
+ strcpy(xevs->evs.ev_strings, ev->ev_group);
+ strcpy(xevs->evs.ev_strings + ev->ev_grouplen + 1, ev->ev_name);
+ }
+
+ *copylenp = copylen;
+ return len;
}
+
+static int
+sysctl_doevcnt(SYSCTLFN_ARGS)
+{
+ struct xevcnt_sysctl *xevs0 = NULL, *xevs;
+ const struct evcnt *ev;
+ int error;
+ int retries;
+ size_t needed, len;
+ char *dp;
+
+ if (namelen == 1 && name[0] == CTL_QUERY)
+ return (sysctl_query(SYSCTLFN_CALL(rnode)));
+
+ if (namelen != 2)
+ return (EINVAL);
+
+ /*
+ * We can filter on the type of evcnt.
+ */
+ const int filter = name[0];
+ if (filter != EVCNT_TYPE_ANY
+ && filter != EVCNT_TYPE_MISC
+ && filter != EVCNT_TYPE_INTR
+ && filter != EVCNT_TYPE_TRAP)
+ return (EINVAL);
+
+ const u_int count = name[1];
+ if (count != KERN_EVCNT_COUNT_ANY
+ && count != KERN_EVCNT_COUNT_NONZERO)
+ return (EINVAL);
+
+ sysctl_unlock();
+
+ if (oldp != NULL && xevs0 == NULL)
+ xevs0 = kmem_alloc(sizeof(*xevs0), KM_SLEEP);
+
+ retries = 100;
+ retry:
+ dp = oldp;
+ len = (oldp != NULL) ? *oldlenp : 0;
+ xevs = xevs0;
+ error = 0;
+ needed = 0;
+
+ mutex_enter(&evcnt_lock);
+ TAILQ_FOREACH(ev, &allevents, ev_list) {
+ if (filter != EVCNT_TYPE_ANY && filter != ev->ev_type)
+ continue;
+ if (count == KERN_EVCNT_COUNT_NONZERO && ev->ev_count == 0)
+ continue;
+
+ /*
+ * Prepare to copy. If xevs is NULL, fillevcnt will just
+ * how big the item is.
+ */
+ size_t copylen;
+ const size_t elem_size = sysctl_fillevcnt(ev, xevs, ©len);
+ needed += elem_size;
+
+ if (len < elem_size) {
+ xevs = NULL;
+ continue;
+ }
+
+ KASSERT(xevs != NULL);
+ KASSERT(xevs->evs.ev_grouplen != 0);
+ KASSERT(xevs->evs.ev_namelen != 0);
+ KASSERT(xevs->evs.ev_strings[0] != 0);
+
+ const uint32_t last_generation = evcnt_generation;
+ mutex_exit(&evcnt_lock);
+
+ /*
+ * Only copy the actual number of bytes, not the rounded
+ * number. If we did the latter we'd have to zero them
+ * first or we'd leak random kernel memory.
+ */
+ error = copyout(xevs, dp, copylen);
+
+ mutex_enter(&evcnt_lock);
+ if (error)
+ break;
+
+ if (__predict_false(last_generation != evcnt_generation)) {
+ /*
+ * This sysctl node is only for statistics.
+ * Retry; if the queue keeps changing, then
+ * bail out.
+ */
+ if (--retries == 0) {
+ error = EAGAIN;
+ break;
+ }
+ mutex_exit(&evcnt_lock);
+ goto retry;
+ }
+
+ /*
+ * Now we deal with the pointer/len since we aren't going to
+ * toss their values away.
+ */
+ dp += elem_size;
+ len -= elem_size;
+ }
+ mutex_exit(&evcnt_lock);
+
+ if (xevs0 != NULL)
+ kmem_free(xevs0, sizeof(*xevs0));
+
+ sysctl_relock();
+
+ *oldlenp = needed;
+ if (oldp == NULL)
+ *oldlenp += 1024;
+
+ return (error);
+}
+
+
+
+SYSCTL_SETUP(sysctl_evcnt_setup, "sysctl kern.evcnt subtree setup")
+{
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "kern", NULL,
+ NULL, 0, NULL, 0,
+ CTL_KERN, CTL_EOL);
+ sysctl_createv(clog, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_STRUCT, "evcnt",
+ SYSCTL_DESCR("Kernel evcnt information"),
+ sysctl_doevcnt, 0, NULL, 0,
+ CTL_KERN, KERN_EVCNT, CTL_EOL);
+}
diff -r ae206b07810c -r 284de030cfc9 sys/sys/sysctl.h
--- a/sys/sys/sysctl.h Sat Jan 29 16:56:26 2011 +0000
+++ b/sys/sys/sysctl.h Sat Jan 29 17:35:23 2011 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: sysctl.h,v 1.191 2011/01/28 18:44:45 pooka Exp $ */
+/* $NetBSD: sysctl.h,v 1.192 2011/01/29 17:35:23 matt Exp $ */
/*
* Copyright (c) 1989, 1993
@@ -259,7 +259,8 @@
#define KERN_ARND 81 /* void *buf, size_t siz random */
#define KERN_SYSVIPC 82 /* node: SysV IPC parameters */
#define KERN_BOOTTIME 83 /* struct: time kernel was booted */
-#define KERN_MAXID 84 /* number of valid kern ids */
+#define KERN_EVCNT 84 /* struct: evcnts */
+#define KERN_MAXID 85 /* number of valid kern ids */
#define CTL_KERN_NAMES { \
@@ -347,6 +348,7 @@
{ "arandom", CTLTYPE_STRUCT }, \
{ "sysvipc", CTLTYPE_STRUCT }, \
{ "boottime", CTLTYPE_STRUCT }, \
+ { "evcnt", CTLTYPE_STRUCT }, \
}
/*
@@ -782,6 +784,30 @@
#define KERN_FILESLOP 10
/*
+ * kern.evcnt returns an array of these structures, which are designed both to
+ * be immune to 32/64 bit emulation issues. Note that the struct here differs
+ * from the real struct evcnt but contains the same information in order to
+ * accomodate sysctl.
+ */
+struct evcnt_sysctl {
+ uint64_t ev_count; /* current count */
+ uint64_t ev_addr; /* kernel address of evcnt */
+ uint64_t ev_parent; /* kernel address of parent */
+ uint8_t ev_type; /* EVCNT_TRAP_* */
+ uint8_t ev_grouplen; /* length of group with NUL */
+ uint8_t ev_namelen; /* length of name with NUL */
+ uint8_t ev_len; /* multiply by 8 */
+ /*
+ * Now the group and name strings follow (both include the trailing
+ * NUL). ev_name start at &ev_strings[ev_grouplen+1]
+ */
+ char ev_strings[0];
+};
+
+#define KERN_EVCNT_COUNT_ANY 0
+#define KERN_EVCNT_COUNT_NONZERO 1
+
+/*
* CTL_HW identifiers
*/
#define HW_MACHINE 1 /* string: machine class */
Home |
Main Index |
Thread Index |
Old Index