diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h
index 56a98936a6a9..7e192bd8253b 100644
--- a/arch/powerpc/include/asm/hw_irq.h
+++ b/arch/powerpc/include/asm/hw_irq.h
@@ -215,6 +215,23 @@ static inline bool arch_irqs_disabled(void)
return arch_irqs_disabled_flags(arch_local_save_flags());
}
+static inline int get_clear_pmi_irq_pending(void)
+{
+ /*
+ * Some corner cases could clear the PMU counter overflow
+ * while a masked PMI is pending. One of such case is
+ * when a PMI happens during interrupt replay and perf
+ * counter values gets cleared by PMU callbacks before
+ * replay. So the pending PMI must be cleared here.
+ */
+ if (get_paca()->irq_happened & PACA_IRQ_PMI) {
+ WARN_ON_ONCE(mfmsr() & MSR_EE);
+ get_paca()->irq_happened &= ~PACA_IRQ_PMI;
+ return 1;
+ }
+ return 0;
+}
+
#ifdef CONFIG_PPC_BOOK3S
/*
* To support disabling and enabling of irq with PMI, set of@@ -391,6 +408,8 @@ static inline bool arch_irq_disabled_regs(struct pt_regs *regs)
static inline void may_hard_irq_enable(void) { }
+static inline int get_clear_pmi_irq_pending(void) { return 0; }
+
static inline void irq_soft_mask_regs_set_state(struct pt_regs *regs, unsigned long val)
{
}diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index 766f064f00fb..6ddac1b913fa 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -847,6 +847,20 @@ static void write_pmc(int idx, unsigned long val)
}
}
+static int pmc_overflown(int idx)
+{
+ unsigned long val[8];
+ int i;
+
+ for (i = 0; i < ppmu->n_counter; i++)
+ val[i] = read_pmc(i + 1);
+
+ if ((int)val[idx-1] < 0)
+ return 1;
+
+ return 0;
+}
+
/* Called from sysrq_handle_showregs() */
void perf_event_print_debug(void)
{@@ -1438,6 +1452,17 @@ static void power_pmu_enable(struct pmu *pmu)
event = cpuhw->event[i];
if (event->hw.idx && event->hw.idx != hwc_index[i] + 1) {
power_pmu_read(event);
+ /*
+ * if the PMC corresponding to event->hw.idx is
+ * overflown, check if there is any pending perf
+ * interrupt set in paca. If so, disable the interrupt
+ * by clearing the paca bit for PMI since we are going
+ * to reset the PMC. power_pmu_enable will reset PMAO bit
+ * of MMCR0 while enabling the event. So PMAO check
+ * is not needed here (versus PMAO check done in del/stop).
+ */
+ if (pmc_overflown(event->hw.idx))
+ get_clear_pmi_irq_pending();
write_pmc(event->hw.idx, 0);
event->hw.idx = 0;
}@@ -1474,6 +1499,10 @@ static void power_pmu_enable(struct pmu *pmu)
event->hw.idx = idx;
if (event->hw.state & PERF_HES_STOPPED)
val = 0;
+
+ /* See above for get_clear_pmi_irq_pending */
+ if (pmc_overflown(event->hw.idx))
+ get_clear_pmi_irq_pending();
write_pmc(idx, val);
perf_event_update_userpage(event);
@@ -1619,6 +1648,7 @@ static void power_pmu_del(struct perf_event *event, int ef_flags)
struct cpu_hw_events *cpuhw;
long i;
unsigned long flags;
+ unsigned long val_mmcr0;
local_irq_save(flags);
perf_pmu_disable(event->pmu);
@@ -1636,6 +1666,23 @@ static void power_pmu_del(struct perf_event *event, int ef_flags)
--cpuhw->n_events;
ppmu->disable_pmc(event->hw.idx - 1, &cpuhw->mmcr);
if (event->hw.idx) {
+ /*
+ * if the PMC corresponding to event->hw.idx is
+ * overflown, check if there is any pending perf
+ * interrupt set in paca or indicated by PMAO bit
+ * in MMCR0. If so, disable the interrupt and clear
+ * the MMCR0 PMAO bit since we are going to reset
+ * the PMC and delete the event.
+ */
+ if (pmc_overflown(event->hw.idx)) {
+ if ((get_clear_pmi_irq_pending() | (mfspr(SPRN_MMCR0) & MMCR0_PMAO))) {
+ val_mmcr0 = mfspr(SPRN_MMCR0);
+ val_mmcr0 &= ~MMCR0_PMAO;
+ write_mmcr0(cpuhw, val_mmcr0);
+ mb();
+ isync();
+ }
+ }
write_pmc(event->hw.idx, 0);
event->hw.idx = 0;
}@@ -1705,6 +1752,8 @@ static void power_pmu_start(struct perf_event *event, int ef_flags)
static void power_pmu_stop(struct perf_event *event, int ef_flags)
{
unsigned long flags;
+ unsigned long val_mmcr0;
+ struct cpu_hw_events *cpuhw;
if (!event->hw.idx || !event->hw.sample_period)
return;@@ -1713,8 +1762,27 @@ static void power_pmu_stop(struct perf_event *event, int ef_flags)
return;
local_irq_save(flags);
+ cpuhw = this_cpu_ptr(&cpu_hw_events);
perf_pmu_disable(event->pmu);
+ /*
+ * if the PMC corresponding to event->hw.idx is
+ * overflown, check if there is any pending perf
+ * interrupt set in paca or indicated by PMAO bit
+ * in MMCR0. If so, disable the interrupt and clear
+ * the MMCR0 PMAO bit since we are going to reset
+ * the PMC.
+ */
+ if (pmc_overflown(event->hw.idx)) {
+ if ((get_clear_pmi_irq_pending() | (mfspr(SPRN_MMCR0) & MMCR0_PMAO))) {
+ val_mmcr0 = mfspr(SPRN_MMCR0);
+ val_mmcr0 &= ~MMCR0_PMAO;
+ write_mmcr0(cpuhw, val_mmcr0);
+ mb();
+ isync();
+ }
+ }
+
power_pmu_read(event);
event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
write_pmc(event->hw.idx, 0);@@ -2343,6 +2411,15 @@ static void __perf_event_interrupt(struct pt_regs *regs)
}
}
}
+
+ /*
+ * During system wide profling or while specific CPU
+ * is monitored for an event, some corner cases could
+ * cause PMC to overflow in idle path. This will trigger
+ * a PMI after waking up from idle. Since counter values
+ * are _not_ saved/restored in idle path, can lead to
+ * below "Can't find PMC" message.
+ */
if (unlikely(!found) && !arch_irq_disabled_regs(regs))
printk_ratelimited(KERN_WARNING "Can't find PMC that caused IRQ\n");
--
2.26.2