aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/irq/manage.c101
1 files changed, 54 insertions, 47 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 8f4bc61f0df9..7a954b860c07 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -575,72 +575,79 @@ int setup_irq(unsigned int irq, struct irqaction *act)
575void free_irq(unsigned int irq, void *dev_id) 575void free_irq(unsigned int irq, void *dev_id)
576{ 576{
577 struct irq_desc *desc = irq_to_desc(irq); 577 struct irq_desc *desc = irq_to_desc(irq);
578 struct irqaction **p; 578 struct irqaction *action, **p, **pp;
579 unsigned long flags; 579 unsigned long flags;
580 580
581 WARN_ON(in_interrupt()); 581 WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
582 582
583 if (!desc) 583 if (!desc)
584 return; 584 return;
585 585
586 spin_lock_irqsave(&desc->lock, flags); 586 spin_lock_irqsave(&desc->lock, flags);
587
588 /*
589 * There can be multiple actions per IRQ descriptor, find the right
590 * one based on the dev_id:
591 */
587 p = &desc->action; 592 p = &desc->action;
588 for (;;) { 593 for (;;) {
589 struct irqaction *action = *p; 594 action = *p;
595 pp = p;
596
597 if (!action) {
598 WARN(1, "Trying to free already-free IRQ %d\n", irq);
599 spin_unlock_irqrestore(&desc->lock, flags);
600
601 return;
602 }
590 603
591 if (action) { 604 p = &action->next;
592 struct irqaction **pp = p; 605 if (action->dev_id != dev_id)
606 continue;
593 607
594 p = &action->next; 608 break;
595 if (action->dev_id != dev_id) 609 }
596 continue;
597 610
598 /* Found it - now remove it from the list of entries */ 611 /* Found it - now remove it from the list of entries: */
599 *pp = action->next; 612 *pp = action->next;
600 613
601 /* Currently used only by UML, might disappear one day.*/ 614 /* Currently used only by UML, might disappear one day: */
602#ifdef CONFIG_IRQ_RELEASE_METHOD 615#ifdef CONFIG_IRQ_RELEASE_METHOD
603 if (desc->chip->release) 616 if (desc->chip->release)
604 desc->chip->release(irq, dev_id); 617 desc->chip->release(irq, dev_id);
605#endif 618#endif
606 619
607 if (!desc->action) { 620 /* If this was the last handler, shut down the IRQ line: */
608 desc->status |= IRQ_DISABLED; 621 if (!desc->action) {
609 if (desc->chip->shutdown) 622 desc->status |= IRQ_DISABLED;
610 desc->chip->shutdown(irq); 623 if (desc->chip->shutdown)
611 else 624 desc->chip->shutdown(irq);
612 desc->chip->disable(irq); 625 else
613 } 626 desc->chip->disable(irq);
614 spin_unlock_irqrestore(&desc->lock, flags); 627 }
615 unregister_handler_proc(irq, action); 628 spin_unlock_irqrestore(&desc->lock, flags);
629
630 unregister_handler_proc(irq, action);
631
632 /* Make sure it's not being used on another CPU: */
633 synchronize_irq(irq);
616 634
617 /* Make sure it's not being used on another CPU */
618 synchronize_irq(irq);
619#ifdef CONFIG_DEBUG_SHIRQ
620 /*
621 * It's a shared IRQ -- the driver ought to be
622 * prepared for it to happen even now it's
623 * being freed, so let's make sure.... We do
624 * this after actually deregistering it, to
625 * make sure that a 'real' IRQ doesn't run in
626 * parallel with our fake
627 */
628 if (action->flags & IRQF_SHARED) {
629 local_irq_save(flags);
630 action->handler(irq, dev_id);
631 local_irq_restore(flags);
632 }
633#endif
634 kfree(action);
635 return;
636 }
637 printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);
638#ifdef CONFIG_DEBUG_SHIRQ 635#ifdef CONFIG_DEBUG_SHIRQ
639 dump_stack(); 636 /*
640#endif 637 * It's a shared IRQ -- the driver ought to be prepared for an IRQ
641 spin_unlock_irqrestore(&desc->lock, flags); 638 * event to happen even now it's being freed, so let's make sure that
642 return; 639 * is so by doing an extra call to the handler ....
640 *
641 * ( We do this after actually deregistering it, to make sure that a
642 * 'real' IRQ doesn't run in * parallel with our fake. )
643 */
644 if (action->flags & IRQF_SHARED) {
645 local_irq_save(flags);
646 action->handler(irq, dev_id);
647 local_irq_restore(flags);
643 } 648 }
649#endif
650 kfree(action);
644} 651}
645EXPORT_SYMBOL(free_irq); 652EXPORT_SYMBOL(free_irq);
646 653