From: sashiko-bot@kernel.org
To: "Dave Jiang" <dave.jiang@intel.com>
Cc: linux-cxl@vger.kernel.org, linux-perf-users@vger.kernel.org
Subject: Re: [RESEND PATCH v4 08/11] perf/cxl: Unfreeze counters after handling an overflow interrupt
Date: Wed, 05 Aug 2026 16:15:57 +0000 [thread overview]
Message-ID: <20260805161558.D1C471F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260805155911.1304807-9-dave.jiang@intel.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] A cross-CPU TOCTOU race condition during CPU migration can leave the PMU permanently unfrozen when it is supposed to be disabled, leading to a specification violation where a running counter is written to.
Pre-existing issues:
- [High] `__cxl_pmu_read` is vulnerable to a cross-CPU data race on `hwc->prev_count` during CPU migration because it relies on `local64_cmpxchg`.
- [High] Pending overflow interrupts can be incorrectly attributed to newly scheduled events on the same counter, corrupting their counts by adding a spurious full period.
- [High] Writing 1s to RsvdZ (Reserved, must be zero) bits in the Freeze register violates the CXL specification and risks fatal hardware errors.
- [High] Concurrent PMU reads and overflows cause massive double-counting or loss of events due to stateless overflow handling.
--
--- Patch [8]: [RESEND PATCH v4 08/11] perf/cxl: Unfreeze counters after handling an overflow interrupt ---
commit 77054eb72889cfc4fca68e72d632bb6f5cfbe02d
Author: Dave Jiang <dave.jiang@intel.com>
perf/cxl: Unfreeze counters after handling an overflow interrupt
The counters run with Freeze on Overflow set, so one overflow freezes every
counter in the block. cxl_pmu_irq() reads the overflowed counters and clears
the overflow status but never unfreezes, so everything stays frozen until the
next pmu_enable(). This patch unfreezes after clearing the status, unless the
PMU has been disabled in the meantime.
Closes: https://sashiko.dev/#/patchset/20260715191454.459673-1-dave@stgolabs.net?part=1
> diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c
> index 448e1da3d59f9..a081fcba69917 100644
> --- a/drivers/perf/cxl_pmu.c
> +++ b/drivers/perf/cxl_pmu.c
[ ... ]
> @@ -604,6 +607,7 @@ static void cxl_pmu_disable(struct pmu *pmu)
> struct cxl_pmu_info *info = pmu_to_cxl_pmu_info(pmu);
> void __iomem *base = info->base;
>
> + WRITE_ONCE(info->enabled, false);
> /*
> * Whilst bits above number of counters are RsvdZ
> * they are unlikely to be repurposed given
> * number of counters is allowed to be 64 leaving
> * no reserved bits. Hence this is only slightly
> * naughty.
> */
> writeq(GENMASK_ULL(63, 0), base + CXL_PMU_FREEZE_REG);
[Severity: High]
This isn't a bug introduced by this patch, but does writing a full 64-bit mask
to the freeze register risk fatal hardware errors on strict CXL implementations?
In cxl_pmu_disable(), the driver writes GENMASK_ULL(63, 0) to
CXL_PMU_FREEZE_REG to freeze all counters.
According to the CXL 3.0 specification, bits above the implemented number
of counters (info->num_counters) are RsvdZ, and software is strictly
required to write 0 to these fields.
Writing 1s to reserved fields could cause AER faults or Poisoned transactions.
[ ... ]
> @@ -802,6 +806,21 @@ static irqreturn_t cxl_pmu_irq(int irq, void *data)
>
> writeq(overflowed, base + CXL_PMU_OVERFLOW_REG);
>
> + /*
> + * An overflow freezes every counter in the CPMU, so unfreeze once the
> + * overflowed ones have been read and their status cleared. Otherwise
> + * they stay frozen until the next pmu_enable() and events are lost.
> + *
> + * Not while the PMU is disabled, so as not to undo an intentional freeze.
> + * The check is advisory, not exclusive: pmu_disable() normally runs on
> + * info->on_cpu with interrupts off, where the pinned handler cannot
> + * preempt it. In the one window where it does not - the migration in
> + * cxl_pmu_offline_cpu() - the counters are legitimately running again,
> + * so unfreezing is correct there anyway.
> + */
> + if (READ_ONCE(info->enabled))
[Severity: High]
Could this advisory check on info->enabled cause a cross-CPU race condition
during CPU migration?
During CPU hotplug migration, perf_pmu_migrate_context() in
cxl_pmu_offline_cpu() moves events to the target CPU, but the IRQ
affinity is not updated until afterwards.
If the IRQ fires on the old CPU, it can read a stale true for info->enabled
concurrently with the target CPU executing cxl_pmu_disable(), which sets
it to false and freezes the PMU.
The old CPU would then write 0 to unfreeze the PMU here. The target CPU,
expecting the PMU to be frozen, proceeds to cxl_pmu_event_start(), enabling
the counter via CXL_PMU_COUNTER_CFG_ENABLE and writing 0 to
CXL_PMU_COUNTER_REG.
Because the PMU was incorrectly unfrozen, the counter starts immediately,
leading to a write to a running counter, which violates the CXL 3.0 spec.
> + writeq(0, base + CXL_PMU_FREEZE_REG);
> +
> return IRQ_HANDLED;
> }
I also noticed a few other pre-existing issues in this file while reviewing.
[Severity: High]
This isn't a bug introduced by this patch, but is the use of local64_cmpxchg
in __cxl_pmu_read() safe across multiple CPUs?
drivers/perf/cxl_pmu.c:__cxl_pmu_read() {
...
do {
prev_cnt = local64_read(&hwc->prev_count);
new_cnt = cxl_pmu_read_counter(event);
} while (local64_cmpxchg(&hwc->prev_count, prev_cnt, new_cnt) != prev_cnt);
...
}
During the CPU hotplug migration window in cxl_pmu_offline_cpu(), events
are migrated to the target CPU before IRQ affinity is updated via
irq_set_affinity().
If an overflow IRQ fires in this window, it runs on the old CPU while the
target CPU can concurrently execute cxl_pmu_read().
Since local64_t only guarantees atomicity for local CPU accesses, both CPUs
iterating over the same event and concurrently executing local64_cmpxchg()
could cause a data race and silent counter corruption.
[Severity: High]
This isn't a bug introduced by this patch, but can pending overflow interrupts
be incorrectly attributed to newly scheduled events?
When a counter overflows, the hardware asserts an interrupt and sets the
corresponding bit in CXL_PMU_OVERFLOW_REG.
If the current event is deleted before the IRQ is serviced, neither
cxl_pmu_event_del() nor cxl_pmu_event_start() clears the overflow bit:
drivers/perf/cxl_pmu.c:cxl_pmu_event_start() {
...
local64_set(&hwc->prev_count, 0);
writeq(0, base + CXL_PMU_COUNTER_REG(hwc->idx));
...
}
When cxl_pmu_event_add() maps a new event to the same counter index, and
the pending IRQ is serviced, cxl_pmu_irq() calls __cxl_pmu_read(event, true).
For the new event, both new_cnt and prev_cnt are near 0, meaning the
following check is satisfied:
drivers/perf/cxl_pmu.c:__cxl_pmu_read() {
...
if (overflow && new_cnt >= prev_cnt)
delta += mask + 1;
...
}
Could this cause the driver to falsely believe a wrap occurred and corrupt
the count by adding a spurious full period?
[Severity: High]
This isn't a bug introduced by this patch, but does the stateless overflow
handling in __cxl_pmu_read() cause double-counting or lost events?
drivers/perf/cxl_pmu.c:__cxl_pmu_read() {
...
delta = (new_cnt - prev_cnt) & mask;
if (overflow && new_cnt >= prev_cnt)
delta += mask + 1;
...
}
If a polled read (overflow = false) occurs after the counter wraps and
freezes, but before the IRQ handler runs, it adds the correct delta and
updates prev_cnt.
When the IRQ handler subsequently runs, new_cnt and prev_cnt will be equal,
satisfying new_cnt >= prev_cnt.
Wouldn't the IRQ handler then erroneously add a full period on top of the
already correct count? Conversely, if the event is deleted before the IRQ
handler runs, could the full period be permanently lost?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260805155911.1304807-1-dave.jiang@intel.com?part=8
next prev parent reply other threads:[~2026-08-05 16:16 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-05 15:59 [RESEND PATCH v4 00/11] perf/cxlpmu: Misc sashiko raised issues fixes Dave Jiang
2026-08-05 15:59 ` [RESEND PATCH v4 01/11] perf/cxl: Program the requested event group on configurable counters Dave Jiang
2026-08-05 16:23 ` sashiko-bot
2026-08-05 15:59 ` [RESEND PATCH v4 02/11] perf/cxl: Clear stale event fields before reprogramming a counter Dave Jiang
2026-08-05 16:12 ` sashiko-bot
2026-08-05 15:59 ` [RESEND PATCH v4 03/11] perf/cxl: Fix the counter overflow delta fixup Dave Jiang
2026-08-05 15:59 ` [RESEND PATCH v4 04/11] perf/cxl: Accept an overflow interrupt on MSI message number 0 Dave Jiang
2026-08-05 15:59 ` [RESEND PATCH v4 05/11] perf/cxl: Split the MSI vector out of info->irq Dave Jiang
2026-08-05 15:59 ` [RESEND PATCH v4 06/11] cxl/pci: Add the PMUs after configuring events Dave Jiang
2026-08-05 16:51 ` Alison Schofield
2026-08-05 15:59 ` [RESEND PATCH v4 07/11] perf/cxl: Don't share the overflow interrupt, and keep it pinned Dave Jiang
2026-08-05 15:59 ` [RESEND PATCH v4 08/11] perf/cxl: Unfreeze counters after handling an overflow interrupt Dave Jiang
2026-08-05 16:15 ` sashiko-bot [this message]
2026-08-05 16:35 ` Dave Jiang
2026-08-05 15:59 ` [RESEND PATCH v4 09/11] perf/cxl: Validate the hardware-reported counter width Dave Jiang
2026-08-05 16:16 ` sashiko-bot
2026-08-05 15:59 ` [RESEND PATCH v4 10/11] perf/cxl: Don't log through pmu.dev in the overflow interrupt handler Dave Jiang
2026-08-05 15:59 ` [RESEND PATCH v4 11/11] perf/cxl: Clear stale overflow status before using a counter Dave Jiang
2026-08-05 16:21 ` 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=20260805161558.D1C471F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=dave.jiang@intel.com \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--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