aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2014-09-02 00:23:16 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2014-09-25 09:14:50 -0400
commitd6a4f70909d279004a2b3d539e240e07b1ecc1cb (patch)
treedc224f5ad3efe6f87ea2159249f73827a5d2f746 /arch
parent423216ed3273dae18c347ce52c5ecc193cfdd4e5 (diff)
powerpc/powernv: Don't call generic code on offline cpus
On PowerNV platforms, when a CPU is offline, we put it into nap mode. It's possible that the CPU wakes up from nap mode while it is still offline due to a stray IPI. A misdirected device interrupt could also potentially cause it to wake up. In that circumstance, we need to clear the interrupt so that the CPU can go back to nap mode. In the past the clearing of the interrupt was accomplished by briefly enabling interrupts and allowing the normal interrupt handling code (do_IRQ() etc.) to handle the interrupt. This has the problem that this code calls irq_enter() and irq_exit(), which call functions such as account_system_vtime() which use RCU internally. Use of RCU is not permitted on offline CPUs and will trigger errors if RCU checking is enabled. To avoid calling into any generic code which might use RCU, we adopt a different method of clearing interrupts on offline CPUs. Since we are on the PowerNV platform, we know that the system interrupt controller is a XICS being driven directly (i.e. not via hcalls) by the kernel. Hence this adds a new icp_native_flush_interrupt() function to the native-mode XICS driver and arranges to call that when an offline CPU is woken from nap. This new function reads the interrupt from the XICS. If it is an IPI, it clears the IPI; if it is a device interrupt, it prints a warning and disables the source. Then it does the end-of-interrupt processing for the interrupt. The other thing that briefly enabling interrupts did was to check and clear the irq_happened flag in this CPU's PACA. Therefore, after flushing the interrupt from the XICS, we also clear all bits except the PACA_IRQ_HARD_DIS (interrupts are hard disabled) bit from the irq_happened flag. The PACA_IRQ_HARD_DIS flag is set by power7_nap() and is left set to indicate that interrupts are hard disabled. This means we then have to ignore that flag in power7_nap(), which is reasonable since it doesn't indicate that any interrupt event needs servicing. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/xics.h1
-rw-r--r--arch/powerpc/kernel/idle_power7.S2
-rw-r--r--arch/powerpc/platforms/powernv/smp.c6
-rw-r--r--arch/powerpc/sysdev/xics/icp-native.c25
4 files changed, 30 insertions, 4 deletions
diff --git a/arch/powerpc/include/asm/xics.h b/arch/powerpc/include/asm/xics.h
index 282d43a0c855..0d050ea37a04 100644
--- a/arch/powerpc/include/asm/xics.h
+++ b/arch/powerpc/include/asm/xics.h
@@ -29,6 +29,7 @@
29/* Native ICP */ 29/* Native ICP */
30#ifdef CONFIG_PPC_ICP_NATIVE 30#ifdef CONFIG_PPC_ICP_NATIVE
31extern int icp_native_init(void); 31extern int icp_native_init(void);
32extern void icp_native_flush_interrupt(void);
32#else 33#else
33static inline int icp_native_init(void) { return -ENODEV; } 34static inline int icp_native_init(void) { return -ENODEV; }
34#endif 35#endif
diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S
index be05841396cf..c0754bbf8118 100644
--- a/arch/powerpc/kernel/idle_power7.S
+++ b/arch/powerpc/kernel/idle_power7.S
@@ -73,7 +73,7 @@ _GLOBAL(power7_powersave_common)
73 73
74 /* Check if something happened while soft-disabled */ 74 /* Check if something happened while soft-disabled */
75 lbz r0,PACAIRQHAPPENED(r13) 75 lbz r0,PACAIRQHAPPENED(r13)
76 cmpwi cr0,r0,0 76 andi. r0,r0,~PACA_IRQ_HARD_DIS@l
77 beq 1f 77 beq 1f
78 cmpwi cr0,r4,0 78 cmpwi cr0,r4,0
79 beq 1f 79 beq 1f
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index b73adc573031..4753958cd509 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -168,9 +168,9 @@ static void pnv_smp_cpu_kill_self(void)
168 power7_nap(1); 168 power7_nap(1);
169 ppc64_runlatch_on(); 169 ppc64_runlatch_on();
170 170
171 /* Reenable IRQs briefly to clear the IPI that woke us */ 171 /* Clear the IPI that woke us up */
172 local_irq_enable(); 172 icp_native_flush_interrupt();
173 local_irq_disable(); 173 local_paca->irq_happened &= PACA_IRQ_HARD_DIS;
174 mb(); 174 mb();
175 175
176 if (cpu_core_split_required()) 176 if (cpu_core_split_required())
diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c
index de8d9483bbe8..2fc4cf1b7557 100644
--- a/arch/powerpc/sysdev/xics/icp-native.c
+++ b/arch/powerpc/sysdev/xics/icp-native.c
@@ -155,6 +155,31 @@ static void icp_native_cause_ipi(int cpu, unsigned long data)
155 icp_native_set_qirr(cpu, IPI_PRIORITY); 155 icp_native_set_qirr(cpu, IPI_PRIORITY);
156} 156}
157 157
158/*
159 * Called when an interrupt is received on an off-line CPU to
160 * clear the interrupt, so that the CPU can go back to nap mode.
161 */
162void icp_native_flush_interrupt(void)
163{
164 unsigned int xirr = icp_native_get_xirr();
165 unsigned int vec = xirr & 0x00ffffff;
166
167 if (vec == XICS_IRQ_SPURIOUS)
168 return;
169 if (vec == XICS_IPI) {
170 /* Clear pending IPI */
171 int cpu = smp_processor_id();
172 kvmppc_set_host_ipi(cpu, 0);
173 icp_native_set_qirr(cpu, 0xff);
174 } else {
175 pr_err("XICS: hw interrupt 0x%x to offline cpu, disabling\n",
176 vec);
177 xics_mask_unknown_vec(vec);
178 }
179 /* EOI the interrupt */
180 icp_native_set_xirr(xirr);
181}
182
158void xics_wake_cpu(int cpu) 183void xics_wake_cpu(int cpu)
159{ 184{
160 icp_native_set_qirr(cpu, IPI_PRIORITY); 185 icp_native_set_qirr(cpu, IPI_PRIORITY);