diff options
author | James Hogan <james.hogan@imgtec.com> | 2013-07-01 08:44:10 -0400 |
---|---|---|
committer | James Hogan <james.hogan@imgtec.com> | 2013-07-02 09:12:08 -0400 |
commit | f6b30d32d242243a967993789c4cf550d840727e (patch) | |
tree | 95369d42eb77261e5f31b7908ee97af9e2f6a73e | |
parent | 9649814432faa9016952895684120942da0d8481 (diff) |
metag: kick: add missing irq_enter/exit to kick_handler()
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.hogan@imgtec.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/metag/kernel/kick.c | 9 |
1 files changed, 9 insertions, 0 deletions
diff --git a/arch/metag/kernel/kick.c b/arch/metag/kernel/kick.c index 50fcbec98cd2..beb377621322 100644 --- a/arch/metag/kernel/kick.c +++ b/arch/metag/kernel/kick.c | |||
@@ -26,6 +26,8 @@ | |||
26 | * pass it as an argument. | 26 | * pass it as an argument. |
27 | */ | 27 | */ |
28 | #include <linux/export.h> | 28 | #include <linux/export.h> |
29 | #include <linux/hardirq.h> | ||
30 | #include <linux/irq.h> | ||
29 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
30 | #include <linux/mm.h> | 32 | #include <linux/mm.h> |
31 | #include <linux/types.h> | 33 | #include <linux/types.h> |
@@ -66,6 +68,7 @@ EXPORT_SYMBOL(kick_unregister_func); | |||
66 | TBIRES | 68 | TBIRES |
67 | kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) | 69 | kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) |
68 | { | 70 | { |
71 | struct pt_regs *old_regs; | ||
69 | struct kick_irq_handler *kh; | 72 | struct kick_irq_handler *kh; |
70 | struct list_head *lh; | 73 | struct list_head *lh; |
71 | int handled = 0; | 74 | int handled = 0; |
@@ -79,6 +82,9 @@ kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) | |||
79 | 82 | ||
80 | trace_hardirqs_off(); | 83 | trace_hardirqs_off(); |
81 | 84 | ||
85 | old_regs = set_irq_regs((struct pt_regs *)State.Sig.pCtx); | ||
86 | irq_enter(); | ||
87 | |||
82 | /* | 88 | /* |
83 | * There is no need to disable interrupts here because we | 89 | * There is no need to disable interrupts here because we |
84 | * can't nest KICK interrupts in a KICK interrupt handler. | 90 | * 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) | |||
97 | 103 | ||
98 | WARN_ON(!handled); | 104 | WARN_ON(!handled); |
99 | 105 | ||
106 | irq_exit(); | ||
107 | set_irq_regs(old_regs); | ||
108 | |||
100 | return tail_end(ret); | 109 | return tail_end(ret); |
101 | } | 110 | } |