Skip to content

Commit 5f86830

Browse files
ouptonjannau
authored andcommitted
drivers/perf: apple_m1: Support host/guest event filtering
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. Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent 74c65f5 commit 5f86830

1 file changed

Lines changed: 16 additions & 4 deletions

File tree

drivers/perf/apple_m1_cpu_pmu.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ enum m1_pmu_events {
120120
*/
121121
M1_PMU_CFG_COUNT_USER = BIT(8),
122122
M1_PMU_CFG_COUNT_KERNEL = BIT(9),
123+
M1_PMU_CFG_COUNT_HOST = BIT(10),
124+
M1_PMU_CFG_COUNT_GUEST = BIT(11),
123125
};
124126

125127
/*
@@ -326,7 +328,7 @@ static void m1_pmu_disable_counter_interrupt(unsigned int index)
326328
}
327329

328330
static void __m1_pmu_configure_event_filter(unsigned int index, bool user,
329-
bool kernel)
331+
bool kernel, bool host)
330332
{
331333
u64 clear, set, user_bit, kernel_bit;
332334

@@ -354,7 +356,10 @@ static void __m1_pmu_configure_event_filter(unsigned int index, bool user,
354356
else
355357
clear |= kernel_bit;
356358

357-
sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL1, clear, set);
359+
if (host)
360+
sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL1, clear, set);
361+
else if (is_kernel_in_hyp_mode())
362+
sysreg_clear_set_s(SYS_IMP_APL_PMCR1_EL12, clear, set);
358363
}
359364

360365
static void __m1_pmu_configure_eventsel(unsigned int index, u8 event)
@@ -389,10 +394,13 @@ static void __m1_pmu_configure_eventsel(unsigned int index, u8 event)
389394
static void m1_pmu_configure_counter(unsigned int index, unsigned long config_base)
390395
{
391396
bool kernel = config_base & M1_PMU_CFG_COUNT_KERNEL;
397+
bool guest = config_base & M1_PMU_CFG_COUNT_GUEST;
398+
bool host = config_base & M1_PMU_CFG_COUNT_HOST;
392399
bool user = config_base & M1_PMU_CFG_COUNT_USER;
393400
u8 evt = config_base & M1_PMU_CFG_EVENT;
394401

395-
__m1_pmu_configure_event_filter(index, user, kernel);
402+
__m1_pmu_configure_event_filter(index, user && host, kernel && host, true);
403+
__m1_pmu_configure_event_filter(index, user && guest, kernel && guest, false);
396404
__m1_pmu_configure_eventsel(index, evt);
397405
}
398406

@@ -568,14 +576,18 @@ static int m1_pmu_set_event_filter(struct hw_perf_event *event,
568576
{
569577
unsigned long config_base = 0;
570578

571-
if (!attr->exclude_guest) {
579+
if (!attr->exclude_guest && !is_kernel_in_hyp_mode()) {
572580
pr_debug("ARM performance counters do not support mode exclusion\n");
573581
return -EOPNOTSUPP;
574582
}
575583
if (!attr->exclude_kernel)
576584
config_base |= M1_PMU_CFG_COUNT_KERNEL;
577585
if (!attr->exclude_user)
578586
config_base |= M1_PMU_CFG_COUNT_USER;
587+
if (!attr->exclude_host)
588+
config_base |= M1_PMU_CFG_COUNT_HOST;
589+
if (!attr->exclude_guest)
590+
config_base |= M1_PMU_CFG_COUNT_GUEST;
579591

580592
event->config_base = config_base;
581593

0 commit comments

Comments
 (0)