powerpc/powernv: Don't call generic code on offline cpus
authorPaul Mackerras <paulus@samba.org>
Tue, 2 Sep 2014 04:23:16 +0000 (14:23 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 25 Sep 2014 13:14:50 +0000 (23:14 +1000)
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>
arch/powerpc/include/asm/xics.h
arch/powerpc/kernel/idle_power7.S
arch/powerpc/platforms/powernv/smp.c
arch/powerpc/sysdev/xics/icp-native.c

index 282d43a0c85566927755dc71d6a2ce9b5bd40d4a..0d050ea37a0489a90eae1d844be6400e4eb9965d 100644 (file)
@@ -29,6 +29,7 @@
 /* Native ICP */
 #ifdef CONFIG_PPC_ICP_NATIVE
 extern int icp_native_init(void);
+extern void icp_native_flush_interrupt(void);
 #else
 static inline int icp_native_init(void) { return -ENODEV; }
 #endif
index be05841396cff00d6095e8a9edecd6e4fed7ffc3..c0754bbf81181c93c36cd8e1b12776b5ab729463 100644 (file)
@@ -73,7 +73,7 @@ _GLOBAL(power7_powersave_common)
 
        /* Check if something happened while soft-disabled */
        lbz     r0,PACAIRQHAPPENED(r13)
-       cmpwi   cr0,r0,0
+       andi.   r0,r0,~PACA_IRQ_HARD_DIS@l
        beq     1f
        cmpwi   cr0,r4,0
        beq     1f
index b73adc57303152e16f991fd2486c5128ee31fb4e..4753958cd509bdfeec1be8bf088eb69e32077569 100644 (file)
@@ -168,9 +168,9 @@ static void pnv_smp_cpu_kill_self(void)
                power7_nap(1);
                ppc64_runlatch_on();
 
-               /* Reenable IRQs briefly to clear the IPI that woke us */
-               local_irq_enable();
-               local_irq_disable();
+               /* Clear the IPI that woke us up */
+               icp_native_flush_interrupt();
+               local_paca->irq_happened &= PACA_IRQ_HARD_DIS;
                mb();
 
                if (cpu_core_split_required())
index de8d9483bbe89136042d0685680c6bddc815849a..2fc4cf1b75575f9916e26fcf330cfce4959f39a5 100644 (file)
@@ -155,6 +155,31 @@ static void icp_native_cause_ipi(int cpu, unsigned long data)
                icp_native_set_qirr(cpu, IPI_PRIORITY);
 }
 
+/*
+ * 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_native_flush_interrupt(void)
+{
+       unsigned int xirr = icp_native_get_xirr();
+       unsigned int vec = xirr & 0x00ffffff;
+
+       if (vec == XICS_IRQ_SPURIOUS)
+               return;
+       if (vec == XICS_IPI) {
+               /* Clear pending IPI */
+               int cpu = smp_processor_id();
+               kvmppc_set_host_ipi(cpu, 0);
+               icp_native_set_qirr(cpu, 0xff);
+       } else {
+               pr_err("XICS: hw interrupt 0x%x to offline cpu, disabling\n",
+                      vec);
+               xics_mask_unknown_vec(vec);
+       }
+       /* EOI the interrupt */
+       icp_native_set_xirr(xirr);
+}
+
 void xics_wake_cpu(int cpu)
 {
        icp_native_set_qirr(cpu, IPI_PRIORITY);