[PATCH v2 1/2] perf/arm-cmn: Don't schedule events on a CPU which no longer owns the PMU
From: Haris Okanovic
Date: Wed Sep 30 2026 - 20:08:24 EST
perf_event_open() can latch cmn->cpu and then install the event after a
migration has already moved the PMU. The driver has no locking -- it
relies on all events living in one CPU's context -- so such an event
races the owning CPU and can corrupt the shared DTC and DTM state,
giving wrong counts. arm_cmn_event_add() now rejects an event which is
not on the owning CPU.
arm_cmn_migrate() now claims cmn->cpu before migrating, so that events
it reinstalls via perf_pmu_migrate_context() still pass that check.
Signed-off-by: Haris Okanovic <harisokn@xxxxxxxxxx>
---
drivers/perf/arm-cmn.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
index 5378fba916cf5..a5593e5821925 100644
--- a/drivers/perf/arm-cmn.c
+++ b/drivers/perf/arm-cmn.c
@@ -2112,6 +2112,9 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
enum cmn_node_type type = CMN_EVENT_TYPE(event);
unsigned int input_sel, i = 0;
+ if (cmn->cpu != smp_processor_id())
+ return -ENOENT;
+
if (type == CMN_TYPE_DTC) {
while (cmn->dtc[i].cycles)
if (++i == cmn->num_dtcs)
@@ -2245,12 +2248,12 @@ static int arm_cmn_commit_txn(struct pmu *pmu)
static void arm_cmn_migrate(struct arm_cmn *cmn, unsigned int cpu)
{
- unsigned int i;
+ unsigned int i, old = cmn->cpu;
- perf_pmu_migrate_context(&cmn->pmu, cmn->cpu, cpu);
+ cmn->cpu = cpu;
+ perf_pmu_migrate_context(&cmn->pmu, old, cpu);
for (i = 0; i < cmn->num_dtcs; i++)
irq_set_affinity(cmn->dtc[i].irq, cpumask_of(cpu));
- cmn->cpu = cpu;
}
static int arm_cmn_pmu_online_cpu(unsigned int cpu, struct hlist_node *cpuhp_node)
--
Haris Okanovic
AWS Graviton