diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index 5a9f95b5cc26..da9bcdcd9856 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c @@ -286,7 +286,15 @@ static u64 get_ibs_fetch_count(u64 config) static u64 get_ibs_op_count(u64 config) { - return (config & IBS_OP_CUR_CNT) >> 32; + u64 count = 0; + + if (config & IBS_OP_VAL) + count += (config & IBS_OP_MAX_CNT) << 4; /* cnt rolled over */ + + if (ibs_caps & IBS_CAPS_RDWROPCNT) + count += (config & IBS_OP_CUR_CNT) >> 32; + + return count; } static void @@ -295,7 +303,12 @@ perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event, { u64 count = perf_ibs->get_count(*config); - while (!perf_event_try_update(event, count, 20)) { + /* + * Set width to 64 since we do not overflow on max width but + * instead on max count. In perf_ibs_set_period() we clear + * prev count manually on overflow. + */ + while (!perf_event_try_update(event, count, 64)) { rdmsrl(event->hw.config_base, *config); count = perf_ibs->get_count(*config); } @@ -374,6 +387,12 @@ static void perf_ibs_stop(struct perf_event *event, int flags) if (hwc->state & PERF_HES_UPTODATE) return; + /* + * Clear valid bit to not count rollovers on update, rollovers + * are only updated in the irq handler. + */ + config &= ~perf_ibs->valid_mask; + perf_ibs_event_update(perf_ibs, event, &config); hwc->state |= PERF_HES_UPTODATE; } @@ -488,17 +507,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) if (!(*buf++ & perf_ibs->valid_mask)) return 0; - /* - * Emulate IbsOpCurCnt in MSRC001_1033 (IbsOpCtl), not - * supported in all cpus. As this triggered an interrupt, we - * set the current count to the max count. - */ config = &ibs_data.regs[0]; - if (perf_ibs == &perf_ibs_op && !(ibs_caps & IBS_CAPS_RDWROPCNT)) { - *config &= ~IBS_OP_CUR_CNT; - *config |= (*config & IBS_OP_MAX_CNT) << 36; - } - perf_ibs_event_update(perf_ibs, event, config); perf_sample_data_init(&data, 0, hwc->last_period); if (!perf_ibs_set_period(perf_ibs, hwc, &period))