diff options
-rw-r--r-- | arch/x86/machine/pmu_amd.c | 29 | ||||
-rw-r--r-- | arch/x86/machine/pmu_intel.c | 46 | ||||
-rw-r--r-- | kern/perfmon.c | 64 | ||||
-rw-r--r-- | kern/perfmon.h | 17 |
4 files changed, 84 insertions, 72 deletions
diff --git a/arch/x86/machine/pmu_amd.c b/arch/x86/machine/pmu_amd.c index 105ba50..34df620 100644 --- a/arch/x86/machine/pmu_amd.c +++ b/arch/x86/machine/pmu_amd.c @@ -237,18 +237,12 @@ pmu_amd_handle_of_intr_v1(void) } } -static uint8_t -pmu_amd_get_pmc_width(void) -{ - return PMU_AMD_PMC_WIDTH; -} - static int __init pmu_amd_setup(void) { const struct cpu *cpu; struct pmu_amd *pmu; - struct perfmon_pmu_ops pmu_driver; + struct perfmon_pmu_driver pmu_driver; cpu = cpu_current(); @@ -264,16 +258,17 @@ pmu_amd_setup(void) pmu = pmu_amd_get(); pmu->pmc_bm = (1U << PMU_AMD_NR_PMCS) - 1; - pmu_driver.info = pmu_amd_info; - pmu_driver.translate = pmu_amd_translate; - pmu_driver.alloc = pmu_amd_alloc; - pmu_driver.free = pmu_amd_free; - pmu_driver.start = pmu_amd_start; - pmu_driver.stop = pmu_amd_stop; - pmu_driver.read = pmu_amd_read; - pmu_driver.write = pmu_amd_write; - pmu_driver.get_pmc_width = pmu_amd_get_pmc_width; - pmu_driver.handle_of_intr = pmu_amd_handle_of_intr_v1; + pmu_driver.pmc_width = PMU_AMD_PMC_WIDTH; + pmu_driver.of_max_ticks = 1UL << (pmu_driver.pmc_width - 1); + + pmu_driver.ops.info = pmu_amd_info; + pmu_driver.ops.translate = pmu_amd_translate; + pmu_driver.ops.alloc = pmu_amd_alloc; + pmu_driver.ops.free = pmu_amd_free; + pmu_driver.ops.start = pmu_amd_start; + pmu_driver.ops.stop = pmu_amd_stop; + pmu_driver.ops.read = pmu_amd_read; + pmu_driver.ops.write = pmu_amd_write; return perfmon_pmu_register(&pmu_driver); } diff --git a/arch/x86/machine/pmu_intel.c b/arch/x86/machine/pmu_intel.c index f8168bb..ccc2294 100644 --- a/arch/x86/machine/pmu_intel.c +++ b/arch/x86/machine/pmu_intel.c @@ -174,9 +174,10 @@ pmu_intel_info(void) pmu = pmu_intel_get(); nr_events = pmu_popcount(pmu->events); - log_info("pmu: driver: intel, architectural v1\n" + log_info("pmu: driver: intel, architectural v%d\n" "pmu: nr_pmcs: %u, pmc_width: %u, events: %#x, nr_events: %u\n", - pmu->nr_pmcs, pmu->pmc_width, pmu->events, nr_events); + pmu->version, pmu->nr_pmcs, pmu->pmc_width, pmu->events, + nr_events); } static int @@ -342,22 +343,12 @@ pmu_intel_handle_of_intr_v2(void) } } -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) { const struct cpu *cpu; struct pmu_intel *pmu; - struct perfmon_pmu_ops pmu_driver; + struct perfmon_pmu_driver pmu_driver; unsigned int eax, ebx, ecx, edx, ev_len; cpu = cpu_current(); @@ -376,7 +367,7 @@ pmu_intel_setup(void) pmu->version = eax & PMU_INTEL_ID_VERSION_MASK; /* TODO Check this */ - if ((pmu->version == 0) || (pmu->version > 3)) { + if (pmu->version == 0) { return ENODEV; } @@ -391,20 +382,21 @@ pmu_intel_setup(void) pmu->events = ~ebx & ((1U << ev_len) - 1); - pmu_driver.info = pmu_intel_info; - pmu_driver.translate = pmu_intel_translate; - pmu_driver.alloc = pmu_intel_alloc; - pmu_driver.free = pmu_intel_free; - pmu_driver.start = pmu_intel_start; - pmu_driver.stop = pmu_intel_stop; - pmu_driver.read = pmu_intel_read; - pmu_driver.write = pmu_intel_write; - pmu_driver.get_pmc_width = pmu_intel_get_pmc_width; - - if (pmu->version > 2) { - pmu_driver.handle_of_intr = pmu_intel_handle_of_intr_v1; + pmu_driver.pmc_width = pmu->pmc_width; + pmu_driver.ops.info = pmu_intel_info; + pmu_driver.ops.translate = pmu_intel_translate; + pmu_driver.ops.alloc = pmu_intel_alloc; + pmu_driver.ops.free = pmu_intel_free; + pmu_driver.ops.start = pmu_intel_start; + pmu_driver.ops.stop = pmu_intel_stop; + pmu_driver.ops.read = pmu_intel_read; + pmu_driver.ops.write = pmu_intel_write; + + if (pmu->version >= 2) { + pmu_driver.ops.handle_of_intr = pmu_intel_handle_of_intr_v2; + pmu_driver.of_max_ticks = 0; } else { - pmu_driver.handle_of_intr = pmu_intel_handle_of_intr_v2; + pmu_driver.of_max_ticks = 1UL << (pmu_driver.pmc_width - 1); } return perfmon_pmu_register(&pmu_driver); diff --git a/kern/perfmon.c b/kern/perfmon.c index 156ec7a..17175ca 100644 --- a/kern/perfmon.c +++ b/kern/perfmon.c @@ -16,7 +16,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * * - * TODO Description. + * The perfomance monitoring modules allows to manage performance monitoring as + * event groups. Each physical performance monitoring counter (pmc) may be + * referenced by perfmon events, which are theelves groupped in perfmon groups. + * Groups can then be attached to either threads or cpus into perfmon + * grouplists. + * + * In order to guarantee that thread relocation, is properly handled, events + * types are reseved on perfomance monitoring units (pmu) for all cpus for every + * event of a group when it is attached. Therefore a group attach may fail if no + * compatible pmc is available globally. * * Locking order : interrupts -> thread runq -> grouplist -> group * @@ -188,9 +197,11 @@ struct perfmon_cpu_pmc { */ struct perfmon_cpu_pmu { struct perfmon_cpu_pmc pmcs[PERFMON_MAX_PMCS]; + struct timer of_timer; + unsigned int cpu_id; }; -static struct perfmon_pmu_ops pmu_driver __read_mostly; +static struct perfmon_pmu_driver pmu_driver __read_mostly; static struct perfmon_pmu perfmon_pmu; static unsigned int perfmon_pmc_id_to_index[PERFMON_MAX_PMCS]; @@ -216,7 +227,7 @@ perfmon_translate(unsigned int *raw_event_idp, unsigned int event_type, *raw_event_idp = event_id; return 0; case PERFMON_ET_GENERIC: - return pmu_driver.translate(raw_event_idp, event_id); + return pmu_driver.ops.translate(raw_event_idp, event_id); default: panic("perfmon: unsupported event type"); } @@ -242,7 +253,7 @@ perfmon_pmc_alloc(struct perfmon_pmc **pmcp, unsigned int raw_event_id) } assert(i < ARRAY_SIZE(perfmon_pmu.pmcs)); - error = pmu_driver.alloc(&pmc->id, raw_event_id); + error = pmu_driver.ops.alloc(&pmc->id, raw_event_id); if (error) { return error; @@ -345,7 +356,7 @@ perfmon_pmc_put(struct perfmon_pmc *pmc) pmc->nr_refs--; if (pmc->nr_refs == 0) { - pmu_driver.free(pmc->id); + pmu_driver.ops.free(pmc->id); assert(perfmon_pmc_id_to_index[pmc->id] != UINT32_MAX); perfmon_pmc_id_to_index[pmc->id] = UINT32_MAX; } @@ -394,7 +405,7 @@ perfmon_cpu_pmu_init(struct perfmon_cpu_pmu *cpu_pmu) pmc = &cpu_pmu->pmcs[i]; pmc->nr_refs = 0; - pmc->prev_value = pmu_driver.read(perfmon_pmu.pmcs[i].id); + pmc->prev_value = pmu_driver.ops.read(perfmon_pmu.pmcs[i].id); pmc->overflow_id = 0; } @@ -458,8 +469,8 @@ perfmon_cpu_pmu_load(struct perfmon_cpu_pmu *cpu_pmu, unsigned int pmc_index) cpu_pmc = &cpu_pmu->pmcs[pmc_index]; if (cpu_pmc->nr_refs == 0) { - pmu_driver.start(perfmon_pmu.pmcs[pmc_index].id, - perfmon_pmu.pmcs[pmc_index].raw_event_id); + pmu_driver.ops.start(perfmon_pmu.pmcs[pmc_index].id, + perfmon_pmu.pmcs[pmc_index].raw_event_id); } cpu_pmc->nr_refs++; @@ -475,24 +486,26 @@ perfmon_cpu_pmu_unload(struct perfmon_cpu_pmu *cpu_pmu, unsigned int pmc_index) cpu_pmc->nr_refs--; if (cpu_pmc->nr_refs == 0) { - pmu_driver.stop(perfmon_pmu.pmcs[pmc_index].id); + pmu_driver.ops.stop(perfmon_pmu.pmcs[pmc_index].id); } } void perfmon_of_intr(void) { - pmu_driver.handle_of_intr(); + pmu_driver.ops.handle_of_intr(); } int -perfmon_pmu_register(struct perfmon_pmu_ops *driver) +perfmon_pmu_register(struct perfmon_pmu_driver *driver) { - assert(driver->info && driver->translate && driver->alloc - && driver->free && driver->start && driver->stop - && driver->get_pmc_width); + struct perfmon_pmu_ops *ops = &driver->ops; + + assert(ops->info && ops->translate && ops->alloc + && ops->free && ops->start && ops->stop); + assert(!ops->handle_of_intr != !driver->of_max_ticks); - if (pmu_driver.info) { + if (pmu_driver.ops.info) { /* Already initialized */ assert(0); return EINVAL; @@ -545,15 +558,14 @@ perfmon_setup(void) percpu_var(perfmon_cpu_grouplist, i) = grouplist; } - if (!pmu_driver.info) { + if (!pmu_driver.ops.info) { log_err("unable to start perfmon: no compatible pmu driver available"); return ENODEV; } - pmu_driver.info(); - if (pmu_driver.handle_of_intr) { + 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); - } else { - log_warning("registered pmu does not handle overflow\n"); } return 0; @@ -623,7 +635,7 @@ perfmon_event_read(const struct perfmon_event *event) int perfmon_event_write(struct perfmon_event *event, uint64_t value) { - if (!pmu_driver.write) { + if (!pmu_driver.ops.write) { return ENODEV; } event->value = value; @@ -635,7 +647,7 @@ perfmon_event_write(struct perfmon_event *event, uint64_t value) int perfmon_get_pmc_width(void) { - return pmu_driver.get_pmc_width(); + return pmu_driver.pmc_width; } #endif /* CONFIG_PERFMON_TEST */ @@ -657,14 +669,14 @@ perfmon_event_sync(struct perfmon_cpu_pmu *cpu_pmu, pmc = perfmon_pmc_from_index(event->pmc_index); cpu_pmc = &cpu_pmu->pmcs[event->pmc_index]; - count = pmu_driver.read(pmc->id); + count = pmu_driver.ops.read(pmc->id); 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.get_pmc_width()) * diff + event->count += (1UL << pmu_driver.pmc_width) * diff - event->prev + count; event->overflow_id = cpu_pmc->overflow_id; } else { @@ -949,14 +961,14 @@ perfmon_group_load(struct perfmon_group *group) continue; } pmc = perfmon_pmc_from_index(event->pmc_index); - pmu_driver.write(pmc->id, event->value); + pmu_driver.ops.write(pmc->id, event->value); event->set_value = false; } #endif 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->prev = pmu_driver.ops.read(perfmon_pmu.pmcs[event->pmc_index].id); event->overflow_id = cpu_pmu->pmcs[event->pmc_index].overflow_id; } diff --git a/kern/perfmon.h b/kern/perfmon.h index b9a3882..cd7eb3a 100644 --- a/kern/perfmon.h +++ b/kern/perfmon.h @@ -65,11 +65,24 @@ struct perfmon_pmu_ops { void (*stop)(unsigned int pmc_id); uint64_t (*read)(unsigned int pmc_id); void (*write)(unsigned int pmc_id, uint64_t value); - uint8_t (*get_pmc_width)(void); + /* If set, of_max_ticks should be set to 0. */ void (*handle_of_intr)(void); }; /* + * Pmu device description. + */ +struct perfmon_pmu_driver { + uint8_t pmc_width; /* width in bits of a pmc */ + /* + * Maximum number of clock ticks between two overflow ckecks. + * Should be set to 0 if handle_of_intr is set. + */ + uint64_t of_max_ticks; + struct perfmon_pmu_ops ops; +}; + +/* * Performance monitoring event. * * An event describes a single, well-defined state and records its @@ -226,7 +239,7 @@ void perfmon_of_intr(void); /* * Register an architecture-specific driver. */ -int perfmon_pmu_register(struct perfmon_pmu_ops *driver); +int perfmon_pmu_register(struct perfmon_pmu_driver *driver); /* * Get the last value of given pmc. |