[PATCH] genirq: Create temporary kernel thread when resending

From: Ning Jiang
Date: Thu Jun 14 2012 - 21:13:24 EST


---
kernel/irq/chip.c | 1 +
kernel/irq/handle.c | 2 +-
kernel/irq/internals.h | 4 ++++
kernel/irq/manage.c | 8 +++++++-
kernel/irq/resend.c | 20 +++++++++++++++++---
5 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 6cec1a2..ec48d4e 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -272,6 +272,7 @@ void handle_nested_irq(unsigned int irq)

raw_spin_lock_irq(&desc->lock);

+ desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);

action = desc->action;
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
index bdb1803..556b469 100644
--- a/kernel/irq/handle.c
+++ b/kernel/irq/handle.c
@@ -51,7 +51,7 @@ static void warn_no_thread(unsigned int irq, struct
irqaction *action)
"but no thread function available.", irq, action->name);
}

-static void irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
+void irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
{
/*
* In case the thread crashed and was killed we just pretend that
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 001fa5b..fd9dabe 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -104,6 +104,10 @@ extern void irq_set_thread_affinity(struct irq_desc *desc);
extern int irq_do_set_affinity(struct irq_data *data,
const struct cpumask *dest, bool force);

+extern int irq_thread(void *data);
+
+extern void irq_wake_thread(struct irq_desc *desc, struct irqaction *action);
+
/* Inline functions for support of irq chips on slow busses */
static inline void chip_bus_lock(struct irq_desc *desc)
{
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 8c54823..2bd4a39 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -811,7 +811,7 @@ static void irq_thread_dtor(struct task_work *unused)
/*
* Interrupt handler thread
*/
-static int irq_thread(void *data)
+int irq_thread(void *data)
{
struct task_work on_exit_work;
static const struct sched_param param = {
@@ -843,6 +843,12 @@ static int irq_thread(void *data)
note_interrupt(action->irq, desc, action_ret);

wake_threads_waitq(desc);
+
+ if (irq_settings_is_nested_thread(desc) && desc->action->thread) {
+ put_task_struct(desc->action->thread);
+ desc->action->thread = NULL;
+ do_exit(0);
+ }
}

/*
diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c
index 6454db7..5bb9227 100644
--- a/kernel/irq/resend.c
+++ b/kernel/irq/resend.c
@@ -14,6 +14,7 @@
*/

#include <linux/irq.h>
+#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/interrupt.h>
@@ -74,9 +75,22 @@ void check_irq_resend(struct irq_desc *desc,
unsigned int irq)
if (!desc->irq_data.chip->irq_retrigger ||
!desc->irq_data.chip->irq_retrigger(&desc->irq_data)) {
#ifdef CONFIG_HARDIRQS_SW_RESEND
- /* Set it pending and activate the softirq: */
- set_bit(irq, irqs_resend);
- tasklet_schedule(&resend_tasklet);
+ struct task_struct *t;
+ if (irq_settings_is_nested_thread(desc)) {
+ raw_spin_unlock(&desc->lock);
+ t = kthread_create(irq_thread, desc->action,
+ "irq/%d-%s", irq, desc->action->name);
+ raw_spin_lock(&desc->lock);
+ if (IS_ERR(t))
+ return;
+ get_task_struct(t);
+ desc->action->thread = t;
+ irq_wake_thread(desc, desc->action);
+ } else {
+ /* Set it pending and activate the softirq: */
+ set_bit(irq, irqs_resend);
+ tasklet_schedule(&resend_tasklet);
+ }
#endif
}
}
--
1.7.1


> That requires a check for irq in progress in the nested handler as
> well, but that's trivial to add.

Will check it later.

> Thanks,
>
>        tglx
--
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/