[PATCH 3/4] perf/core: Allow userspace to select a device PMU's CPU
Haris Okanovic
harisokn at amazon.com
Tue Oct 13 11:56:02 PDT 2026
perf core picks the CPU a "device" PMU (PERF_PMU_SCOPE_DEVICE) is
counted on when the PMU registers, and perf_try_init_event() then binds
every event to it. That choice is only revisited on CPU hotplug and
there is no way for users to set it explicitly, so in practice the PMU
stays wherever it was first placed.
All of the PMU's recurring work therefore lands on one CPU. This is
problematic on systems which reserve particular CPUs for latency
sensitive work or confine background activity to a chosen set of
housekeeping CPUs.
Make the 'cpumask' attribute writable for device scope, allowing users
to rebind PMU devices outside of hotplug by writing an arbitrary CPU
set. The same migration rules apply: user chosen CPU sets must contain
an online CPU that's accepted by the PMU driver's filter. Writes fail
with -ENODEV when none of the given CPUs satisfy these constraints.
The attribute expresses a preference rather than a guarantee. Hotplug
may still move the PMU, e.g. when the user chosen CPU is offlined.
Signed-off-by: Haris Okanovic <harisokn at amazon.com>
---
kernel/events/core.c | 46 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 43 insertions(+), 3 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0d4e2c6d71584..fe048704ef4a1 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -12777,7 +12777,40 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr,
return 0;
}
-static DEVICE_ATTR_RO(cpumask);
+/*
+ * Move a device PMU to one of the CPUs written here. The PMU gets the final
+ * say: the write fails if it cannot be counted on any of them.
+ */
+static ssize_t cpumask_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pmu *pmu = dev_get_drvdata(dev);
+ cpumask_var_t requested;
+ ssize_t ret;
+
+ if (pmu->scope != PERF_PMU_SCOPE_DEVICE)
+ return -EOPNOTSUPP;
+
+ if (!zalloc_cpumask_var(&requested, GFP_KERNEL))
+ return -ENOMEM;
+
+ ret = cpulist_parse(buf, requested);
+ if (ret)
+ goto out;
+
+ mutex_lock(&pmus_lock);
+ ret = perf_pmu_set_cpu(pmu, requested);
+ mutex_unlock(&pmus_lock);
+
+ if (!ret)
+ ret = count;
+out:
+ free_cpumask_var(requested);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RW(cpumask);
static struct attribute *pmu_dev_attrs[] = {
&dev_attr_type.attr,
@@ -12796,8 +12829,15 @@ static umode_t pmu_dev_is_visible(struct kobject *kobj, struct attribute *a, int
return 0;
/* cpumask */
- if (n == 3 && pmu->scope == PERF_PMU_SCOPE_NONE)
- return 0;
+ if (n == 3) {
+ /* Writable, the core picks the CPU. See cpumask_store(). */
+ if (pmu->scope == PERF_PMU_SCOPE_DEVICE)
+ return a->mode;
+ if (pmu->scope == PERF_PMU_SCOPE_NONE)
+ return 0;
+ /* A scope cpumask follows the CPU topology, read-only. */
+ return 0444;
+ }
return a->mode;
}
--
Haris Okanovic
AWS Graviton
More information about the linux-arm-kernel
mailing list