From aa9265d5073cb01b89f31ae030442873d4a259d3 Mon Sep 17 00:00:00 2001 From: Nia Su Date: Wed, 9 Sep 2026 19:31:52 -0700 Subject: [PATCH] perf evlist: Don't restrict uncore events to PMU CPUs The PMU CPU map has different semantics for core and uncore PMUs. For core PMUs, pmu_cpus contains the CPUs on which the event can be opened. Intersect the user-requested CPU map with the PMU CPU map to reject CPUs unsupported by the PMU. For uncore PMUs, pmu_cpus is read from the PMU's cpumask sysfs attribute, which lists the default CPU to open the event on per socket/die, not the set of CPUs it can only be opened on. The user-requested CPU may override these defaults, and the driver can redirect the event to the appropriate control CPU. Currently, __perf_evlist__propagate_maps() intersects the user-requested CPU map with pmu_cpus for all PMUs. If the user-requested CPU is not in pmu_cpus, the intersection becomes empty and the evsel is removed before perf_event_open() is called. Add evsel->is_pmu_uncore, set once from pmu->is_uncore at parse time, to skip the intersection for uncore PMUs. Core PMUs, and other non-core PMUs such as software events, breakpoints, or tracepoints, continue to be restricted to their PMU CPU map. Fixes: 811082e4b668db96 ("perf parse-events: Support user CPUs mixed with threads/processes") Reviewed-by: Ian Rogers Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Nia Su Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/evlist.c | 5 +++-- tools/lib/perf/include/internal/evsel.h | 2 ++ tools/perf/util/evsel.c | 1 + tools/perf/util/parse-events.c | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c index 1f210dadd666..c1c5b0684eed 100644 --- a/tools/lib/perf/evlist.c +++ b/tools/lib/perf/evlist.c @@ -79,8 +79,9 @@ static void __perf_evlist__propagate_maps(struct perf_evlist *evlist, } } - /* Ensure cpus only references valid PMU CPUs. */ - if (!perf_cpu_map__has_any_cpu(evsel->cpus) && + /* Ensure cpus only references valid PMU CPUs, except for uncore PMUs. */ + if (!evsel->is_pmu_uncore && + !perf_cpu_map__has_any_cpu(evsel->cpus) && !perf_cpu_map__is_subset(evsel->pmu_cpus, evsel->cpus)) { struct perf_cpu_map *tmp = perf_cpu_map__intersect(evsel->pmu_cpus, evsel->cpus); diff --git a/tools/lib/perf/include/internal/evsel.h b/tools/lib/perf/include/internal/evsel.h index b988034f1371..35447bed0f95 100644 --- a/tools/lib/perf/include/internal/evsel.h +++ b/tools/lib/perf/include/internal/evsel.h @@ -128,6 +128,8 @@ struct perf_evsel { bool requires_cpu; /** Is the PMU for the event a core one? Effects the handling of own_cpus. */ bool is_pmu_core; + /** Is the PMU for the event an uncore one? */ + bool is_pmu_uncore; /** Does the evsel on read on the first CPU index such as tool time events? */ bool reads_only_on_cpu_idx0; int idx; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1c9e5a30a0cd..3367242c5764 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -555,6 +555,7 @@ struct evsel *evsel__clone(struct evsel *orig) evsel->core.system_wide = orig->core.system_wide; evsel->core.requires_cpu = orig->core.requires_cpu; evsel->core.is_pmu_core = orig->core.is_pmu_core; + evsel->core.is_pmu_uncore = orig->core.is_pmu_uncore; if (orig->name) { evsel->name = strdup(orig->name); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index e40c16cbef01..2f3317ce17f0 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -269,6 +269,7 @@ __add_event(struct list_head *list, int *idx, evsel->core.pmu_cpus = pmu_cpus; evsel->core.requires_cpu = pmu ? pmu->is_uncore : false; evsel->core.is_pmu_core = is_pmu_core; + evsel->core.is_pmu_uncore = pmu ? pmu->is_uncore : false; evsel->core.reads_only_on_cpu_idx0 = perf_pmu__reads_only_on_cpu_idx0(attr); evsel->pmu = pmu; evsel->alternate_hw_config = alternate_hw_config;