From: sashiko-bot@kernel.org
To: "Robin Murphy" <robin.murphy@arm.com>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH 1/2] perf: Generic hotplug/cpumask for system PMUs
Date: Wed, 22 Jul 2026 17:38:15 +0000 [thread overview]
Message-ID: <20260722173816.3E7AF1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <b8aae1114794a348ecfb4e9cf7a792b3ac300d56.1784653319.git.robin.murphy@arm.com>
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [Critical] Taking a CPU offline triggers a NULL pointer dereference if a PMU defines `init_cpu` but not `exit_cpu`.
- [High] CPU online context migration passes the new CPU as both source and destination to `perf_pmu_migrate_context`, causing a self-deadlock and failing to migrate events.
- [High] When a PMU is unregistered, the perf core fails to invoke the `pmu->exit_cpu()` callback for currently online CPUs, leaking state.
- [High] Missing bounds check in `perf_event_setup_cpumask` leads to an out-of-bounds per-CPU pointer access during migration.
- [High] `perf_pmu_register()` invokes the PMU's `init_cpu` callback in atomic IPI context, contradicting its documented thread context.
- [Medium] Secondary CPU hotplug triggers a `WARN_ON_ONCE` because `perf_event_setup_cpumask` incorrectly iterates up to `PERF_PMU_SCOPE_CPUMASK`.
- [Medium] PMUs using `PERF_PMU_SCOPE_CPUMASK` with static masks but no hotplug callbacks will never have their context migrated on CPU offline.
- [Medium] If `perf_pmu_register()` fails during the IDR allocation step, it leaks the CPU-affine state initialized via `pmu->init_cpu()`.
--
--- Patch [1]: [PATCH 1/2] perf: Generic hotplug/cpumask for system PMUs ---
commit aa795c7bb07889a3ee4d3e9c388a299913298dba
Author: Robin Murphy <robin.murphy@arm.com>
perf: Generic hotplug/cpumask for system PMUs
This commit extends the generic uncore machinery to handle system PMUs
by introducing a new PERF_PMU_SCOPE_CPUMASK scope. It allows the core
code to retrieve a per-PMU cpumask dynamically and adds optional callbacks
to manage CPU-affine state during hotplug operations.
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index ba5bd6a78fe7b..505cbda2ac41f 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
[ ... ]
> @@ -12920,6 +12945,16 @@ int perf_pmu_register(struct pmu *_pmu, const char *name, int type)
> INIT_LIST_HEAD(&pmu->events);
> spin_lock_init(&pmu->events_lock);
>
> + /*
> + * Finally, if appropriate give the PMU a chance to initialise any
> + * of its own CPU-affine state. Note that we're serialised against
> + * perf_event_{init,exit}_cpu() themselves by virtue of pmus_lock.
> + */
> + if (pmu->init_cpu) {
> + for_each_online_cpu(cpu)
> + smp_call_function_single(cpu, perf_scope_init_cpu, pmu, 1);
> + }
> +
[Severity: High]
Does executing init_cpu via smp_call_function_single() conflict with the
header documentation?
The documentation states init_cpu runs from CPUHP_AP_PERF_ONLINE context,
which is preemptible. However, smp_call_function_single() executes the
callback in atomic IPI context. If a driver sleeps here, will it cause a
panic?
[Severity: Medium]
If idr_cmpxchg() fails shortly after this initialization loop, does this
leak the newly allocated CPU-affine state?
There doesn't appear to be an error path to invoke exit_cpu on the online CPUs
to unwind this initialization.
[Severity: High]
While not explicitly in this diff, since this function sets up CPU-affine
state for a PMU during registration, is there a corresponding teardown step
missing?
It appears perf_pmu_unregister() doesn't invoke exit_cpu across the online
CPUs, which could leave this state dangling when a driver is unloaded.
> /*
> * Now that the PMU is complete, make it visible to perf_try_init_event().
> */
[ ... ]
> @@ -15189,10 +15225,19 @@ static void perf_event_clear_cpumask(unsigned int cpu)
>
> /* migrate */
> list_for_each_entry(pmu, &pmus, entry) {
> + bool cpumask_migrate = false;
> +
> + if (pmu->init_cpu && (!pmu->cpumask || cpumask_test_cpu(cpu, pmu->cpumask)))
> + cpumask_migrate = pmu->exit_cpu(pmu, cpu);
> +
[Severity: Critical]
Is there a risk of a NULL pointer dereference here?
This code checks if pmu->init_cpu is present, but then invokes
pmu->exit_cpu. Because both callbacks are optional, a PMU driver might
define init_cpu but leave exit_cpu as NULL.
> if (pmu->scope == PERF_PMU_SCOPE_NONE ||
> WARN_ON_ONCE(pmu->scope >= PERF_PMU_MAX_SCOPE))
> continue;
>
> + target[PERF_PMU_SCOPE_CPUMASK] = -1;
> + if (cpumask_migrate && !WARN_ON_ONCE(!pmu->cpumask))
> + target[PERF_PMU_SCOPE_CPUMASK] = cpumask_any_but(pmu->cpumask, cpu);
> +
[Severity: Medium]
Does this strand PMU contexts for PMUs that use a static cpumask without
providing hotplug callbacks?
Since cpumask_migrate defaults to false and is only set if a callback
is invoked, target[PERF_PMU_SCOPE_CPUMASK] remains -1 and the migration
seems to be bypassed, leaving the context orphaned on the offline CPU.
> if (target[pmu->scope] >= 0 && target[pmu->scope] < nr_cpu_ids)
> perf_pmu_migrate_context(pmu, cpu, target[pmu->scope]);
> }
[ ... ]
> @@ -15246,7 +15292,7 @@ static void perf_event_setup_cpumask(unsigned int cpu)
> goto end;
> }
>
> - for (scope = PERF_PMU_SCOPE_NONE + 1; scope < PERF_PMU_MAX_SCOPE; scope++) {
> + for (scope = PERF_PMU_SCOPE_NONE + 1; scope <= PERF_PMU_SCOPE_CPUMASK; scope++) {
[Severity: Medium]
Does iterating up to PERF_PMU_SCOPE_CPUMASK unconditionally trigger a
warning during secondary CPU hotplug?
For this scope, perf_scope_cpumask() explicitly returns NULL, which would
immediately trip the WARN_ON_ONCE(!pmu_cpumask || !cpumask) check later in
this loop.
> const struct cpumask *cpumask = perf_scope_cpu_topology_cpumask(scope, cpu);
>
> pmu_cpumask = perf_scope_cpumask(scope);
[ ... ]
> @@ -15258,6 +15304,16 @@ static void perf_event_setup_cpumask(unsigned int cpu)
> cpumask_any_and(pmu_cpumask, cpumask) >= nr_cpu_ids)
> cpumask_set_cpu(cpu, pmu_cpumask);
> }
> +
> + /* Allow migrating if the new CPU is preferable (e.g. NUMA locality) */
> + list_for_each_entry(pmu, &pmus, entry) {
> + bool cpumask_migrate = false;
> +
> + if (pmu->init_cpu && (!pmu->cpumask || !cpumask_test_cpu(cpu, pmu->cpumask)))
> + cpumask_migrate = pmu->init_cpu(pmu, cpu);
> + if (cpumask_migrate && !WARN_ON_ONCE(!pmu->cpumask))
> + perf_pmu_migrate_context(pmu, cpu, cpumask_any(pmu->cpumask));
> + }
[Severity: High]
Will this cause a self-deadlock in perf_pmu_migrate_context()?
The newly onlined CPU is passed as both the source CPU (cpu) and
potentially the destination CPU (via cpumask_any). This would lock the same
mutex twice in perf_pmu_migrate_context():
kernel/events/core.c:perf_pmu_migrate_context() {
...
mutex_lock_double(&src_ctx->mutex, &dst_ctx->mutex);
...
}
Should this be migrating from the old CPU to the new CPU instead?
[Severity: High]
What happens if the PMU cpumask is empty here?
If init_cpu returns true but the PMU's mask is empty (for example, if it
defaulted to cpu_none_mask), cpumask_any() could return a value greater
than or equal to nr_cpu_ids. Passing this out-of-bounds value to
perf_pmu_migrate_context() might result in corrupting memory when it accesses
the per-CPU pointers.
> end:
> cpumask_set_cpu(cpu, perf_online_mask);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1784653319.git.robin.murphy@arm.com?part=1
next prev parent reply other threads:[~2026-07-22 17:38 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-22 17:24 [PATCH 0/2] perf: Generic hotplug/cpumask for system PMUs Robin Murphy
2026-07-22 17:24 ` [PATCH 1/2] " Robin Murphy
2026-07-22 17:38 ` sashiko-bot [this message]
2026-07-22 18:42 ` Robin Murphy
2026-07-22 17:24 ` [PATCH 2/2] perf/arm-cmn: Switch to generic cpumask Robin Murphy
2026-07-22 17:59 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260722173816.3E7AF1F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=robin.murphy@arm.com \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox