kick_handler() doesn't have an irq_enter/exit pair, but it's used for
handling SMP IPIs which require work to be done in softirqs, which are
invoked from irq_exit() when the hard irq nest count reaches 0.

The scheduler_ipi() callback in the IPI handler calls irq_enter/exit
itself, but this is inside kick_handler()'s spin lock critical section,
so if an invoked softirq issues an IPI the kick_handler() will be
re-entered on the same CPU and will deadlock.

This is easily fixed by adding the missing irq_enter/exit to
kick_handler() so that the hard irq nest count doesn't reach 0 until
after the spin lock has been released.

Ideally the spin lock protected handler list will also be replaced by a
lockless RCU protected list since it is certainly mostly read. That can
be done in a later change though.

Signed-off-by: James Hogan <james.ho...@imgtec.com>
Cc: Thomas Gleixner <t...@linutronix.de>
---
Changes in v2:
 - Reworded commit message to hopefully be less confusing (previously
   "metag: kick: prevent nested kick handlers").

 arch/metag/kernel/kick.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/metag/kernel/kick.c b/arch/metag/kernel/kick.c
index 50fcbec..beb3776 100644
--- a/arch/metag/kernel/kick.c
+++ b/arch/metag/kernel/kick.c
@@ -26,6 +26,8 @@
  * pass it as an argument.
  */
 #include <linux/export.h>
+#include <linux/hardirq.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/types.h>
@@ -66,6 +68,7 @@ EXPORT_SYMBOL(kick_unregister_func);
 TBIRES
 kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI)
 {
+       struct pt_regs *old_regs;
        struct kick_irq_handler *kh;
        struct list_head *lh;
        int handled = 0;
@@ -79,6 +82,9 @@ kick_handler(TBIRES State, int SigNum, int Triggers, int 
Inst, PTBI pTBI)
 
        trace_hardirqs_off();
 
+       old_regs = set_irq_regs((struct pt_regs *)State.Sig.pCtx);
+       irq_enter();
+
        /*
         * There is no need to disable interrupts here because we
         * can't nest KICK interrupts in a KICK interrupt handler.
@@ -97,5 +103,8 @@ kick_handler(TBIRES State, int SigNum, int Triggers, int 
Inst, PTBI pTBI)
 
        WARN_ON(!handled);
 
+       irq_exit();
+       set_irq_regs(old_regs);
+
        return tail_end(ret);
 }
-- 
1.8.1.2


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to