Commit 9b256714 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Michael Ellerman

powerpc/powernv: Fix CPU hotplug to handle waking on HVI

The IPIs come in as HVI not EE, so we need to test the appropriate
SRR1 bits. The encoding is such that it won't have false positives
on P7 and P8 so we can just test it like that. We also need to handle
the icp-opal variant of the flush.

Fixes: d7436188 ("powerpc/xics: Add ICP OPAL backend")
Cc: stable@vger.kernel.org # v4.8+
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 90c1e3c2
...@@ -649,9 +649,10 @@ ...@@ -649,9 +649,10 @@
#define SRR1_ISI_N_OR_G 0x10000000 /* ISI: Access is no-exec or G */ #define SRR1_ISI_N_OR_G 0x10000000 /* ISI: Access is no-exec or G */
#define SRR1_ISI_PROT 0x08000000 /* ISI: Other protection fault */ #define SRR1_ISI_PROT 0x08000000 /* ISI: Other protection fault */
#define SRR1_WAKEMASK 0x00380000 /* reason for wakeup */ #define SRR1_WAKEMASK 0x00380000 /* reason for wakeup */
#define SRR1_WAKEMASK_P8 0x003c0000 /* reason for wakeup on POWER8 */ #define SRR1_WAKEMASK_P8 0x003c0000 /* reason for wakeup on POWER8 and 9 */
#define SRR1_WAKESYSERR 0x00300000 /* System error */ #define SRR1_WAKESYSERR 0x00300000 /* System error */
#define SRR1_WAKEEE 0x00200000 /* External interrupt */ #define SRR1_WAKEEE 0x00200000 /* External interrupt */
#define SRR1_WAKEHVI 0x00240000 /* Hypervisor Virtualization Interrupt (P9) */
#define SRR1_WAKEMT 0x00280000 /* mtctrl */ #define SRR1_WAKEMT 0x00280000 /* mtctrl */
#define SRR1_WAKEHMI 0x00280000 /* Hypervisor maintenance */ #define SRR1_WAKEHMI 0x00280000 /* Hypervisor maintenance */
#define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */ #define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */
......
...@@ -44,6 +44,7 @@ static inline int icp_hv_init(void) { return -ENODEV; } ...@@ -44,6 +44,7 @@ static inline int icp_hv_init(void) { return -ENODEV; }
#ifdef CONFIG_PPC_POWERNV #ifdef CONFIG_PPC_POWERNV
extern int icp_opal_init(void); extern int icp_opal_init(void);
extern void icp_opal_flush_interrupt(void);
#else #else
static inline int icp_opal_init(void) { return -ENODEV; } static inline int icp_opal_init(void) { return -ENODEV; }
#endif #endif
......
...@@ -155,8 +155,10 @@ static void pnv_smp_cpu_kill_self(void) ...@@ -155,8 +155,10 @@ static void pnv_smp_cpu_kill_self(void)
wmask = SRR1_WAKEMASK_P8; wmask = SRR1_WAKEMASK_P8;
idle_states = pnv_get_supported_cpuidle_states(); idle_states = pnv_get_supported_cpuidle_states();
/* We don't want to take decrementer interrupts while we are offline, /* We don't want to take decrementer interrupts while we are offline,
* so clear LPCR:PECE1. We keep PECE2 enabled. * so clear LPCR:PECE1. We keep PECE2 (and LPCR_PECE_HVEE on P9)
* enabled as to let IPIs in.
*/ */
mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
...@@ -206,8 +208,12 @@ static void pnv_smp_cpu_kill_self(void) ...@@ -206,8 +208,12 @@ static void pnv_smp_cpu_kill_self(void)
* contains 0. * contains 0.
*/ */
if (((srr1 & wmask) == SRR1_WAKEEE) || if (((srr1 & wmask) == SRR1_WAKEEE) ||
((srr1 & wmask) == SRR1_WAKEHVI) ||
(local_paca->irq_happened & PACA_IRQ_EE)) { (local_paca->irq_happened & PACA_IRQ_EE)) {
icp_native_flush_interrupt(); if (cpu_has_feature(CPU_FTR_ARCH_300))
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));
...@@ -221,6 +227,8 @@ static void pnv_smp_cpu_kill_self(void) ...@@ -221,6 +227,8 @@ static void pnv_smp_cpu_kill_self(void)
if (srr1 && !generic_check_cpu_restart(cpu)) if (srr1 && !generic_check_cpu_restart(cpu))
DBG("CPU%d Unexpected exit while offline !\n", cpu); DBG("CPU%d Unexpected exit while offline !\n", cpu);
} }
/* Re-enable decrementer interrupts */
mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1);
DBG("CPU%d coming online...\n", cpu); DBG("CPU%d coming online...\n", cpu);
} }
......
...@@ -132,6 +132,35 @@ static irqreturn_t icp_opal_ipi_action(int irq, void *dev_id) ...@@ -132,6 +132,35 @@ static irqreturn_t icp_opal_ipi_action(int irq, void *dev_id)
return smp_ipi_demux(); return smp_ipi_demux();
} }
/*
* Called when an interrupt is received on an off-line CPU to
* clear the interrupt, so that the CPU can go back to nap mode.
*/
void icp_opal_flush_interrupt(void)
{
unsigned int xirr;
unsigned int vec;
do {
xirr = icp_opal_get_xirr();
vec = xirr & 0x00ffffff;
if (vec == XICS_IRQ_SPURIOUS)
break;
if (vec == XICS_IPI) {
/* Clear pending IPI */
int cpu = smp_processor_id();
kvmppc_set_host_ipi(cpu, 0);
opal_int_set_mfrr(get_hard_smp_processor_id(cpu), 0xff);
} else {
pr_err("XICS: hw interrupt 0x%x to offline cpu, "
"disabling\n", vec);
xics_mask_unknown_vec(vec);
}
/* EOI the interrupt */
} while (opal_int_eoi(xirr) > 0);
}
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
static const struct icp_ops icp_opal_ops = { static const struct icp_ops icp_opal_ops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment