diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-02-15 05:29:50 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-02-15 05:36:49 -0500 |
| commit | ae88a23b32fa7e0dc9fa7ce735966e68eb41b0bc (patch) | |
| tree | 39f39938fe6d197e0c1c74ecec649833a3321a68 | |
| parent | 327ec5699c29454322d0136375f717f509c145b6 (diff) | |
irq: refactor and clean up the free_irq() code flow
Impact: cleanup
- separate out the loop from the actual freeing logic, this wins us
two indentation levels allowing a number of followup prettifications
- turn the WARN_ON() into a more informative WARN().
- clean up the comments and the code flow some more
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -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 | ||
