diff options
Diffstat (limited to 'kern/perfmon.c')
-rw-r--r-- | kern/perfmon.c | 91 |
1 files changed, 78 insertions, 13 deletions
diff --git a/kern/perfmon.c b/kern/perfmon.c index 17175ca..0211141 100644 --- a/kern/perfmon.c +++ b/kern/perfmon.c @@ -48,10 +48,10 @@ #include <kern/perfmon_i.h> #include <kern/spinlock.h> #include <kern/thread.h> +#include <kern/timer.h> #include <kern/xcall.h> #include <machine/cpu.h> #include <machine/pmu.h> -#include <machine/trap.h> /* * Performance monitoring event. @@ -394,10 +394,23 @@ perfmon_grouplist_destroy(struct perfmon_grouplist *grouplist) kmem_cache_free(&perfmon_grouplist_cache, grouplist); } +static void perfmon_check_of(struct timer *timer); + static void __init -perfmon_cpu_pmu_init(struct perfmon_cpu_pmu *cpu_pmu) +perfmon_cpu_pmu_init(unsigned int cpuid) { unsigned int i; + struct perfmon_cpu_pmu *cpu_pmu; + + cpu_pmu = percpu_ptr(perfmon_cpu_pmu, cpuid); + cpu_pmu->cpu_id = cpuid; + if (!pmu_driver.ops.handle_of_intr) { + /* XXX: using high prio instead or INTR because we might xcall from the + * callbacks. + */ + timer_init(&cpu_pmu->of_timer, &perfmon_check_of, TIMER_HIGH_PRIO); + timer_schedule(&cpu_pmu->of_timer, pmu_driver.of_max_ticks); + } for (i = 0; i < ARRAY_SIZE(cpu_pmu->pmcs); i++) { struct perfmon_cpu_pmc *pmc; @@ -405,7 +418,6 @@ perfmon_cpu_pmu_init(struct perfmon_cpu_pmu *cpu_pmu) pmc = &cpu_pmu->pmcs[i]; pmc->nr_refs = 0; - pmc->prev_value = pmu_driver.ops.read(perfmon_pmu.pmcs[i].id); pmc->overflow_id = 0; } @@ -462,6 +474,55 @@ perfmon_cpu_pmc_inc_of(unsigned int pmc_id) } static void +perfmon_check_of_remote(void *arg) +{ + perfmon_check_of(arg); +} + +static void +perfmon_check_pmc_of(struct perfmon_cpu_pmc *cpu_pmc, uint64_t value) +{ + uint64_t prev; + + prev = cpu_pmc->prev_value; + if (prev > value) { + /* Overflow */ + cpu_pmc->overflow_id++; + } + cpu_pmc->prev_value = value; +} + +static void +perfmon_check_of(struct timer *timer) +{ + struct perfmon_pmc *pmc; + struct perfmon_cpu_pmc *cpu_pmc; + struct perfmon_cpu_pmu *cpu_pmu; + uint64_t value; + + cpu_pmu = structof(timer, struct perfmon_cpu_pmu, of_timer); + if (cpu_pmu->cpu_id != cpu_id()) + { + xcall_call(perfmon_check_of_remote, timer, cpu_pmu->cpu_id); + return; + } + + for (size_t i = 0; i < ARRAY_SIZE(perfmon_pmu.pmcs); i++) { + pmc = perfmon_pmc_from_index(i); + if (pmc->nr_refs == 0) { + continue; + } + + cpu_pmc = &cpu_pmu->pmcs[i]; + value = pmu_driver.ops.read(pmc->id); + + perfmon_check_pmc_of(cpu_pmc, value); + } + + timer_schedule(timer, pmu_driver.of_max_ticks); +} + +static void perfmon_cpu_pmu_load(struct perfmon_cpu_pmu *cpu_pmu, unsigned int pmc_index) { struct perfmon_cpu_pmc *cpu_pmc; @@ -471,6 +532,7 @@ perfmon_cpu_pmu_load(struct perfmon_cpu_pmu *cpu_pmu, unsigned int pmc_index) if (cpu_pmc->nr_refs == 0) { pmu_driver.ops.start(perfmon_pmu.pmcs[pmc_index].id, perfmon_pmu.pmcs[pmc_index].raw_event_id); + cpu_pmc->prev_value = pmu_driver.ops.read(perfmon_pmu.pmcs[pmc_index].id); } cpu_pmc->nr_refs++; @@ -493,6 +555,7 @@ perfmon_cpu_pmu_unload(struct perfmon_cpu_pmu *cpu_pmu, unsigned int pmc_index) void perfmon_of_intr(void) { + assert(pmu_driver.ops.handle_of_intr); pmu_driver.ops.handle_of_intr(); } @@ -545,7 +608,7 @@ perfmon_setup(void) } for (i = 0; i < cpu_count(); i++) { - perfmon_cpu_pmu_init(percpu_ptr(perfmon_cpu_pmu, i)); + perfmon_cpu_pmu_init(i); } for (i = 0; i < cpu_count(); i++) { @@ -563,10 +626,6 @@ perfmon_setup(void) return ENODEV; } pmu_driver.ops.info(); - if (pmu_driver.ops.handle_of_intr) { - /* FIXME: this should not require an architectural api call. */ - trap_register(TRAP_LAPIC_PMC_OF, lapic_pmc_of_intr); - } return 0; } @@ -671,12 +730,16 @@ perfmon_event_sync(struct perfmon_cpu_pmu *cpu_pmu, cpu_pmc = &cpu_pmu->pmcs[event->pmc_index]; count = pmu_driver.ops.read(pmc->id); + if (!pmu_driver.ops.handle_of_intr) { + /* Force pmc overflow status update */ + perfmon_check_pmc_of(cpu_pmc, count); + } + if (unlikely(event->overflow_id != cpu_pmc->overflow_id)) { assert(cpu_pmc->overflow_id > event->overflow_id); - diff = cpu_pmc->overflow_id > event->overflow_id; /* diff is very likely 1. */ - event->count += (1UL << pmu_driver.pmc_width) * diff + event->count += (1ULL << pmu_driver.pmc_width) * diff - event->prev + count; event->overflow_id = cpu_pmc->overflow_id; } else { @@ -942,9 +1005,8 @@ perfmon_group_load(struct perfmon_group *group) { struct perfmon_cpu_pmu *cpu_pmu; struct perfmon_event *event; -#ifdef CONFIG_PERFMON_TEST struct perfmon_pmc *pmc; -#endif + uint64_t prev; assert(!thread_preempt_enabled()); assert(perfmon_group_enabled(group)); @@ -967,8 +1029,11 @@ perfmon_group_load(struct perfmon_group *group) #endif list_for_each_entry(&group->events, event, node) { + pmc = perfmon_pmc_from_index(event->pmc_index); + prev = pmu_driver.ops.read(pmc->id); + perfmon_cpu_pmu_load(cpu_pmu, event->pmc_index); - event->prev = pmu_driver.ops.read(perfmon_pmu.pmcs[event->pmc_index].id); + event->prev = prev; event->overflow_id = cpu_pmu->pmcs[event->pmc_index].overflow_id; } |