diff options
-rw-r--r-- | kernel/irq/manage.c | 101 |
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) | |||
575 | void free_irq(unsigned int irq, void *dev_id) | 575 | void 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 | } |
645 | EXPORT_SYMBOL(free_irq); | 652 | EXPORT_SYMBOL(free_irq); |
646 | 653 | ||