[PATCH v3 1/2] perf: arm_pmu: Accept a per-device CPU mask from platform data

From: Johnny.Fan

Date: Mon Nov 02 2026 - 03:43:58 EST


From: "Johnny.Fan" <Johnny.Fan@xxxxxxxxxxx>

Synthetic platform devices created from ACPI data have no fwnode, so
platform_get_irq_affinity() cannot tell which CPUs implement the
feature and falls back to cpu_possible_mask.

Add an optional CPU mask to the platform data and make both the SPE and
the TRBE drivers prefer it over the firmware-described IRQ affinity.
Device tree devices have no platform data and keep using the affinity.

TRBE must also pass a stable copy of the mask to
request_percpu_irq_affinity(), because supported_cpus is pruned as CPUs
are probed, while the IRQ action keeps a pointer to the mask.

This is a preparatory change: no ACPI device carries platform data yet,
so behaviour is unchanged.

Signed-off-by: Johnny.Fan <Johnny.Fan@xxxxxxxxxxx>
Reviewed-by: Fugang Duan <fugang.duan@xxxxxxxxxxx>
---
drivers/hwtracing/coresight/coresight-trbe.c | 22 +++++++++++++++++---
drivers/perf/arm_spe_pmu.c | 10 ++++++++-
include/linux/perf/arm_pmu.h | 10 +++++++++
3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c
index c7cbca45f2de..0c4bc8fbcaf3 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.c
+++ b/drivers/hwtracing/coresight/coresight-trbe.c
@@ -158,6 +158,7 @@ struct trbe_drvdata {
struct hlist_node hotplug_node;
int irq;
cpumask_t supported_cpus;
+ cpumask_t irq_affinity;
enum cpuhp_state trbe_online;
struct platform_device *pdev;
};
@@ -1533,6 +1534,8 @@ static void arm_trbe_remove_cpuhp(struct trbe_drvdata *drvdata)
static int arm_trbe_probe_irq(struct platform_device *pdev,
struct trbe_drvdata *drvdata)
{
+ const struct arm_pmu_acpi_dev_data *acpi_data =
+ dev_get_platdata(&pdev->dev);
const struct cpumask *affinity;
int ret;

@@ -1547,14 +1550,27 @@ static int arm_trbe_probe_irq(struct platform_device *pdev,
return -EINVAL;
}

- cpumask_copy(&drvdata->supported_cpus, affinity);
+ /*
+ * The ACPI path has no fwnode describing which CPUs implement TRBE,
+ * so it passes the mask explicitly. DT devices still use the
+ * firmware-described IRQ affinity.
+ */
+ cpumask_copy(&drvdata->supported_cpus,
+ acpi_data ? &acpi_data->cpus : affinity);
+
+ /*
+ * supported_cpus gets pruned as CPUs are probed, so keep a stable
+ * copy for the IRQ action.
+ */
+ cpumask_copy(&drvdata->irq_affinity, &drvdata->supported_cpus);

drvdata->handle = alloc_percpu(struct perf_output_handle *);
if (!drvdata->handle)
return -ENOMEM;

- ret = request_percpu_irq_affinity(drvdata->irq, arm_trbe_irq_handler, DRVNAME,
- affinity, drvdata->handle);
+ ret = request_percpu_irq_affinity(drvdata->irq, arm_trbe_irq_handler,
+ DRVNAME, &drvdata->irq_affinity,
+ drvdata->handle);
if (ret) {
free_percpu(drvdata->handle);
return ret;
diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c
index b70f3fedf028..712b7f187301 100644
--- a/drivers/perf/arm_spe_pmu.c
+++ b/drivers/perf/arm_spe_pmu.c
@@ -1330,6 +1330,8 @@ static void arm_spe_pmu_dev_teardown(struct arm_spe_pmu *spe_pmu)
static int arm_spe_pmu_irq_probe(struct arm_spe_pmu *spe_pmu)
{
struct platform_device *pdev = spe_pmu->pdev;
+ const struct arm_pmu_acpi_dev_data *acpi_data =
+ dev_get_platdata(&pdev->dev);
const struct cpumask *affinity;
int irq;

@@ -1342,7 +1344,13 @@ static int arm_spe_pmu_irq_probe(struct arm_spe_pmu *spe_pmu)
return -EINVAL;
}

- cpumask_copy(&spe_pmu->supported_cpus, affinity);
+ /*
+ * The ACPI path has no fwnode describing which CPUs implement SPE,
+ * so it passes the mask explicitly. DT devices still use the
+ * firmware-described IRQ affinity.
+ */
+ cpumask_copy(&spe_pmu->supported_cpus,
+ acpi_data ? &acpi_data->cpus : affinity);

spe_pmu->irq = irq;
return 0;
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 02d2c7f45b52..7f1c03c55025 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -8,6 +8,7 @@
#ifndef __ARM_PMU_H__
#define __ARM_PMU_H__

+#include <linux/cpumask.h>
#include <linux/interrupt.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
@@ -199,6 +200,15 @@ void armpmu_free_irq(struct arm_pmu * __percpu *armpmu, int irq, int cpu);
#define ARMV8_SPE_PDEV_NAME "arm,spe-v1"
#define ARMV8_TRBE_PDEV_NAME "arm,trbe"

+/*
+ * Per-device CPU mask passed by the ACPI MADT walk. The synthetic
+ * platform devices have no fwnode, so the IRQ affinity cannot describe
+ * which CPUs actually implement the feature.
+ */
+struct arm_pmu_acpi_dev_data {
+ struct cpumask cpus;
+};
+
/* Why does everything I do descend into this? */
#define __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \
(lo) == (hi) ? #cfg ":" #lo "\n" : #cfg ":" #lo "-" #hi
--
2.55.0