Re: [PATCH v3 6/9] perf/cxl: Don't share the overflow interrupt, and keep it pinned

[email protected] Fri, 31 Jul 2026 23:50:27 +0000
Newsgroups org.kernel.vger.linux-cxl,org.kernel.vger.linux-perf-users
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 1 potential issue(=
s) to consider:

Pre-existing issues:
- [High] CPU hotplug migration race causes cross-CPU data corruption on in-=
flight interrupts.
--

--- Patch [6]: [PATCH v3 6/9] perf/cxl: Don't share the overflow interrupt,=
 and keep it pinned ---
commit 0c52a628eb5f3fcd46d4c3f0c11a1448687ffd14
Author: Dave Jiang <[email protected]>

perf/cxl: Don't share the overflow interrupt, and keep it pinned

The PMU pins its overflow interrupt to info->on_cpu in the hotplug
callbacks, but requests it with only IRQF_SHARED | IRQF_NO_THREAD. Without
IRQF_NOBALANCING, irqbalance or a userspace smp_affinity write can move the
interrupt to another CPU. cxl_pmu_irq() then runs local64_cmpxchg() and
local64_add() on hwc->prev_count and event->count there, at the same time
as the managing CPU. local64_t is only atomic against same-CPU access, so
the counts get corrupted.

Link: https://sashiko.dev/#/patchset/20260715191454.459673-1-dave@stgolabs.=
net?part=3D1

> diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c
> index c9e30cb149dfa..6fdc66a01fb67 100644
> --- a/drivers/perf/cxl_pmu.c
> +++ b/drivers/perf/cxl_pmu.c

[ ... ]

> @@ -784,7 +784,7 @@ static irqreturn_t cxl_pmu_irq(int irq, void *data)
> =20
>  	overflowed =3D readq(base + CXL_PMU_OVERFLOW_REG);
> =20
> -	/* Interrupt may be shared, so maybe it isn't ours */
> +	/* Nothing overflowed, so the device did not raise this */
>  	if (!overflowed)
>  		return IRQ_NONE;

[Severity: High]
This is a pre-existing issue, but can this IRQ handler race with CPU hotplug
migration and cause cross-CPU data corruption?

Looking at cxl_pmu_offline_cpu(), the context is migrated to a new CPU befo=
re
the IRQ affinity is updated:

cxl_pmu_offline_cpu() {
    ...
    perf_pmu_migrate_context(&info->pmu, cpu, target);
    info->on_cpu =3D target;
    /*
     * CPU HP lock is held so we should be guaranteed that this CPU hasn't
     * yet gone away.
     */
    WARN_ON(irq_set_affinity(info->irq, cpumask_of(target)));
    ...
}

During installation on the new CPU, the hardware counters and interrupts are
re-enabled. If the hardware generates a PMU overflow interrupt in this
window before irq_set_affinity() completes, the interrupt is delivered to
the old CPU.

The old CPU's IRQ handler (cxl_pmu_irq()) will then see the newly installed
event in the globally shared info->hw_events[i] array:

cxl_pmu_irq() {
    ...
	for_each_set_bit(i, overflowedbm, info->num_counters) {
		struct perf_event *event =3D info->hw_events[i];
    ...
		__cxl_pmu_read(event, true);
	}
    ...
}

This causes the old CPU to execute non-atomic local64_cmpxchg() and
local64_add() operations concurrently with the new managing CPU. Since
local64_t operations are only safe for same-CPU access, won't this corrupt
the hardware event counts?

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260731232827.4014=
[email protected]?part=3D6