From 1fee9db9b42d821e8007289d4eea74bdf85b1543 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 6 Jul 2021 11:38:59 +0100 Subject: irqchip/mips: Fix RCU violation when using irqdomain lookup on interrupt entry Since d4a45c68dc81 ("irqdomain: Protect the linear revmap with RCU"), any irqdomain lookup requires the RCU read lock to be held. This assumes that the architecture code will be structured such as irq_enter() will be called *before* the interrupt is looked up in the irq domain. However, this isn't the case for MIPS, and a number of drivers are structured to do it the other way around when handling an interrupt in their root irqchip (secondary irqchips are OK by construction). This results in a RCU splat on a lockdep-enabled kernel when the kernel takes an interrupt from idle, as reported by Guenter Roeck. Note that this could have fired previously if any driver had used tree-based irqdomain, which always had the RCU requirement. To solve this, provide a MIPS-specific helper (do_domain_IRQ()) as the pendent of do_IRQ() that will do thing in the right order (and maybe save some cycles in the process). Ideally, MIPS would be moved over to using handle_domain_irq(), but that's much more ambitious. Reported-by: Guenter Roeck Tested-by: Guenter Roeck [maz: add dependency on CONFIG_IRQ_DOMAIN after report from the kernelci bot] Signed-off-by: Marc Zyngier Cc: Thomas Bogendoerfer Cc: Serge Semin Link: https://lore.kernel.org/r/20210705172352.GA56304@roeck-us.net Link: https://lore.kernel.org/r/20210706110647.3979002-1-maz@kernel.org --- arch/mips/include/asm/irq.h | 3 +++ arch/mips/kernel/irq.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'arch') diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h index d1477ecb1af9..57561e0e6e8d 100644 --- a/arch/mips/include/asm/irq.h +++ b/arch/mips/include/asm/irq.h @@ -57,6 +57,9 @@ asmlinkage void plat_irq_dispatch(void); extern void do_IRQ(unsigned int irq); +struct irq_domain; +extern void do_domain_IRQ(struct irq_domain *domain, unsigned int irq); + extern void arch_init_irq(void); extern void spurious_interrupt(void); diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 85b6c60f285d..d20e002b3246 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -107,3 +108,18 @@ void __irq_entry do_IRQ(unsigned int irq) irq_exit(); } +#ifdef CONFIG_IRQ_DOMAIN +void __irq_entry do_domain_IRQ(struct irq_domain *domain, unsigned int hwirq) +{ + struct irq_desc *desc; + + irq_enter(); + check_stack_overflow(); + + desc = irq_resolve_mapping(domain, hwirq); + if (likely(desc)) + handle_irq_desc(desc); + + irq_exit(); +} +#endif -- cgit v1.2.3