aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2019-06-28 07:11:49 -0400
committerThomas Gleixner <tglx@linutronix.de>2019-07-03 04:12:28 -0400
commit4001d8e8762f57d418b66e4e668601791900a1dd (patch)
tree8424f1b08fd15896521cbc842571042fe9f242fe
parentc8c4076723daca08bf35ccd68f22ea1c6219e207 (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.c6
-rw-r--r--kernel/irq/chip.c6
-rw-r--r--kernel/irq/cpuhotplug.c2
-rw-r--r--kernel/irq/internals.h1
-rw-r--r--kernel/irq/manage.c12
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
320void 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);
82extern int irq_startup(struct irq_desc *desc, bool resend, bool force); 82extern int irq_startup(struct irq_desc *desc, bool resend, bool force);
83 83
84extern void irq_shutdown(struct irq_desc *desc); 84extern void irq_shutdown(struct irq_desc *desc);
85extern void irq_shutdown_and_deactivate(struct irq_desc *desc);
85extern void irq_enable(struct irq_desc *desc); 86extern void irq_enable(struct irq_desc *desc);
86extern void irq_disable(struct irq_desc *desc); 87extern void irq_disable(struct irq_desc *desc);
87extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu); 88extern 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