Module Name: src Committed By: maxv Date: Fri Mar 24 19:21:07 UTC 2017
Modified Files: src/sys/arch/x86/x86: pmc.c Log Message: Handle counter overflows, and sample with 500000 events per interrupt. It's a pre-requisite for real sampling. Overflows are not yet displayed by pmc(1), but will be soon. To generate a diff of this commit: cvs rdiff -u -r1.4 -r1.5 src/sys/arch/x86/x86/pmc.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/x86/x86/pmc.c diff -u src/sys/arch/x86/x86/pmc.c:1.4 src/sys/arch/x86/x86/pmc.c:1.5 --- src/sys/arch/x86/x86/pmc.c:1.4 Fri Mar 24 18:30:44 2017 +++ src/sys/arch/x86/x86/pmc.c Fri Mar 24 19:21:06 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: pmc.c,v 1.4 2017/03/24 18:30:44 maxv Exp $ */ +/* $NetBSD: pmc.c,v 1.5 2017/03/24 19:21:06 maxv Exp $ */ /* * Copyright (c) 2017 The NetBSD Foundation, Inc. @@ -67,7 +67,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pmc.c,v 1.4 2017/03/24 18:30:44 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pmc.c,v 1.5 2017/03/24 19:21:06 maxv Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -82,17 +82,26 @@ __KERNEL_RCSID(0, "$NetBSD: pmc.c,v 1.4 #include <machine/pmc.h> #include <machine/cpu_counter.h> #include <machine/cputypes.h> +#include <machine/i82489reg.h> +#include <machine/i82489var.h> + +#include <x86/nmi.h> + +#define NEVENTS_SAMPLE 500000 typedef struct { bool running; uint32_t evtmsr; /* event selector MSR */ uint64_t evtval; /* event selector value */ uint32_t ctrmsr; /* counter MSR */ - uint64_t ctrval; /* initial counter value */ + uint64_t ctrinitval; /* initial counter value */ uint64_t ctrmaxval; /* maximal counter value */ uint64_t ctrmask; } pmc_state_t; +static nmi_handler_t *pmc_nmi_handle; +static uint32_t pmc_lapic_image[MAXCPUS]; + static x86_pmc_cpuval_t pmc_val_cpus[MAXCPUS] __aligned(CACHE_LINE_SIZE); static kmutex_t pmc_lock; @@ -100,31 +109,58 @@ static pmc_state_t pmc_state[PMC_NCOUNTE static int pmc_ncounters __read_mostly; static int pmc_type __read_mostly; -static void -pmc_read_cpu(void *arg1, void *arg2) +static int +pmc_nmi(const struct trapframe *tf, void *dummy) { - pmc_state_t *pmc = (pmc_state_t *)arg1; struct cpu_info *ci = curcpu(); + pmc_state_t *pmc; + size_t i; + + if (pmc_type == PMC_TYPE_NONE) { + return 0; + } + for (i = 0; i < pmc_ncounters; i++) { + pmc = &pmc_state[i]; + if (!pmc->running) { + continue; + } + /* XXX make sure it really comes from this PMC */ + break; + } + if (i == pmc_ncounters) { + return 0; + } + + /* Count the overflow, and restart the counter */ + pmc_val_cpus[cpu_index(ci)].overfl++; + wrmsr(pmc->ctrmsr, pmc->ctrinitval); - pmc_val_cpus[cpu_index(ci)].ctrval = rdmsr(pmc->ctrmsr) & pmc->ctrmask; + return 1; } static void -pmc_read(pmc_state_t *pmc) +pmc_read_cpu(void *arg1, void *arg2) { - uint64_t xc; + pmc_state_t *pmc = (pmc_state_t *)arg1; + struct cpu_info *ci = curcpu(); - xc = xc_broadcast(0, pmc_read_cpu, pmc, NULL); - xc_wait(xc); + pmc_val_cpus[cpu_index(ci)].ctrval = + (rdmsr(pmc->ctrmsr) & pmc->ctrmask) - pmc->ctrinitval; } static void pmc_apply_cpu(void *arg1, void *arg2) { pmc_state_t *pmc = (pmc_state_t *)arg1; + bool start = (bool)arg2; struct cpu_info *ci = curcpu(); - wrmsr(pmc->ctrmsr, pmc->ctrval); + if (start) { + pmc_lapic_image[cpu_index(ci)] = i82489_readreg(LAPIC_PCINT); + i82489_writereg(LAPIC_PCINT, LAPIC_DLMODE_NMI); + } + + wrmsr(pmc->ctrmsr, pmc->ctrinitval); switch (pmc_type) { case PMC_TYPE_I686: case PMC_TYPE_K7: @@ -135,15 +171,30 @@ pmc_apply_cpu(void *arg1, void *arg2) pmc_val_cpus[cpu_index(ci)].ctrval = 0; pmc_val_cpus[cpu_index(ci)].overfl = 0; + + if (!start) { + i82489_writereg(LAPIC_PCINT, pmc_lapic_image[cpu_index(ci)]); + } +} + +static void +pmc_read(pmc_state_t *pmc) +{ + uint64_t xc; + + xc = xc_broadcast(0, pmc_read_cpu, pmc, NULL); + xc_wait(xc); } static void -pmc_apply(pmc_state_t *pmc) +pmc_apply(pmc_state_t *pmc, bool start) { uint64_t xc; - xc = xc_broadcast(0, pmc_apply_cpu, pmc, NULL); + xc = xc_broadcast(0, pmc_apply_cpu, pmc, (void *)start); xc_wait(xc); + + pmc->running = start; } static void @@ -151,19 +202,17 @@ pmc_start(pmc_state_t *pmc, struct x86_p { uint64_t event, unit; - pmc->running = true; - /* * Initialize the counter MSR. */ - pmc->ctrval = args->val; + pmc->ctrinitval = pmc->ctrmaxval - NEVENTS_SAMPLE; /* * Initialize the event MSR. */ switch (pmc_type) { case PMC_TYPE_I686: - pmc->evtval = args->event | PMC6_EVTSEL_EN | + pmc->evtval = args->event | PMC6_EVTSEL_EN | PMC6_EVTSEL_INT | (args->unit << PMC6_EVTSEL_UNIT_SHIFT) | ((args->flags & PMC_SETUP_KERNEL) ? PMC6_EVTSEL_OS : 0) | ((args->flags & PMC_SETUP_USER) ? PMC6_EVTSEL_USR : 0) | @@ -176,7 +225,7 @@ pmc_start(pmc_state_t *pmc, struct x86_p event = (args->event & K7_EVTSEL_EVENT); unit = (args->unit << K7_EVTSEL_UNIT_SHIFT) & K7_EVTSEL_UNIT; - pmc->evtval = event | unit | K7_EVTSEL_EN | + pmc->evtval = event | unit | K7_EVTSEL_EN | K7_EVTSEL_INT | ((args->flags & PMC_SETUP_KERNEL) ? K7_EVTSEL_OS : 0) | ((args->flags & PMC_SETUP_USER) ? K7_EVTSEL_USR : 0) | ((args->flags & PMC_SETUP_EDGE) ? K7_EVTSEL_E : 0) | @@ -192,7 +241,7 @@ pmc_start(pmc_state_t *pmc, struct x86_p F10H_EVTSEL_EVENT_SHIFT_HIGH); unit = (args->unit << F10H_EVTSEL_UNIT_SHIFT) & F10H_EVTSEL_UNIT_MASK; - pmc->evtval = event | unit | F10H_EVTSEL_EN | + pmc->evtval = event | unit | F10H_EVTSEL_EN | F10H_EVTSEL_INT | ((args->flags & PMC_SETUP_KERNEL) ? F10H_EVTSEL_OS : 0) | ((args->flags & PMC_SETUP_USER) ? F10H_EVTSEL_USR : 0) | ((args->flags & PMC_SETUP_EDGE) ? F10H_EVTSEL_EDGE : 0) | @@ -204,16 +253,15 @@ pmc_start(pmc_state_t *pmc, struct x86_p /* * Apply the changes. */ - pmc_apply(pmc); + pmc_apply(pmc, true); } static void pmc_stop(pmc_state_t *pmc, struct x86_pmc_startstop_args *args) { - pmc->running = false; pmc->evtval = 0; - pmc->ctrval = 0; - pmc_apply(pmc); + pmc->ctrinitval = 0; + pmc_apply(pmc, false); } void @@ -269,6 +317,7 @@ pmc_init(void) } } + pmc_nmi_handle = nmi_establish(pmc_nmi, NULL); mutex_init(&pmc_lock, MUTEX_DEFAULT, IPL_NONE); }