Commit fae9ca79 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Jeremy Kerr

[POWERPC] spufs: synchronize IRQ when disabling

There is a small race between the context save procedure
and the SPU interrupt handling, where we expect all interrupt
processing to have finished after disabling them, while
an interrupt is still being processed on another CPU.

The obvious fix is to call synchronize_irq() after disabling
the interrupts at the start of the context save procedure
to make sure we never access the SPU any more during an
ongoing save or even after that.

Thanks to Benjamin Herrenschmidt for pointing this out.
Acked-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarJeremy Kerr <jk@ozlabs.org>
parent 71791bee
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/hardirq.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
...@@ -117,6 +118,8 @@ static inline void disable_interrupts(struct spu_state *csa, struct spu *spu) ...@@ -117,6 +118,8 @@ static inline void disable_interrupts(struct spu_state *csa, struct spu *spu)
* Write INT_MASK_class1 with value of 0. * Write INT_MASK_class1 with value of 0.
* Save INT_Mask_class2 in CSA. * Save INT_Mask_class2 in CSA.
* Write INT_MASK_class2 with value of 0. * Write INT_MASK_class2 with value of 0.
* Synchronize all three interrupts to be sure
* we no longer execute a handler on another CPU.
*/ */
spin_lock_irq(&spu->register_lock); spin_lock_irq(&spu->register_lock);
if (csa) { if (csa) {
...@@ -129,6 +132,9 @@ static inline void disable_interrupts(struct spu_state *csa, struct spu *spu) ...@@ -129,6 +132,9 @@ static inline void disable_interrupts(struct spu_state *csa, struct spu *spu)
spu_int_mask_set(spu, 2, 0ul); spu_int_mask_set(spu, 2, 0ul);
eieio(); eieio();
spin_unlock_irq(&spu->register_lock); spin_unlock_irq(&spu->register_lock);
synchronize_irq(spu->irqs[0]);
synchronize_irq(spu->irqs[1]);
synchronize_irq(spu->irqs[2]);
} }
static inline void set_watchdog_timer(struct spu_state *csa, struct spu *spu) static inline void set_watchdog_timer(struct spu_state *csa, struct spu *spu)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment