summaryrefslogtreecommitdiff
path: root/kern/perfmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'kern/perfmon.c')
-rw-r--r--kern/perfmon.c91
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;
}