Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions drivers/perf/arm_pmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,12 @@ int armpmu_register(struct arm_pmu *pmu)
if (ret)
return ret;

/*
* By this stage we know our supported CPUs on either DT/ACPI platforms,
* detect the SMT implementation.
*/
pmu->has_smt = topology_core_has_smt(cpumask_first(&pmu->supported_cpus));

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

Expand Down
39 changes: 37 additions & 2 deletions drivers/perf/arm_pmuv3.c
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,42 @@ static int armv8pmu_get_chain_idx(struct pmu_hw_events *cpuc,
return -EAGAIN;
}

static bool armv8pmu_can_use_pmccntr(struct pmu_hw_events *cpuc,
struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
unsigned long evtype = hwc->config_base & ARMV8_PMU_EVTYPE_EVENT;

if (evtype != ARMV8_PMUV3_PERFCTR_CPU_CYCLES)
return false;

/*
* A CPU_CYCLES event with threshold counting cannot use PMCCNTR_EL0
* since it lacks threshold support.
*/
if (armv8pmu_event_get_threshold(&event->attr))
return false;

/*
* PMCCNTR_EL0 is not affected by BRBE controls like BRBCR_ELx.FZP.
* So don't use it for branch events.
*/
if (has_branch_stack(event))
return false;

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

return true;
}

static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
struct perf_event *event)
{
Expand All @@ -986,8 +1022,7 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
unsigned long evtype = hwc->config_base & ARMV8_PMU_EVTYPE_EVENT;

/* Always prefer to place a cycle counter into the cycle counter. */
if ((evtype == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) &&
!armv8pmu_event_get_threshold(&event->attr) && !has_branch_stack(event)) {
if (armv8pmu_can_use_pmccntr(cpuc, event)) {
if (!test_and_set_bit(ARMV8_PMU_CYCLE_IDX, cpuc->used_mask))
return ARMV8_PMU_CYCLE_IDX;
else if (armv8pmu_event_is_64bit(event) &&
Expand Down
11 changes: 11 additions & 0 deletions include/linux/arch_topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ void remove_cpu_topology(unsigned int cpuid);
void reset_cpu_topology(void);
int parse_acpi_topology(void);
void freq_inv_set_max_ratio(int cpu, u64 max_rate);

/*
* Architectures like ARM64 don't have reliable architectural way to get SMT
* information and depend on the firmware (ACPI/OF) report. Non-SMT core won't
* initialize thread_id so we can use this to detect the SMT implementation.
*/
static inline bool topology_core_has_smt(int cpu)
{
return cpu_topology[cpu].thread_id != -1;
}

#endif

#endif /* _LINUX_ARCH_TOPOLOGY_H_ */
1 change: 1 addition & 0 deletions include/linux/perf/arm_pmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ struct arm_pmu {

/* PMUv3 only */
int pmuver;
bool has_smt;
u64 reg_pmmir;
u64 reg_brbidr;
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40
Expand Down