summaryrefslogtreecommitdiff
path: root/kern/perfmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'kern/perfmon.c')
-rw-r--r--kern/perfmon.c58
1 files changed, 44 insertions, 14 deletions
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);
}
}