| From 753649dbc49345a73a2454c770a3f2d54d11aec6 Mon Sep 17 00:00:00 2001 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Wed, 31 Mar 2010 13:30:19 +0200 |
| Subject: genirq: Force MSI irq handlers to run with interrupts disabled |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 753649dbc49345a73a2454c770a3f2d54d11aec6 upstream. |
| |
| Network folks reported that directing all MSI-X vectors of their multi |
| queue NICs to a single core can cause interrupt stack overflows when |
| enough interrupts fire at the same time. |
| |
| This is caused by the fact that we run interrupt handlers by default |
| with interrupts enabled unless the driver reuqests the interrupt with |
| the IRQF_DISABLED set. The NIC handlers do not set this flag, so |
| simultaneous interrupts can nest unlimited and cause the stack |
| overflow. |
| |
| The only safe counter measure is to run the interrupt handlers with |
| interrupts disabled. We can't switch to this mode in general right |
| now, but it is safe to do so for MSI interrupts. |
| |
| Force IRQF_DISABLED for MSI interrupt handlers. |
| |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Cc: Andi Kleen <andi@firstfloor.org> |
| Cc: Linus Torvalds <torvalds@osdl.org> |
| Cc: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Ingo Molnar <mingo@elte.hu> |
| Cc: Peter Zijlstra <peterz@infradead.org> |
| Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> |
| Cc: David Miller <davem@davemloft.net> |
| Cc: Greg Kroah-Hartman <gregkh@suse.de> |
| Cc: Arnaldo Carvalho de Melo <acme@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| kernel/irq/manage.c | 10 ++++++++++ |
| 1 file changed, 10 insertions(+) |
| |
| --- a/kernel/irq/manage.c |
| +++ b/kernel/irq/manage.c |
| @@ -735,6 +735,16 @@ __setup_irq(unsigned int irq, struct irq |
| if (new->flags & IRQF_ONESHOT) |
| desc->status |= IRQ_ONESHOT; |
| |
| + /* |
| + * Force MSI interrupts to run with interrupts |
| + * disabled. The multi vector cards can cause stack |
| + * overflows due to nested interrupts when enough of |
| + * them are directed to a core and fire at the same |
| + * time. |
| + */ |
| + if (desc->msi_desc) |
| + new->flags |= IRQF_DISABLED; |
| + |
| if (!(desc->status & IRQ_NOAUTOEN)) { |
| desc->depth = 0; |
| desc->status &= ~IRQ_DISABLED; |