[RESEND PATCH v4 11/11] perf/cxl: Clear stale overflow status before using a counter
Dave Jiang <[email protected]> Wed, 5 Aug 2026 08:59:11 -0700
| Newsgroups | org.kernel.vger.linux-cxl,org.kernel.vger.linux-perf-users |
|---|---|
| Message-ID | <[email protected]> |
cxl_pmu_event_stop() leaves the counter's bit set in the overflow status
register and only cxl_pmu_irq() ever clears it, so a counter handed from
one event to the next starts with the previous owner's overflow pending.
The perf core stops and starts back to back with interrupts off, so an
interrupt latched over the handover lands on the new event. prev_count is 0
by then, so the read path adds a full period: 2^counter_width phantom
counts.
Clear the bit in cxl_pmu_event_start(), before the config write arms
Interrupt on Overflow. Clear the whole register at probe too, for counters
no event ever owns - the driver never touched it, so firmware or a previous
kernel can leave a bit set there.
Fixes: 5d7107c72796 ("perf: CXL Performance Monitoring Unit driver")
Reported-by: Richard Cheng <[email protected]>
Closes: https://lore.kernel.org/linux-cxl/anAVpO--i5heVfz9@MWDK4CY14F/
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Dave Jiang <[email protected]>
---
v4:
- New patch. The event_start() clear is Richard's; the probe clear was a
second hunk on the pmu.dev patch in v3.
- Clear before the config write rather than after it, so the question of
whether arming the interrupt over a set status bit can raise an MSI does
not arise (sashiko-bot).
---
drivers/perf/cxl_pmu.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c
index 45edef53a983..3ec8933b04e2 100644
--- a/drivers/perf/cxl_pmu.c
+++ b/drivers/perf/cxl_pmu.c
@@ -683,6 +683,15 @@ static void cxl_pmu_event_start(struct perf_event *event, int flags)
*/
cfg |= FIELD_PREP(CXL_PMU_COUNTER_CFG_THRESHOLD_MSK,
cxl_pmu_config1_get_threshold(event));
+
+ /*
+ * Drop any overflow the previous owner of this counter left pending;
+ * cxl_pmu_event_stop() does not. Otherwise an interrupt latched over
+ * the handover gets charged to this event. Do it before arming Interrupt
+ * on Overflow below, and note RW1C leaves the other counters alone.
+ */
+ writeq(BIT_ULL(hwc->idx), base + CXL_PMU_OVERFLOW_REG);
+
writeq(cfg, base + CXL_PMU_COUNTER_CFG_REG(hwc->idx));
local64_set(&hwc->prev_count, 0);
@@ -914,6 +923,14 @@ static int cxl_pmu_probe(struct device *dev)
if (!irq_name)
return -ENOMEM;
+ /*
+ * Same for counters no event owns yet: clear whatever firmware or a
+ * previous kernel left set before the handler goes live. Bits above the
+ * implemented counters are reserved, so only write those.
+ */
+ writeq(GENMASK_ULL(info->num_counters - 1, 0),
+ info->base + CXL_PMU_OVERFLOW_REG);
+
/*
* The handler must run on info->on_cpu, so the interrupt cannot be
* shared - IRQF_NOBALANCING is only honoured for the first action on a
--
2.54.0