summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRemy Noel <mocramis@gmail.com>2018-04-08 12:29:41 +0200
committerRemy Noel <mocramis@gmail.com>2018-04-21 00:04:20 +0200
commit0274c8726015e83c989aac905b6626e40397495c (patch)
treed7857b4d4a8983ad2fe08339c83d0f285b45e1f6
parent7f545f440e58b081b1ff630e78903ee4ee901a5a (diff)
kern/perfmon: Handle overflow on sync.
Add new perfmon driver method "get_pmc_width".
-rw-r--r--arch/x86/machine/pmu_amd.c7
-rw-r--r--arch/x86/machine/pmu_intel.c9
-rw-r--r--kern/perfmon.c58
-rw-r--r--kern/perfmon.h1
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);
};
/*