Skip to content

Commit 9622e67

Browse files
sandip4nPeter Zijlstra
authored andcommitted
perf/x86/amd/core: Add PerfMonV2 counter control
If AMD Performance Monitoring Version 2 (PerfMonV2) is supported, use a new scheme to manage the Core PMCs using the new global control and status registers. This will be bypassed on unsupported hardware (x86_pmu.version < 2). Currently, all PMCs have dedicated control (PERF_CTL) and counter (PERF_CTR) registers. For a given PMC, the enable (En) bit of its PERF_CTL register is used to start or stop counting. The Performance Counter Global Control (PerfCntrGlobalCtl) register has enable (PerfCntrEn) bits for each PMC. For a PMC to start counting, both PERF_CTL and PerfCntrGlobalCtl enable bits must be set. If either of those are cleared, the PMC stops counting. In x86_pmu_{en,dis}able_all(), the PERF_CTL registers of all active PMCs are written to in a loop. Ideally, PMCs counting the same event that were started and stopped at the same time should record the same counts. Due to delays in between writes to the PERF_CTL registers across loop iterations, the PMCs cannot be enabled or disabled at the same instant and hence, record slightly different counts. This is fixed by enabling or disabling all active PMCs at the same time with a single write to the PerfCntrGlobalCtl register. Signed-off-by: Sandipan Das <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/dfe8e934074aaabc6ba748dfaccd0a77c974bb82.1650515382.git.sandipan.das@amd.com
1 parent 56e026a commit 9622e67

File tree

1 file changed

+45
-5
lines changed

1 file changed

+45
-5
lines changed

arch/x86/events/amd/core.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,11 @@ static void amd_pmu_cpu_dead(int cpu)
664664
amd_pmu_cpu_reset(cpu);
665665
}
666666

667+
static inline void amd_pmu_set_global_ctl(u64 ctl)
668+
{
669+
wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, ctl);
670+
}
671+
667672
/*
668673
* When a PMC counter overflows, an NMI is used to process the event and
669674
* reset the counter. NMI latency can result in the counter being updated
@@ -693,15 +698,11 @@ static void amd_pmu_wait_on_overflow(int idx)
693698
}
694699
}
695700

696-
static void amd_pmu_disable_all(void)
701+
static void amd_pmu_check_overflow(void)
697702
{
698703
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
699704
int idx;
700705

701-
amd_brs_disable_all();
702-
703-
x86_pmu_disable_all();
704-
705706
/*
706707
* This shouldn't be called from NMI context, but add a safeguard here
707708
* to return, since if we're in NMI context we can't wait for an NMI
@@ -748,6 +749,26 @@ static void amd_pmu_enable_all(int added)
748749
}
749750
}
750751

752+
static void amd_pmu_v2_enable_event(struct perf_event *event)
753+
{
754+
struct hw_perf_event *hwc = &event->hw;
755+
756+
/*
757+
* Testing cpu_hw_events.enabled should be skipped in this case unlike
758+
* in x86_pmu_enable_event().
759+
*
760+
* Since cpu_hw_events.enabled is set only after returning from
761+
* x86_pmu_start(), the PMCs must be programmed and kept ready.
762+
* Counting starts only after x86_pmu_enable_all() is called.
763+
*/
764+
__x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE);
765+
}
766+
767+
static void amd_pmu_v2_enable_all(int added)
768+
{
769+
amd_pmu_set_global_ctl(amd_pmu_global_cntr_mask);
770+
}
771+
751772
static void amd_pmu_disable_event(struct perf_event *event)
752773
{
753774
x86_pmu_disable_event(event);
@@ -765,6 +786,20 @@ static void amd_pmu_disable_event(struct perf_event *event)
765786
amd_pmu_wait_on_overflow(event->hw.idx);
766787
}
767788

789+
static void amd_pmu_disable_all(void)
790+
{
791+
amd_brs_disable_all();
792+
x86_pmu_disable_all();
793+
amd_pmu_check_overflow();
794+
}
795+
796+
static void amd_pmu_v2_disable_all(void)
797+
{
798+
/* Disable all PMCs */
799+
amd_pmu_set_global_ctl(0);
800+
amd_pmu_check_overflow();
801+
}
802+
768803
static void amd_pmu_add_event(struct perf_event *event)
769804
{
770805
if (needs_branch_stack(event))
@@ -1216,6 +1251,11 @@ static int __init amd_core_pmu_init(void)
12161251
x86_pmu.num_counters = ebx.split.num_core_pmc;
12171252

12181253
amd_pmu_global_cntr_mask = (1ULL << x86_pmu.num_counters) - 1;
1254+
1255+
/* Update PMC handling functions */
1256+
x86_pmu.enable_all = amd_pmu_v2_enable_all;
1257+
x86_pmu.disable_all = amd_pmu_v2_disable_all;
1258+
x86_pmu.enable = amd_pmu_v2_enable_event;
12191259
}
12201260

12211261
/*

0 commit comments

Comments
 (0)