From 0274c8726015e83c989aac905b6626e40397495c Mon Sep 17 00:00:00 2001 From: Remy Noel Date: Sun, 8 Apr 2018 12:29:41 +0200 Subject: kern/perfmon: Handle overflow on sync. Add new perfmon driver method "get_pmc_width". --- arch/x86/machine/pmu_amd.c | 7 ++++++ arch/x86/machine/pmu_intel.c | 9 +++++++ kern/perfmon.c | 58 +++++++++++++++++++++++++++++++++----------- kern/perfmon.h | 1 + 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/arch/x86/machine/pmu_amd.c b/arch/x86/machine/pmu_amd.c index 0f8b14a..1c6545e 100644 --- a/arch/x86/machine/pmu_amd.c +++ b/arch/x86/machine/pmu_amd.c @@ -197,6 +197,12 @@ pmu_amd_read(unsigned int pmc_id) return cpu_get_msr64(PMU_AMD_MSR_PERCTR0 + pmc_id); } +static uint8_t +pmu_amd_get_pmc_width(void) +{ + return PMU_AMD_PMC_WIDTH; +} + static int __init pmu_amd_setup(void) { @@ -225,6 +231,7 @@ pmu_amd_setup(void) pmu_driver.start = pmu_amd_start; pmu_driver.stop = pmu_amd_stop; pmu_driver.read = pmu_amd_read; + pmu_driver.get_pmc_width = pmu_amd_get_pmc_width; return perfmon_pmu_register(&pmu_driver); } diff --git a/arch/x86/machine/pmu_intel.c b/arch/x86/machine/pmu_intel.c index 26647af..fcebb06 100644 --- a/arch/x86/machine/pmu_intel.c +++ b/arch/x86/machine/pmu_intel.c @@ -226,7 +226,15 @@ pmu_intel_read(unsigned int pmc_id) return cpu_get_msr64(PMU_INTEL_MSR_PMC0 + pmc_id); } +static uint8_t +pmu_intel_get_pmc_width(void) +{ + struct pmu_intel *pmu; + pmu = pmu_intel_get(); + + return pmu->pmc_width; +} static int __init pmu_intel_setup(void) @@ -273,6 +281,7 @@ pmu_intel_setup(void) pmu_driver.start = pmu_intel_start; pmu_driver.stop = pmu_intel_stop; pmu_driver.read = pmu_intel_read; + pmu_driver.get_pmc_width = pmu_intel_get_pmc_width; return perfmon_pmu_register(&pmu_driver); } diff --git a/kern/perfmon.c b/kern/perfmon.c index 5627726..d5d6589 100644 --- a/kern/perfmon.c +++ b/kern/perfmon.c @@ -54,6 +54,7 @@ struct perfmon_event { uint64_t count; uint64_t prev; + uint64_t overflow_id; struct list node; int flags; unsigned int type; @@ -167,6 +168,8 @@ struct perfmon_pmu { */ struct perfmon_cpu_pmc { unsigned int nr_refs; + uint64_t prev_value; + uint64_t overflow_id; }; /* @@ -379,14 +382,15 @@ perfmon_cpu_pmu_init(struct perfmon_cpu_pmu *cpu_pmu) unsigned int i; for (i = 0; i < ARRAY_SIZE(cpu_pmu->pmcs); i++) { - cpu_pmu->pmcs[i].nr_refs = 0; - } -} + struct perfmon_cpu_pmu *cpu_pmc; -static inline struct perfmon_cpu_pmc * -perfmon_cpu_pmu_get_pmc(struct perfmon_cpu_pmu *cpu_pmu, unsigned int index) -{ - return &cpu_pmu->pmcs[index]; + cpu_pmu = &cpu_pmu->pmcs[i]; + + pmc.nr_refs = 0; + pmc->prev_value = pmu_driver.read(perfmon_pmu.pmcs[i].id); + pmc->overflow_id = 0; + + } } static void @@ -394,7 +398,7 @@ perfmon_cpu_pmu_load(struct perfmon_cpu_pmu *cpu_pmu, unsigned int pmc_index) { struct perfmon_cpu_pmc *cpu_pmc; - cpu_pmc = perfmon_cpu_pmu_get_pmc(cpu_pmu, pmc_index); + cpu_pmc = cpu_pmu->pmcs[pmc_index]; if (cpu_pmc->nr_refs == 0) { pmu_driver.start(perfmon_pmu.pmcs[pmc_index].id, @@ -422,7 +426,8 @@ int perfmon_pmu_register(struct perfmon_pmu_ops *driver) { assert(driver->info && driver->translate && driver->alloc - && driver->free && driver->start && driver->stop); + && driver->free && driver->start && driver->stop + && driver->get_pmc_width); if (pmu_driver.info) { /* Already initialized */ @@ -551,16 +556,36 @@ perfmon_event_reset(struct perfmon_event *event) } static void -perfmon_event_sync(struct perfmon_event *event) +perfmon_event_sync(struct perfmon_cpu_pmu *cpu_pmu, + struct perfmon_event *event) { struct perfmon_pmc *pmc; + struct perfmon_cpu_pmc *cpu_pmc; uint64_t count; pmc = perfmon_pmc_from_index(event->pmc_index); + cpu_pmc = cpu_pmu->pmcs[event->pmc_index]; count = pmu_driver.read(pmc->id); - /* TODO: overflow managment. */ - event->count += (count - event->prev); + + if (unlikely(event->overflow_id != cpu_pmc->overflow_id)) { + int diff; + + 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.get_pmc_width()) * diff + - event->prev + count; + event->overflow_id = cpu_pmc->overflow_id; + } else { + event->count += (count - event->prev); + } event->prev = count; + + /* Update per cpu prev value. we should use a callback "on_sync" or so + * instead as it is only necessary for certain archtectural overflow + * management.*/ + cpu_pmc->prev_value = count; } static inline int @@ -830,6 +855,11 @@ perfmon_group_load(struct perfmon_group *group) list_for_each_entry(&group->events, event, node) { perfmon_cpu_pmu_load(cpu_pmu, event->pmc_index); event->prev = pmu_driver.read(perfmon_pmu.pmcs[event->pmc_index].id); + event->overflow_id = cpu_pmu->pmcs[event->pmc_index].overflow_id; + /* Update per cpu prev value. we should use a callback "on_sync" or so + * instead as it is only necessary for certain archtectural overflow + * management.*/ + cpu_pmu->pmcs[event->pmc_index].prev_value = event->prev; } group->cpu = cpu_id(); @@ -865,7 +895,7 @@ perfmon_group_unload(struct perfmon_group *group) list_for_each_entry(&group->events, event, node) { perfmon_cpu_pmu_unload(cpu_pmu, event->pmc_index); - perfmon_event_sync(event); + perfmon_event_sync(cpu_pmu, event); } group->flags &= ~PERFMON_GF_LOADED; @@ -993,7 +1023,7 @@ perfmon_group_sync_local(struct perfmon_group *group) * limited amount of *different* events. */ list_for_each_entry(&group->events, event, node) { - perfmon_event_sync(event); + perfmon_event_sync(cpu_pmu, event); } } diff --git a/kern/perfmon.h b/kern/perfmon.h index a899a01..6a2d9c6 100644 --- a/kern/perfmon.h +++ b/kern/perfmon.h @@ -64,6 +64,7 @@ struct perfmon_pmu_ops { void (*start)(unsigned int pmc_id, unsigned int raw_event_id); void (*stop)(unsigned int pmc_id); uint64_t (*read)(unsigned int pmc_id); + uint8_t (*get_pmc_width)(void); }; /* -- cgit v1.2.3