powerpc/64s/idle: Process interrupts from system reset wakeup
authorNicholas Piggin <npiggin@gmail.com>
Tue, 13 Jun 2017 13:05:47 +0000 (23:05 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 19 Jun 2017 09:46:27 +0000 (19:46 +1000)
When the CPU wakes from low power state, it begins at the system reset
interrupt with the exception that caused the wakeup encoded in SRR1.

Today, powernv idle wakeup ignores the wakeup reason (except a special
case for HMI), and the regular interrupt corresponding to the
exception will fire after the idle wakeup exits.

Change this to replay the interrupt from the idle wakeup before
interrupts are hard-enabled.

Test on POWER8 of context_switch selftests benchmark with polling idle
disabled (e.g., always nap, giving cross-CPU IPIs) gives the following
results:

                                original         wakeup direct
Different threads, same core:   315k/s           264k/s
Different cores:                235k/s           242k/s

There is a slowdown for doorbell IPI (same core) case because system
reset wakeup does not clear the message and the doorbell interrupt
fires again needlessly.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/hw_irq.h
arch/powerpc/kernel/irq.c
arch/powerpc/platforms/powernv/idle.c

index f06112cf8734708d751a43f9cc910de640f0fb83..c1dd1929342d6381b6e5df9649dc1bb5aadcde85 100644 (file)
@@ -130,6 +130,7 @@ static inline bool arch_irq_disabled_regs(struct pt_regs *regs)
 
 extern bool prep_irq_for_idle(void);
 extern bool prep_irq_for_idle_irqsoff(void);
+extern void irq_set_pending_from_srr1(unsigned long srr1);
 
 #define fini_irq_for_idle_irqsoff() trace_hardirqs_off();
 
index 58dcac88bc7943f2939cafb79f5c8201e5afe4fb..0bcec745a6724771c076e34132fb40e158b318f1 100644 (file)
@@ -348,6 +348,7 @@ bool prep_irq_for_idle(void)
        return true;
 }
 
+#ifdef CONFIG_PPC_BOOK3S
 /*
  * This is for idle sequences that return with IRQs off, but the
  * idle state itself wakes on interrupt. Tell the irq tracer that
@@ -378,6 +379,34 @@ bool prep_irq_for_idle_irqsoff(void)
        return true;
 }
 
+/*
+ * Take the SRR1 wakeup reason, index into this table to find the
+ * appropriate irq_happened bit.
+ */
+static const u8 srr1_to_lazyirq[0x10] = {
+       0, 0, 0,
+       PACA_IRQ_DBELL,
+       0,
+       PACA_IRQ_DBELL,
+       PACA_IRQ_DEC,
+       0,
+       PACA_IRQ_EE,
+       PACA_IRQ_EE,
+       PACA_IRQ_HMI,
+       0, 0, 0, 0, 0 };
+
+void irq_set_pending_from_srr1(unsigned long srr1)
+{
+       unsigned int idx = (srr1 & SRR1_WAKEMASK_P8) >> 18;
+
+       /*
+        * The 0 index (SRR1[42:45]=b0000) must always evaluate to 0,
+        * so this can be called unconditionally with srr1 wake reason.
+        */
+       local_paca->irq_happened |= srr1_to_lazyirq[idx];
+}
+#endif /* CONFIG_PPC_BOOK3S */
+
 /*
  * Force a replay of the external interrupt handler on this CPU.
  */
index f188d84d9c593660e9ed6fe5ae2ab492667231b7..1028df82cd2f906b0e923c6ea613dc51ba1a4b92 100644 (file)
@@ -302,7 +302,10 @@ static unsigned long __power7_idle_type(unsigned long type)
 
 void power7_idle_type(unsigned long type)
 {
-       __power7_idle_type(type);
+       unsigned long srr1;
+
+       srr1 = __power7_idle_type(type);
+       irq_set_pending_from_srr1(srr1);
 }
 
 void power7_idle(void)
@@ -337,7 +340,10 @@ static unsigned long __power9_idle_type(unsigned long stop_psscr_val,
 void power9_idle_type(unsigned long stop_psscr_val,
                                      unsigned long stop_psscr_mask)
 {
-       __power9_idle_type(stop_psscr_val, stop_psscr_mask);
+       unsigned long srr1;
+
+       srr1 = __power9_idle_type(stop_psscr_val, stop_psscr_mask);
+       irq_set_pending_from_srr1(srr1);
 }
 
 /*