diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2019-06-28 13:11:51 +0200 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2019-07-03 10:12:29 +0200 |
commit | 62e0468650c30f0298822c580f382b16328119f6 (patch) | |
tree | 430697236c11e9a87380ccacb3cb8824771c1d2b /kernel/irq/internals.h | |
parent | genirq: Fix misleading synchronize_irq() documentation (diff) | |
download | linux-62e0468650c30f0298822c580f382b16328119f6.tar.xz linux-62e0468650c30f0298822c580f382b16328119f6.zip |
genirq: Add optional hardware synchronization for shutdown
free_irq() ensures that no hardware interrupt handler is executing on a
different CPU before actually releasing resources and deactivating the
interrupt completely in a domain hierarchy.
But that does not catch the case where the interrupt is on flight at the
hardware level but not yet serviced by the target CPU. That creates an
interesing race condition:
CPU 0 CPU 1 IRQ CHIP
interrupt is raised
sent to CPU1
Unable to handle
immediately
(interrupts off,
deep idle delay)
mask()
...
free()
shutdown()
synchronize_irq()
release_resources()
do_IRQ()
-> resources are not available
That might be harmless and just trigger a spurious interrupt warning, but
some interrupt chips might get into a wedged state.
Utilize the existing irq_get_irqchip_state() callback for the
synchronization in free_irq().
synchronize_hardirq() is not using this mechanism as it might actually
deadlock unter certain conditions, e.g. when called with interrupts
disabled and the target CPU is the one on which the synchronization is
invoked. synchronize_irq() uses it because that function cannot be called
from non preemtible contexts as it might sleep.
No functional change intended and according to Marc the existing GIC
implementations where the driver supports the callback should be able
to cope with that core change. Famous last words.
Fixes: 464d12309e1b ("x86/vector: Switch IOAPIC to global reservation mode")
Reported-by: Robert Hodaszi <Robert.Hodaszi@digi.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Tested-by: Marc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/20190628111440.279463375@linutronix.de
Diffstat (limited to 'kernel/irq/internals.h')
-rw-r--r-- | kernel/irq/internals.h | 4 |
1 files changed, 4 insertions, 0 deletions
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 9c957f8b1198..3a948f41ab00 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -97,6 +97,10 @@ static inline void irq_mark_irq(unsigned int irq) { } extern void irq_mark_irq(unsigned int irq); #endif +extern int __irq_get_irqchip_state(struct irq_data *data, + enum irqchip_irq_state which, + bool *state); + extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr); irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags); |