diff --git a/include/linux/irq.h b/include/linux/irq.h index 216b0ba109d7..526f10a637c1 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -392,6 +392,15 @@ static inline void irq_move_masked_irq(struct irq_data *data) { } extern int no_irq_affinity; +#ifdef CONFIG_HARDIRQS_SW_RESEND +int irq_set_parent(int irq, int parent_irq); +#else +static inline int irq_set_parent(int irq, int parent_irq) +{ + return 0; +} +#endif + /* * Built-in IRQ handlers for various IRQ types, * callable via desc->handle_irq() diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 0ba014c55056..623325e2ff97 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -11,6 +11,8 @@ struct irq_affinity_notify; struct proc_dir_entry; struct module; +struct irq_desc; + /** * struct irq_desc - interrupt descriptor * @irq_data: per irq and chip data passed down to chip functions @@ -65,6 +67,7 @@ struct irq_desc { #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; #endif + int parent_irq; struct module *owner; const char *name; } ____cacheline_internodealigned_in_smp; diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 57d86d07221e..3aca9f29d30e 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/manage.c b/kernel/irq/manage.c index 4c69326aa773..d06a396c7ce3 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -616,6 +616,22 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, return ret; } +#ifdef CONFIG_HARDIRQS_SW_RESEND +int irq_set_parent(int irq, int parent_irq) +{ + unsigned long flags; + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); + + if (!desc) + return -EINVAL; + + desc->parent_irq = parent_irq; + + irq_put_desc_unlock(desc, flags); + return 0; +} +#endif + /* * Default primary interrupt handler for threaded interrupts. Is * assigned as primary handler when request_threaded_irq is called diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c index 6454db7b6a4d..9065107f083e 100644 --- a/kernel/irq/resend.c +++ b/kernel/irq/resend.c @@ -74,6 +74,14 @@ 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 + /* + * If the interrupt has a parent irq and runs + * in the thread context of the parent irq, + * retrigger the parent. + */ + if (desc->parent_irq && + irq_settings_is_nested_thread(desc)) + irq = desc->parent_irq; /* Set it pending and activate the softirq: */ set_bit(irq, irqs_resend); tasklet_schedule(&resend_tasklet);