diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c index f6c4a1b598d8..5e46da55281a 100644 --- a/drivers/perf/cxl_pmu.c +++ b/drivers/perf/cxl_pmu.c @@ -113,6 +113,8 @@ struct cxl_pmu_info { bool filter_crb; int msi_vec; int irq; + /* Set between pmu_enable() and pmu_disable(), read by the IRQ handler */ + bool enabled; }; #define pmu_to_cxl_pmu_info(_pmu) container_of(_pmu, struct cxl_pmu_info, pmu) @@ -701,6 +703,7 @@ static void cxl_pmu_enable(struct pmu *pmu) void __iomem *base = info->base; /* Can assume frozen at this stage */ + WRITE_ONCE(info->enabled, true); writeq(0, base + CXL_PMU_FREEZE_REG); } @@ -709,6 +712,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 @@ -915,6 +919,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)) + writeq(0, base + CXL_PMU_FREEZE_REG); + return IRQ_HANDLED; }