MIPS: Switch to the irq_stack in interrupts
authorMatt Redfearn <matt.redfearn@imgtec.com>
Mon, 19 Dec 2016 14:20:59 +0000 (14:20 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Apr 2017 05:14:36 +0000 (07:14 +0200)
commit dda45f701c9d7ad4ac0bb446e3a96f6df9a468d9 upstream.

When enterring interrupt context via handle_int or except_vec_vi, switch
to the irq_stack of the current CPU if it is not already in use.

The current stack pointer is masked with the thread size and compared to
the base or the irq stack. If it does not match then the stack pointer
is set to the top of that stack, otherwise this is a nested irq being
handled on the irq stack so the stack pointer should be left as it was.

The in-use stack pointer is placed in the callee saved register s1. It
will be saved to the stack when plat_irq_dispatch is invoked and can be
restored once control returns here.

Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
Acked-by: Jason A. Donenfeld <jason@zx2c4.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: James Hogan <james.hogan@imgtec.com>
Cc: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/14743/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/mips/kernel/genex.S

index baa7b6fc0a60b1879976c2d4158f73d01e0ca53b..2c7cd622673ff1e80126443d135e5fd4c6fa106d 100644 (file)
@@ -188,9 +188,44 @@ NESTED(handle_int, PT_SIZE, sp)
 
        LONG_L  s0, TI_REGS($28)
        LONG_S  sp, TI_REGS($28)
-       PTR_LA  ra, ret_from_irq
-       PTR_LA  v0, plat_irq_dispatch
-       jr      v0
+
+       /*
+        * SAVE_ALL ensures we are using a valid kernel stack for the thread.
+        * Check if we are already using the IRQ stack.
+        */
+       move    s1, sp # Preserve the sp
+
+       /* Get IRQ stack for this CPU */
+       ASM_CPUID_MFC0  k0, ASM_SMP_CPUID_REG
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+       lui     k1, %hi(irq_stack)
+#else
+       lui     k1, %highest(irq_stack)
+       daddiu  k1, %higher(irq_stack)
+       dsll    k1, 16
+       daddiu  k1, %hi(irq_stack)
+       dsll    k1, 16
+#endif
+       LONG_SRL        k0, SMP_CPUID_PTRSHIFT
+       LONG_ADDU       k1, k0
+       LONG_L  t0, %lo(irq_stack)(k1)
+
+       # Check if already on IRQ stack
+       PTR_LI  t1, ~(_THREAD_SIZE-1)
+       and     t1, t1, sp
+       beq     t0, t1, 2f
+
+       /* Switch to IRQ stack */
+       li      t1, _IRQ_STACK_SIZE
+       PTR_ADD sp, t0, t1
+
+2:
+       jal     plat_irq_dispatch
+
+       /* Restore sp */
+       move    sp, s1
+
+       j       ret_from_irq
 #ifdef CONFIG_CPU_MICROMIPS
        nop
 #endif
@@ -263,8 +298,44 @@ NESTED(except_vec_vi_handler, 0, sp)
 
        LONG_L  s0, TI_REGS($28)
        LONG_S  sp, TI_REGS($28)
-       PTR_LA  ra, ret_from_irq
-       jr      v0
+
+       /*
+        * SAVE_ALL ensures we are using a valid kernel stack for the thread.
+        * Check if we are already using the IRQ stack.
+        */
+       move    s1, sp # Preserve the sp
+
+       /* Get IRQ stack for this CPU */
+       ASM_CPUID_MFC0  k0, ASM_SMP_CPUID_REG
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+       lui     k1, %hi(irq_stack)
+#else
+       lui     k1, %highest(irq_stack)
+       daddiu  k1, %higher(irq_stack)
+       dsll    k1, 16
+       daddiu  k1, %hi(irq_stack)
+       dsll    k1, 16
+#endif
+       LONG_SRL        k0, SMP_CPUID_PTRSHIFT
+       LONG_ADDU       k1, k0
+       LONG_L  t0, %lo(irq_stack)(k1)
+
+       # Check if already on IRQ stack
+       PTR_LI  t1, ~(_THREAD_SIZE-1)
+       and     t1, t1, sp
+       beq     t0, t1, 2f
+
+       /* Switch to IRQ stack */
+       li      t1, _IRQ_STACK_SIZE
+       PTR_ADD sp, t0, t1
+
+2:
+       jal     plat_irq_dispatch
+
+       /* Restore sp */
+       move    sp, s1
+
+       j       ret_from_irq
        END(except_vec_vi_handler)
 
 /*