aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2015-12-13 12:12:30 -0500
committerThomas Gleixner <tglx@linutronix.de>2015-12-14 03:45:06 -0500
commitabc7e40c81d113ef4bacb556f0a77ca63ac81d85 (patch)
tree06c8130b3f1f442a195135e7a032d28b1fa6bbdf
parent9f9499ae8e6415cefc4fe0a96ad0e27864353c89 (diff)
genirq: Prevent chip buslock deadlock
If a interrupt chip utilizes chip->buslock then free_irq() can deadlock in the following way: CPU0 CPU1 interrupt(X) (Shared or spurious) free_irq(X) interrupt_thread(X) chip_bus_lock(X) irq_finalize_oneshot(X) chip_bus_lock(X) synchronize_irq(X) synchronize_irq() waits for the interrupt thread to complete, i.e. forever. Solution is simple: Drop chip_bus_lock() before calling synchronize_irq() as we do with the irq_desc lock. There is nothing to be protected after the point where irq_desc lock has been released. This adds chip_bus_lock/unlock() to the remove_irq() code path, but that's actually correct in the case where remove_irq() is called on such an interrupt. The current users of remove_irq() are not affected as none of those interrupts is on a chip which requires buslock. Reported-by: Fredrik Markström <fredrik.markstrom@gmail.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: stable@vger.kernel.org
-rw-r--r--kernel/irq/manage.c6
1 files changed, 3 insertions, 3 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 0eebaeef317b..6ead200370da 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1434,6 +1434,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
1434 if (!desc) 1434 if (!desc)
1435 return NULL; 1435 return NULL;
1436 1436
1437 chip_bus_lock(desc);
1437 raw_spin_lock_irqsave(&desc->lock, flags); 1438 raw_spin_lock_irqsave(&desc->lock, flags);
1438 1439
1439 /* 1440 /*
@@ -1447,7 +1448,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
1447 if (!action) { 1448 if (!action) {
1448 WARN(1, "Trying to free already-free IRQ %d\n", irq); 1449 WARN(1, "Trying to free already-free IRQ %d\n", irq);
1449 raw_spin_unlock_irqrestore(&desc->lock, flags); 1450 raw_spin_unlock_irqrestore(&desc->lock, flags);
1450 1451 chip_bus_sync_unlock(desc);
1451 return NULL; 1452 return NULL;
1452 } 1453 }
1453 1454
@@ -1475,6 +1476,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
1475#endif 1476#endif
1476 1477
1477 raw_spin_unlock_irqrestore(&desc->lock, flags); 1478 raw_spin_unlock_irqrestore(&desc->lock, flags);
1479 chip_bus_sync_unlock(desc);
1478 1480
1479 unregister_handler_proc(irq, action); 1481 unregister_handler_proc(irq, action);
1480 1482
@@ -1553,9 +1555,7 @@ void free_irq(unsigned int irq, void *dev_id)
1553 desc->affinity_notify = NULL; 1555 desc->affinity_notify = NULL;
1554#endif 1556#endif
1555 1557
1556 chip_bus_lock(desc);
1557 kfree(__free_irq(irq, dev_id)); 1558 kfree(__free_irq(irq, dev_id));
1558 chip_bus_sync_unlock(desc);
1559} 1559}
1560EXPORT_SYMBOL(free_irq); 1560EXPORT_SYMBOL(free_irq);
1561 1561