| From 26bfb2463a63eaf318aaa0cf2bbb867d5e88c8df Mon Sep 17 00:00:00 2001 |
| From: Nicholas Piggin <npiggin@gmail.com> |
| Date: Tue, 22 Oct 2019 21:58:14 +1000 |
| Subject: [PATCH] powerpc/powernv: Fix CPU idle to be called with IRQs disabled |
| |
| commit 7d6475051fb3d9339c5c760ed9883bc0a9048b21 upstream. |
| |
| Commit e78a7614f3876 ("idle: Prevent late-arriving interrupts from |
| disrupting offline") changes arch_cpu_idle_dead to be called with |
| interrupts disabled, which triggers the WARN in pnv_smp_cpu_kill_self. |
| |
| Fix this by fixing up irq_happened after hard disabling, rather than |
| requiring there are no pending interrupts, similarly to what was done |
| done until commit 2525db04d1cc5 ("powerpc/powernv: Simplify lazy IRQ |
| handling in CPU offline"). |
| |
| Fixes: e78a7614f3876 ("idle: Prevent late-arriving interrupts from disrupting offline") |
| Reported-by: Paul Mackerras <paulus@samba.org> |
| Signed-off-by: Nicholas Piggin <npiggin@gmail.com> |
| [mpe: Add unexpected_mask rather than checking for known bad values, |
| change the WARN_ON() to a WARN_ON_ONCE()] |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Link: https://lore.kernel.org/r/20191022115814.22456-1-npiggin@gmail.com |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c |
| index 94cd96b9b7bb..001dfac8354a 100644 |
| --- a/arch/powerpc/platforms/powernv/smp.c |
| +++ b/arch/powerpc/platforms/powernv/smp.c |
| @@ -146,20 +146,25 @@ static int pnv_smp_cpu_disable(void) |
| return 0; |
| } |
| |
| +static void pnv_flush_interrupts(void) |
| +{ |
| + if (cpu_has_feature(CPU_FTR_ARCH_300)) { |
| + if (xive_enabled()) |
| + xive_flush_interrupt(); |
| + else |
| + icp_opal_flush_interrupt(); |
| + } else { |
| + icp_native_flush_interrupt(); |
| + } |
| +} |
| + |
| static void pnv_smp_cpu_kill_self(void) |
| { |
| + unsigned long srr1, unexpected_mask, wmask; |
| unsigned int cpu; |
| - unsigned long srr1, wmask; |
| u64 lpcr_val; |
| |
| /* Standard hot unplug procedure */ |
| - /* |
| - * This hard disables local interurpts, ensuring we have no lazy |
| - * irqs pending. |
| - */ |
| - WARN_ON(irqs_disabled()); |
| - hard_irq_disable(); |
| - WARN_ON(lazy_irq_pending()); |
| |
| idle_task_exit(); |
| current->active_mm = NULL; /* for sanity */ |
| @@ -173,6 +178,27 @@ static void pnv_smp_cpu_kill_self(void) |
| wmask = SRR1_WAKEMASK_P8; |
| |
| /* |
| + * This turns the irq soft-disabled state we're called with, into a |
| + * hard-disabled state with pending irq_happened interrupts cleared. |
| + * |
| + * PACA_IRQ_DEC - Decrementer should be ignored. |
| + * PACA_IRQ_HMI - Can be ignored, processing is done in real mode. |
| + * PACA_IRQ_DBELL, EE, PMI - Unexpected. |
| + */ |
| + hard_irq_disable(); |
| + if (generic_check_cpu_restart(cpu)) |
| + goto out; |
| + |
| + unexpected_mask = ~(PACA_IRQ_DEC | PACA_IRQ_HMI | PACA_IRQ_HARD_DIS); |
| + if (local_paca->irq_happened & unexpected_mask) { |
| + if (local_paca->irq_happened & PACA_IRQ_EE) |
| + pnv_flush_interrupts(); |
| + DBG("CPU%d Unexpected exit while offline irq_happened=%lx!\n", |
| + cpu, local_paca->irq_happened); |
| + } |
| + local_paca->irq_happened = PACA_IRQ_HARD_DIS; |
| + |
| + /* |
| * We don't want to take decrementer interrupts while we are |
| * offline, so clear LPCR:PECE1. We keep PECE2 (and |
| * LPCR_PECE_HVEE on P9) enabled so as to let IPIs in. |
| @@ -197,6 +223,7 @@ static void pnv_smp_cpu_kill_self(void) |
| |
| srr1 = pnv_cpu_offline(cpu); |
| |
| + WARN_ON_ONCE(!irqs_disabled()); |
| WARN_ON(lazy_irq_pending()); |
| |
| /* |
| @@ -212,13 +239,7 @@ static void pnv_smp_cpu_kill_self(void) |
| */ |
| if (((srr1 & wmask) == SRR1_WAKEEE) || |
| ((srr1 & wmask) == SRR1_WAKEHVI)) { |
| - if (cpu_has_feature(CPU_FTR_ARCH_300)) { |
| - if (xive_enabled()) |
| - xive_flush_interrupt(); |
| - else |
| - icp_opal_flush_interrupt(); |
| - } else |
| - icp_native_flush_interrupt(); |
| + pnv_flush_interrupts(); |
| } else if ((srr1 & wmask) == SRR1_WAKEHDBELL) { |
| unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER); |
| asm volatile(PPC_MSGCLR(%0) : : "r" (msg)); |
| @@ -266,7 +287,7 @@ static void pnv_smp_cpu_kill_self(void) |
| */ |
| lpcr_val = mfspr(SPRN_LPCR) | (u64)LPCR_PECE1; |
| pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val); |
| - |
| +out: |
| DBG("CPU%d coming online...\n", cpu); |
| } |
| |
| -- |
| 2.7.4 |
| |