[RFC][PATCH] irq: restrict the numbers of misrouted interrupt (per second).

From: Warner Wang
Date: Mon Nov 18 2013 - 05:40:30 EST


The original spurious.c already provides the functionality of
detect & disable screaming interrupts. But under some situation
the code won't work. When irq_poll is on and the system has some
ISRs that donât do sanity check and return IRQ_NONE. This will
come into a loop: device A is screaming, keeps generates large
amount of interrupts, while device B's ISR keep being executed by
misrouted irq code (because B's ISR have problem and won't return
as IRQ_NONE. In this situation the original code to detect the
screaming interrupts will not have a chance to be called. And the
system performance will be very poor.

This RFC patch tries to solve this problem, by adding a counter for
maximum misrouted interrupts we should handle per second. And if
we reached the maximum number we simply skip the misrounted irq.

This patch is based on tag v3.12, please give comments on it, all
kinds of comments are welcome.

Signed-off-by: Warner Wang <warner.wang@xxxxxx>
---
include/linux/irqdesc.h | 1 +
kernel/irq/irqdesc.c | 1 +
kernel/irq/manage.c | 1 +
kernel/irq/spurious.c | 14 +++++++++++---
4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 56fb646..1b777e3 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -52,6 +52,7 @@ struct irq_desc {
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
+ unsigned int handled_in_jiff;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 192a302..0fcbb55 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -86,6 +86,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
desc->depth = 1;
desc->irq_count = 0;
desc->irqs_unhandled = 0;
+ desc->handled_in_jiff = 0;
desc->name = NULL;
desc->owner = owner;
for_each_possible_cpu(cpu)
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 514bcfd..31b2cb0 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1146,6 +1146,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
/* Reset broken irq detection when installing new handler */
desc->irq_count = 0;
desc->irqs_unhandled = 0;
+ desc->handled_in_jiff = 0;

/*
* Check whether we disabled the irq via the spurious handler
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c
index 7b5f012..817d5a8 100644
--- a/kernel/irq/spurious.c
+++ b/kernel/irq/spurious.c
@@ -265,6 +265,7 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
return action && (action->flags & IRQF_IRQPOLL);
}

+static int irq_in_sec = 5000;
void note_interrupt(unsigned int irq, struct irq_desc *desc,
irqreturn_t action_ret)
{
@@ -295,9 +296,16 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc,
}

if (unlikely(try_misrouted_irq(irq, desc, action_ret))) {
- int ok = misrouted_irq(irq);
- if (action_ret == IRQ_NONE)
- desc->irqs_unhandled -= ok;
+ if (desc->handled_in_jiff < (irq_in_sec / HZ)) {
+ int ok = misrouted_irq(irq);
+ if (action_ret == IRQ_NONE) {
+ desc->irqs_unhandled -= ok;
+ if (time_after_eq(desc->last_unhandled, jiffies))
+ desc->handled_in_jiff++;
+ else
+ desc->handled_in_jiff = 0;
+ }
+ }
}

desc->irq_count++;
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/