summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2013-03-12 09:37:17 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2013-03-12 09:37:17 +0000
commitf0edf71d0715678b64251c85b679f3192b0020d8 (patch)
treeecfdecfd831ac54a526ebf9f664903cfddf09372 /sys/kern
parent22d6176c7a5b0d9982db2ad3b4a2e6befb249d3b (diff)
Fix kernel profiling on MP systems by using per-CPU buffers and teach
kgmon(8) to deal with them, this time without public header changes. Previously various CPUs were iterating over the same global buffer at the same time to modify it and never ended. This diff includes some ideas submited by Thor Simon to NetBSD via miod@. ok deraadt@, mikeb@, haesbaert@
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_clock.c6
-rw-r--r--sys/kern/subr_prof.c107
2 files changed, 77 insertions, 36 deletions
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index d7d51646c2c..7aec79d3e69 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_clock.c,v 1.78 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: kern_clock.c,v 1.79 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $ */
/*-
@@ -421,8 +421,8 @@ statclock(struct clockframe *frame)
/*
* Kernel statistics are just like addupc_intr, only easier.
*/
- g = &_gmonparam;
- if (g->state == GMON_PROF_ON) {
+ g = ci->ci_gmon;
+ if (g != NULL && g->state == GMON_PROF_ON) {
i = CLKF_PC(frame) - g->lowpc;
if (i < g->textsize) {
i /= HISTFRACTION * sizeof(*g->kcount);
diff --git a/sys/kern/subr_prof.c b/sys/kern/subr_prof.c
index 3786cb11df8..5289ab7f8b4 100644
--- a/sys/kern/subr_prof.c
+++ b/sys/kern/subr_prof.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_prof.c,v 1.23 2013/02/12 08:06:22 mpi Exp $ */
+/* $OpenBSD: subr_prof.c,v 1.24 2013/03/12 09:37:16 mpi Exp $ */
/* $NetBSD: subr_prof.c,v 1.12 1996/04/22 01:38:50 christos Exp $ */
/*-
@@ -49,49 +49,72 @@
#include <uvm/uvm_extern.h>
/*
- * Froms is actually a bunch of unsigned shorts indexing tos
+ * Flag to prevent CPUs from executing the mcount() monitor function
+ * until we're sure they are in a sane state.
*/
-struct gmonparam _gmonparam = { GMON_PROF_OFF };
+int gmoninit = 0;
extern char etext[];
-
void
kmstartup(void)
{
+ CPU_INFO_ITERATOR cii;
+ struct cpu_info *ci;
+ struct gmonparam *p;
+ u_long lowpc, highpc, textsize;
+ u_long kcountsize, fromssize, tossize;
+ long tolimit;
char *cp;
- struct gmonparam *p = &_gmonparam;
int size;
/*
* Round lowpc and highpc to multiples of the density we're using
* so the rest of the scaling (here and in gprof) stays in ints.
*/
- p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
- p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
- p->textsize = p->highpc - p->lowpc;
+ lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
+ highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
+ textsize = highpc - lowpc;
printf("Profiling kernel, textsize=%ld [%lx..%lx]\n",
- p->textsize, p->lowpc, p->highpc);
- p->kcountsize = p->textsize / HISTFRACTION;
- p->hashfraction = HASHFRACTION;
- p->fromssize = p->textsize / HASHFRACTION;
- p->tolimit = p->textsize * ARCDENSITY / 100;
- if (p->tolimit < MINARCS)
- p->tolimit = MINARCS;
- else if (p->tolimit > MAXARCS)
- p->tolimit = MAXARCS;
- p->tossize = p->tolimit * sizeof(struct tostruct);
- size = p->kcountsize + p->fromssize + p->tossize;
- cp = (char *)uvm_km_zalloc(kernel_map, round_page(size));
- if (cp == 0) {
- printf("No memory for profiling.\n");
- return;
+ textsize, lowpc, highpc);
+ kcountsize = textsize / HISTFRACTION;
+ fromssize = textsize / HASHFRACTION;
+ tolimit = textsize * ARCDENSITY / 100;
+ if (tolimit < MINARCS)
+ tolimit = MINARCS;
+ else if (tolimit > MAXARCS)
+ tolimit = MAXARCS;
+ tossize = tolimit * sizeof(struct tostruct);
+ size = sizeof(*p) + kcountsize + fromssize + tossize;
+
+ /* Allocate and initialize one profiling buffer per CPU. */
+ CPU_INFO_FOREACH(cii, ci) {
+ cp = km_alloc(round_page(size), &kv_any, &kp_zero, &kd_nowait);
+ if (cp == NULL) {
+ printf("No memory for profiling.\n");
+ return;
+ }
+
+ p = (struct gmonparam *)cp;
+ cp += sizeof(*p);
+ p->tos = (struct tostruct *)cp;
+ cp += tossize;
+ p->kcount = (u_short *)cp;
+ cp += kcountsize;
+ p->froms = (u_short *)cp;
+
+ p->state = GMON_PROF_OFF;
+ p->lowpc = lowpc;
+ p->highpc = highpc;
+ p->textsize = textsize;
+ p->hashfraction = HASHFRACTION;
+ p->kcountsize = kcountsize;
+ p->fromssize = fromssize;
+ p->tolimit = tolimit;
+ p->tossize = tossize;
+
+ ci->ci_gmon = p;
}
- p->tos = (struct tostruct *)cp;
- cp += p->tossize;
- p->kcount = (u_short *)cp;
- cp += p->kcountsize;
- p->froms = (u_short *)cp;
}
/*
@@ -101,14 +124,32 @@ int
sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
- struct gmonparam *gp = &_gmonparam;
- int error;
+ CPU_INFO_ITERATOR cii;
+ struct cpu_info *ci;
+ struct gmonparam *gp = NULL;
+ int error, cpuid, op;
- /* all sysctl names at this level are terminal */
- if (namelen != 1)
+ /* all sysctl names at this level are name and field */
+ if (namelen != 2)
return (ENOTDIR); /* overloaded */
- switch (name[0]) {
+ op = name[0];
+ cpuid = name[1];
+
+ CPU_INFO_FOREACH(cii, ci) {
+ if (cpuid == CPU_INFO_UNIT(ci)) {
+ gp = ci->ci_gmon;
+ break;
+ }
+ }
+
+ if (gp == NULL)
+ return (EOPNOTSUPP);
+
+ /* Assume that if we're here it is safe to execute profiling. */
+ gmoninit = 1;
+
+ switch (op) {
case GPROF_STATE:
error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
if (error)