| From stable-bounces@linux.kernel.org Tue Feb 19 15:29:28 2008 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Wed, 20 Feb 2008 00:29:02 +0100 (CET) |
| Subject: genirq: do not leave interupts enabled on free_irq |
| To: Stable Team <stable@kernel.org> |
| Message-ID: <alpine.LFD.1.00.0802200026480.7583@apollo.tec.linutronix.de> |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 89d694b9dbe769ca1004e01db0ca43964806a611 |
| |
| The default_disable() function was changed in commit: |
| |
| 76d2160147f43f982dfe881404cfde9fd0a9da21 |
| genirq: do not mask interrupts by default |
| |
| It removed the mask function in favour of the default delayed |
| interrupt disabling. Unfortunately this also broke the shutdown in |
| free_irq() when the last handler is removed from the interrupt for |
| those architectures which rely on the default implementations. Now we |
| can end up with a enabled interrupt line after the last handler was |
| removed, which can result in spurious interrupts. |
| |
| Fix this by adding a default_shutdown function, which is only |
| installed, when the irqchip implementation does provide neither a |
| shutdown nor a disable function. |
| |
| |
| Pointed-out-by: Michael Hennerich <Michael.Hennerich@analog.com> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Acked-by: Ingo Molnar <mingo@elte.hu> |
| Tested-by: Michael Hennerich <Michael.Hennerich@analog.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| |
| --- |
| kernel/irq/chip.c | 20 +++++++++++++++++++- |
| 1 file changed, 19 insertions(+), 1 deletion(-) |
| |
| --- a/kernel/irq/chip.c |
| +++ b/kernel/irq/chip.c |
| @@ -246,6 +246,17 @@ static unsigned int default_startup(unsi |
| } |
| |
| /* |
| + * default shutdown function |
| + */ |
| +static void default_shutdown(unsigned int irq) |
| +{ |
| + struct irq_desc *desc = irq_desc + irq; |
| + |
| + desc->chip->mask(irq); |
| + desc->status |= IRQ_MASKED; |
| +} |
| + |
| +/* |
| * Fixup enable/disable function pointers |
| */ |
| void irq_chip_set_defaults(struct irq_chip *chip) |
| @@ -256,8 +267,15 @@ void irq_chip_set_defaults(struct irq_ch |
| chip->disable = default_disable; |
| if (!chip->startup) |
| chip->startup = default_startup; |
| + /* |
| + * We use chip->disable, when the user provided its own. When |
| + * we have default_disable set for chip->disable, then we need |
| + * to use default_shutdown, otherwise the irq line is not |
| + * disabled on free_irq(): |
| + */ |
| if (!chip->shutdown) |
| - chip->shutdown = chip->disable; |
| + chip->shutdown = chip->disable != default_disable ? |
| + chip->disable : default_shutdown; |
| if (!chip->name) |
| chip->name = chip->typename; |
| if (!chip->end) |