powerpc/powernv: Fix CPU idle to be called with IRQs disabled
Commite78a7614f3
("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 commit2525db04d1
("powerpc/powernv: Simplify lazy IRQ handling in CPU offline"). Fixes:e78a7614f3
("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
This commit is contained in:
parent
05d9a95283
commit
7d6475051f
@ -146,20 +146,25 @@ static int pnv_smp_cpu_disable(void)
|
|||||||
return 0;
|
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)
|
static void pnv_smp_cpu_kill_self(void)
|
||||||
{
|
{
|
||||||
|
unsigned long srr1, unexpected_mask, wmask;
|
||||||
unsigned int cpu;
|
unsigned int cpu;
|
||||||
unsigned long srr1, wmask;
|
|
||||||
u64 lpcr_val;
|
u64 lpcr_val;
|
||||||
|
|
||||||
/* Standard hot unplug procedure */
|
/* 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();
|
idle_task_exit();
|
||||||
current->active_mm = NULL; /* for sanity */
|
current->active_mm = NULL; /* for sanity */
|
||||||
@ -172,6 +177,27 @@ static void pnv_smp_cpu_kill_self(void)
|
|||||||
if (cpu_has_feature(CPU_FTR_ARCH_207S))
|
if (cpu_has_feature(CPU_FTR_ARCH_207S))
|
||||||
wmask = SRR1_WAKEMASK_P8;
|
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
|
* We don't want to take decrementer interrupts while we are
|
||||||
* offline, so clear LPCR:PECE1. We keep PECE2 (and
|
* offline, so clear LPCR:PECE1. We keep PECE2 (and
|
||||||
@ -197,6 +223,7 @@ static void pnv_smp_cpu_kill_self(void)
|
|||||||
|
|
||||||
srr1 = pnv_cpu_offline(cpu);
|
srr1 = pnv_cpu_offline(cpu);
|
||||||
|
|
||||||
|
WARN_ON_ONCE(!irqs_disabled());
|
||||||
WARN_ON(lazy_irq_pending());
|
WARN_ON(lazy_irq_pending());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -212,13 +239,7 @@ static void pnv_smp_cpu_kill_self(void)
|
|||||||
*/
|
*/
|
||||||
if (((srr1 & wmask) == SRR1_WAKEEE) ||
|
if (((srr1 & wmask) == SRR1_WAKEEE) ||
|
||||||
((srr1 & wmask) == SRR1_WAKEHVI)) {
|
((srr1 & wmask) == SRR1_WAKEHVI)) {
|
||||||
if (cpu_has_feature(CPU_FTR_ARCH_300)) {
|
pnv_flush_interrupts();
|
||||||
if (xive_enabled())
|
|
||||||
xive_flush_interrupt();
|
|
||||||
else
|
|
||||||
icp_opal_flush_interrupt();
|
|
||||||
} else
|
|
||||||
icp_native_flush_interrupt();
|
|
||||||
} else if ((srr1 & wmask) == SRR1_WAKEHDBELL) {
|
} else if ((srr1 & wmask) == SRR1_WAKEHDBELL) {
|
||||||
unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER);
|
unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER);
|
||||||
asm volatile(PPC_MSGCLR(%0) : : "r" (msg));
|
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;
|
lpcr_val = mfspr(SPRN_LPCR) | (u64)LPCR_PECE1;
|
||||||
pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val);
|
pnv_program_cpu_hotplug_lpcr(cpu, lpcr_val);
|
||||||
|
out:
|
||||||
DBG("CPU%d coming online...\n", cpu);
|
DBG("CPU%d coming online...\n", cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user