Skip to content

Commit bc8a6c3

Browse files
bwicaksononvgithub-actions[bot]
authored andcommitted
NVIDIA: VR: SAUCE: perf/arm_pmu: Skip PMCCNTR_EL0 on NVIDIA Olympus
PMCCNTR_EL0 may continue to increment on NVIDIA Olympus CPUs while the PE is in WFI/WFE. That does not necessarily match the CPU_CYCLES event counted by a programmable counter, so using PMCCNTR_EL0 for cycles can give results that differ from the programmable counter path. Extend the existing PMCCNTR avoidance decision from the SMT case to also cover Olympus. Store the result in the common arm_pmu state at registration time, so arm_pmuv3 can keep using a single flag when deciding whether CPU_CYCLES may use PMCCNTR_EL0. Signed-off-by: Besar Wicaksono <bwicaksono@nvidia.com> (cherry picked from https://lore.kernel.org/all/20260504175204.3122979-1-bwicaksono@nvidia.com/) Signed-off-by: Lee Trager <ltrager@nvidia.com> Acked-by: Seth Forshee <sforshee@nvidia.com> Acked-by: Matthew R. Ochs <mochs@nvidia.com> Signed-off-by: Matthew R. Ochs <mochs@nvidia.com>
1 parent 9727f6c commit bc8a6c3

3 files changed

Lines changed: 51 additions & 9 deletions

File tree

drivers/perf/arm_pmu.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,8 +943,13 @@ int armpmu_register(struct arm_pmu *pmu)
943943
/*
944944
* By this stage we know our supported CPUs on either DT/ACPI platforms,
945945
* detect the SMT implementation.
946+
* On SMT CPUs, the PMCCNTR_EL0 increments from the processor clock rather
947+
* than the PE clock (ARM DDI0487 L.b D13.1.3) which means it'll continue
948+
* counting on a WFI PE if one of its SMT sibling is not idle on a
949+
* multi-threaded implementation. So don't use it on SMT cores.
946950
*/
947-
pmu->has_smt = topology_core_has_smt(cpumask_first(&pmu->supported_cpus));
951+
pmu->avoid_pmccntr |=
952+
topology_core_has_smt(cpumask_first(&pmu->supported_cpus));
948953

949954
if (!pmu->set_event_filter)
950955
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE;

drivers/perf/arm_pmuv3.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* This code is based heavily on the ARMv7 perf event code.
99
*/
1010

11+
#include <asm/cputype.h>
1112
#include <asm/irq_regs.h>
1213
#include <asm/perf_event.h>
1314
#include <asm/virt.h>
@@ -1002,13 +1003,7 @@ static bool armv8pmu_can_use_pmccntr(struct pmu_hw_events *cpuc,
10021003
if (has_branch_stack(event))
10031004
return false;
10041005

1005-
/*
1006-
* The PMCCNTR_EL0 increments from the processor clock rather than
1007-
* the PE clock (ARM DDI0487 L.b D13.1.3) which means it'll continue
1008-
* counting on a WFI PE if one of its SMT sibling is not idle on a
1009-
* multi-threaded implementation. So don't use it on SMT cores.
1010-
*/
1011-
if (cpu_pmu->has_smt)
1006+
if (cpu_pmu->avoid_pmccntr)
10121007
return false;
10131008

10141009
return true;
@@ -1299,6 +1294,41 @@ static int armv8_vulcan_map_event(struct perf_event *event)
12991294
&armv8_vulcan_perf_cache_map);
13001295
}
13011296

1297+
#ifdef CONFIG_ARM64
1298+
/*
1299+
* List of CPUs that should avoid using PMCCNTR_EL0.
1300+
*/
1301+
static struct midr_range armv8pmu_avoid_pmccntr_cpus[] = {
1302+
/*
1303+
* The PMCCNTR_EL0 in Olympus CPU may still increment while in WFI/WFE state.
1304+
* This is an implementation specific behavior and not an erratum.
1305+
*
1306+
* From ARM DDI0487 D14.4:
1307+
* It is IMPLEMENTATION SPECIFIC whether CPU_CYCLES and PMCCNTR count
1308+
* when the PE is in WFI or WFE state, even if the clocks are not stopped.
1309+
*
1310+
* From ARM DDI0487 D24.5.2:
1311+
* All counters are subject to any changes in clock frequency, including
1312+
* clock stopping caused by the WFI and WFE instructions.
1313+
* This means that it is CONSTRAINED UNPREDICTABLE whether or not
1314+
* PMCCNTR_EL0 continues to increment when clocks are stopped by WFI and
1315+
* WFE instructions.
1316+
*/
1317+
MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS),
1318+
{}
1319+
};
1320+
1321+
static bool armv8pmu_is_in_avoid_pmccntr_cpus(void)
1322+
{
1323+
return is_midr_in_range_list(armv8pmu_avoid_pmccntr_cpus);
1324+
}
1325+
#else
1326+
static bool armv8pmu_is_in_avoid_pmccntr_cpus(void)
1327+
{
1328+
return false;
1329+
}
1330+
#endif
1331+
13021332
struct armv8pmu_probe_info {
13031333
struct arm_pmu *pmu;
13041334
bool present;
@@ -1348,6 +1378,13 @@ static void __armv8pmu_probe_pmu(void *info)
13481378
else
13491379
cpu_pmu->reg_pmmir = 0;
13501380

1381+
/*
1382+
* On some CPUs, PMCCNTR_EL0 does not match the behavior of CPU_CYCLES
1383+
* programmable counter, so avoid routing cycles through PMCCNTR_EL0 to
1384+
* prevent inconsistency in the results.
1385+
*/
1386+
cpu_pmu->avoid_pmccntr |= armv8pmu_is_in_avoid_pmccntr_cpus();
1387+
13511388
brbe_probe(cpu_pmu);
13521389
}
13531390

include/linux/perf/arm_pmu.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ struct arm_pmu {
119119

120120
/* PMUv3 only */
121121
int pmuver;
122-
bool has_smt;
122+
bool avoid_pmccntr;
123123
u64 reg_pmmir;
124124
u64 reg_brbidr;
125125
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40

0 commit comments

Comments
 (0)