Messages in this thread Patch in this message | | | Date | Fri, 15 Jun 2012 14:29:37 +0800 | Subject | Re: [PATCH 2/2] genirq: Resend nested irq's ancestor irq | From | Ning Jiang <> |
| |
2012/6/14 Thomas Gleixner <tglx@linutronix.de>: > On Thu, 14 Jun 2012, Ning Jiang wrote: >> We'll set IRQS_PENDING for handle_nested_irq if it's disabled. When >> it's re-enabled later on, check_irq_resend() will detect this flag >> and trigger the software resend mechanism. resend_irqs() will call >> desc->handle_irq() directly to process this interrupt, hence the >> irq_nested_primary_handler() will be called for the nested irq which >> gives us a warning. >> >> If we need to resend a nested interrupt, we have to trace all the >> way back to its ancestor and trigger ancestor's irq flow handler. > > And what makes you believe that the ancestors demux handler will find > the irq bit of the nested interrupt still pending? There is NO > guarantee for that.
You are right.
> The correct solution for this is to replace the tasklet with a kernel > thread and check whether the interrupt is marked nested or not and > then invoke the correct function. >
Do you want something like this? This just gives a rough idea. Please help to review.
From 1ce5f392446a06631d190468b314b7060f5c34ae Mon Sep 17 00:00:00 2001 From: Ning Jiang <ning.n.jiang@gmail.com> Date: Fri, 15 Jun 2012 09:13:24 +0800 Subject: [PATCH] genirq: Create temporary kernel thread when resending nested irq
--- 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |