diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-03-12 09:37:17 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-03-12 09:37:17 +0000 |
commit | f0edf71d0715678b64251c85b679f3192b0020d8 (patch) | |
tree | ecfdecfd831ac54a526ebf9f664903cfddf09372 /sys/kern | |
parent | 22d6176c7a5b0d9982db2ad3b4a2e6befb249d3b (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.c | 6 | ||||
-rw-r--r-- | sys/kern/subr_prof.c | 107 |
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) |