diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2019-06-28 07:11:49 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2019-07-03 04:12:28 -0400 |
commit | 4001d8e8762f57d418b66e4e668601791900a1dd (patch) | |
tree | 8424f1b08fd15896521cbc842571042fe9f242fe | |
parent | c8c4076723daca08bf35ccd68f22ea1c6219e207 (diff) |
genirq: Delay deactivation in free_irq()
When interrupts are shutdown, they are immediately deactivated in the
irqdomain hierarchy. While this looks obviously correct there is a subtle
issue:
There might be an interrupt in flight when free_irq() is invoking the
shutdown. This is properly handled at the irq descriptor / primary handler
level, but the deactivation might completely disable resources which are
required to acknowledge the interrupt.
Split the shutdown code and deactivate the interrupt after synchronization
in free_irq(). Fixup all other usage sites where this is not an issue to
invoke the combined shutdown_and_deactivate() function instead.
This still might be an issue if the interrupt in flight servicing is
delayed on a remote CPU beyond the invocation of synchronize_irq(), but
that cannot be handled at that level and needs to be handled in the
synchronize_irq() context.
Fixes: f8264e34965a ("irqdomain: Introduce new interfaces to support hierarchy irqdomains")
Reported-by: Robert Hodaszi <Robert.Hodaszi@digi.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/20190628111440.098196390@linutronix.de
-rw-r--r-- | kernel/irq/autoprobe.c | 6 | ||||
-rw-r--r-- | kernel/irq/chip.c | 6 | ||||
-rw-r--r-- | kernel/irq/cpuhotplug.c | 2 | ||||
-rw-r--r-- | kernel/irq/internals.h | 1 | ||||
-rw-r--r-- | kernel/irq/manage.c | 12 |
5 files changed, 22 insertions, 5 deletions
diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c index 16cbf6beb276..ae60cae24e9a 100644 --- a/kernel/irq/autoprobe.c +++ b/kernel/irq/autoprobe.c | |||
@@ -90,7 +90,7 @@ unsigned long probe_irq_on(void) | |||
90 | /* It triggered already - consider it spurious. */ | 90 | /* It triggered already - consider it spurious. */ |
91 | if (!(desc->istate & IRQS_WAITING)) { | 91 | if (!(desc->istate & IRQS_WAITING)) { |
92 | desc->istate &= ~IRQS_AUTODETECT; | 92 | desc->istate &= ~IRQS_AUTODETECT; |
93 | irq_shutdown(desc); | 93 | irq_shutdown_and_deactivate(desc); |
94 | } else | 94 | } else |
95 | if (i < 32) | 95 | if (i < 32) |
96 | mask |= 1 << i; | 96 | mask |= 1 << i; |
@@ -127,7 +127,7 @@ unsigned int probe_irq_mask(unsigned long val) | |||
127 | mask |= 1 << i; | 127 | mask |= 1 << i; |
128 | 128 | ||
129 | desc->istate &= ~IRQS_AUTODETECT; | 129 | desc->istate &= ~IRQS_AUTODETECT; |
130 | irq_shutdown(desc); | 130 | irq_shutdown_and_deactivate(desc); |
131 | } | 131 | } |
132 | raw_spin_unlock_irq(&desc->lock); | 132 | raw_spin_unlock_irq(&desc->lock); |
133 | } | 133 | } |
@@ -169,7 +169,7 @@ int probe_irq_off(unsigned long val) | |||
169 | nr_of_irqs++; | 169 | nr_of_irqs++; |
170 | } | 170 | } |
171 | desc->istate &= ~IRQS_AUTODETECT; | 171 | desc->istate &= ~IRQS_AUTODETECT; |
172 | irq_shutdown(desc); | 172 | irq_shutdown_and_deactivate(desc); |
173 | } | 173 | } |
174 | raw_spin_unlock_irq(&desc->lock); | 174 | raw_spin_unlock_irq(&desc->lock); |
175 | } | 175 | } |
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 51128bea3846..04fe4f989bd8 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -314,6 +314,12 @@ void irq_shutdown(struct irq_desc *desc) | |||
314 | } | 314 | } |
315 | irq_state_clr_started(desc); | 315 | irq_state_clr_started(desc); |
316 | } | 316 | } |
317 | } | ||
318 | |||
319 | |||
320 | void irq_shutdown_and_deactivate(struct irq_desc *desc) | ||
321 | { | ||
322 | irq_shutdown(desc); | ||
317 | /* | 323 | /* |
318 | * This must be called even if the interrupt was never started up, | 324 | * This must be called even if the interrupt was never started up, |
319 | * because the activation can happen before the interrupt is | 325 | * because the activation can happen before the interrupt is |
diff --git a/kernel/irq/cpuhotplug.c b/kernel/irq/cpuhotplug.c index 5b1072e394b2..6c7ca2e983a5 100644 --- a/kernel/irq/cpuhotplug.c +++ b/kernel/irq/cpuhotplug.c | |||
@@ -116,7 +116,7 @@ static bool migrate_one_irq(struct irq_desc *desc) | |||
116 | */ | 116 | */ |
117 | if (irqd_affinity_is_managed(d)) { | 117 | if (irqd_affinity_is_managed(d)) { |
118 | irqd_set_managed_shutdown(d); | 118 | irqd_set_managed_shutdown(d); |
119 | irq_shutdown(desc); | 119 | irq_shutdown_and_deactivate(desc); |
120 | return false; | 120 | return false; |
121 | } | 121 | } |
122 | affinity = cpu_online_mask; | 122 | affinity = cpu_online_mask; |
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 70c3053bc1f6..9c957f8b1198 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h | |||
@@ -82,6 +82,7 @@ extern int irq_activate_and_startup(struct irq_desc *desc, bool resend); | |||
82 | extern int irq_startup(struct irq_desc *desc, bool resend, bool force); | 82 | extern int irq_startup(struct irq_desc *desc, bool resend, bool force); |
83 | 83 | ||
84 | extern void irq_shutdown(struct irq_desc *desc); | 84 | extern void irq_shutdown(struct irq_desc *desc); |
85 | extern void irq_shutdown_and_deactivate(struct irq_desc *desc); | ||
85 | extern void irq_enable(struct irq_desc *desc); | 86 | extern void irq_enable(struct irq_desc *desc); |
86 | extern void irq_disable(struct irq_desc *desc); | 87 | extern void irq_disable(struct irq_desc *desc); |
87 | extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu); | 88 | extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu); |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 53a081392115..dc8b35f2d545 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/random.h> | 14 | #include <linux/random.h> |
15 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
16 | #include <linux/irqdomain.h> | ||
16 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
17 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
18 | #include <linux/sched/rt.h> | 19 | #include <linux/sched/rt.h> |
@@ -1699,6 +1700,7 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id) | |||
1699 | /* If this was the last handler, shut down the IRQ line: */ | 1700 | /* If this was the last handler, shut down the IRQ line: */ |
1700 | if (!desc->action) { | 1701 | if (!desc->action) { |
1701 | irq_settings_clr_disable_unlazy(desc); | 1702 | irq_settings_clr_disable_unlazy(desc); |
1703 | /* Only shutdown. Deactivate after synchronize_hardirq() */ | ||
1702 | irq_shutdown(desc); | 1704 | irq_shutdown(desc); |
1703 | } | 1705 | } |
1704 | 1706 | ||
@@ -1768,6 +1770,14 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id) | |||
1768 | * require it to deallocate resources over the slow bus. | 1770 | * require it to deallocate resources over the slow bus. |
1769 | */ | 1771 | */ |
1770 | chip_bus_lock(desc); | 1772 | chip_bus_lock(desc); |
1773 | /* | ||
1774 | * There is no interrupt on the fly anymore. Deactivate it | ||
1775 | * completely. | ||
1776 | */ | ||
1777 | raw_spin_lock_irqsave(&desc->lock, flags); | ||
1778 | irq_domain_deactivate_irq(&desc->irq_data); | ||
1779 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
1780 | |||
1771 | irq_release_resources(desc); | 1781 | irq_release_resources(desc); |
1772 | chip_bus_sync_unlock(desc); | 1782 | chip_bus_sync_unlock(desc); |
1773 | irq_remove_timings(desc); | 1783 | irq_remove_timings(desc); |
@@ -1855,7 +1865,7 @@ static const void *__cleanup_nmi(unsigned int irq, struct irq_desc *desc) | |||
1855 | } | 1865 | } |
1856 | 1866 | ||
1857 | irq_settings_clr_disable_unlazy(desc); | 1867 | irq_settings_clr_disable_unlazy(desc); |
1858 | irq_shutdown(desc); | 1868 | irq_shutdown_and_deactivate(desc); |
1859 | 1869 | ||
1860 | irq_release_resources(desc); | 1870 | irq_release_resources(desc); |
1861 | 1871 | ||