Skip to content

Commit

Permalink
drivers/perf: apple_m1: Support host/guest event filtering
Browse files Browse the repository at this point in the history
The PMU appears to have a separate register for filtering 'guest'
exception levels (i.e. EL1 and !ELIsInHost(EL0)) which has the same
layout as PMCR1_EL1. Conveniently, there exists a VHE register alias
(PMCR1_EL12) that can be used to configure it.

Support guest events by programming the EL12 register with the intended
guest kernel/userspace filters. Limit support for guest events to VHE
(i.e. kernel running at EL2), as it avoids involving KVM to context
switch PMU registers. VHE is the only supported mode on M* parts anyway,
so this isn't an actual feature limitation.

Tested-by: Janne Grunau <j@jannau.net>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250305202641.428114-3-oliver.upton@linux.dev
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
  • Loading branch information
Oliver Upton committed Mar 11, 2025
1 parent 75ecffc commit 46573d9
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 4 deletions.
1 change: 1 addition & 0 deletions arch/arm64/include/asm/apple_m1_pmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#define PMCR0_PMI_ENABLE_8_9 GENMASK(45, 44)

#define SYS_IMP_APL_PMCR1_EL1 sys_reg(3, 1, 15, 1, 0)
#define SYS_IMP_APL_PMCR1_EL12 sys_reg(3, 1, 15, 7, 2)
#define PMCR1_COUNT_A64_EL0_0_7 GENMASK(15, 8)
#define PMCR1_COUNT_A64_EL1_0_7 GENMASK(23, 16)
#define PMCR1_COUNT_A64_EL0_8_9 GENMASK(41, 40)
Expand Down
20 changes: 16 additions & 4 deletions drivers/perf/apple_m1_cpu_pmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ enum m1_pmu_events {
*/
M1_PMU_CFG_COUNT_USER = BIT(8),
M1_PMU_CFG_COUNT_KERNEL = BIT(9),
M1_PMU_CFG_COUNT_HOST = BIT(10),
M1_PMU_CFG_COUNT_GUEST = BIT(11),
};

/*
Expand Down Expand Up @@ -328,7 +330,7 @@ static void m1_pmu_disable_counter_interrupt(unsigned int index)
}

static void __m1_pmu_configure_event_filter(unsigned int index, bool user,
bool kernel)
bool kernel, bool host)
{
u64 clear, set, user_bit, kernel_bit;

Expand Down Expand Up @@ -356,7 +358,10 @@ static void __m1_pmu_configure_event_filter(unsigned int index, bool user,
else
clear |= kernel_bit;

sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL1, clear, set);
if (host)
sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL1, clear, set);
else if (is_kernel_in_hyp_mode())
sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL12, clear, set);
}

static void __m1_pmu_configure_eventsel(unsigned int index, u8 event)
Expand Down Expand Up @@ -391,10 +396,13 @@ static void __m1_pmu_configure_eventsel(unsigned int index, u8 event)
static void m1_pmu_configure_counter(unsigned int index, unsigned long config_base)
{
bool kernel = config_base & M1_PMU_CFG_COUNT_KERNEL;
bool guest = config_base & M1_PMU_CFG_COUNT_GUEST;
bool host = config_base & M1_PMU_CFG_COUNT_HOST;
bool user = config_base & M1_PMU_CFG_COUNT_USER;
u8 evt = config_base & M1_PMU_CFG_EVENT;

__m1_pmu_configure_event_filter(index, user, kernel);
__m1_pmu_configure_event_filter(index, user && host, kernel && host, true);
__m1_pmu_configure_event_filter(index, user && guest, kernel && guest, false);
__m1_pmu_configure_eventsel(index, evt);
}

Expand Down Expand Up @@ -570,14 +578,18 @@ static int m1_pmu_set_event_filter(struct hw_perf_event *event,
{
unsigned long config_base = 0;

if (!attr->exclude_guest) {
if (!attr->exclude_guest && !is_kernel_in_hyp_mode()) {
pr_debug("ARM performance counters do not support mode exclusion\n");
return -EOPNOTSUPP;
}
if (!attr->exclude_kernel)
config_base |= M1_PMU_CFG_COUNT_KERNEL;
if (!attr->exclude_user)
config_base |= M1_PMU_CFG_COUNT_USER;
if (!attr->exclude_host)
config_base |= M1_PMU_CFG_COUNT_HOST;
if (!attr->exclude_guest)
config_base |= M1_PMU_CFG_COUNT_GUEST;

event->config_base = config_base;

Expand Down

0 comments on commit 46573d9

Please sign in to comment.