Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

irqchip/keystone: Fix "scheduling while atomic" on rt

The below call chain generates "scheduling while atomic" backtrace and
causes system crash when Keystone 2 IRQ chip driver is used with RT-kernel:

gic_handle_irq()
|-__handle_domain_irq()
|-generic_handle_irq()
|-keystone_irq_handler()
|-regmap_read()
|-regmap_lock_spinlock()
|-rt_spin_lock()

The reason is that Keystone driver dispatches IRQ using chained IRQ handler
and accesses I/O memory through syscon->regmap(mmio) which is implemented
as fast_io regmap and uses regular spinlocks for synchronization, but
spinlocks transformed to rt_mutexes on RT.

Hence, convert Keystone 2 IRQ driver to use generic irq handler instead of
chained IRQ handler. This way it will be compatible with RT kernel where it
will be forced thread IRQ handler while in non-RT kernel it still will be
executed in HW IRQ context.

Cc: Suman Anna <s-anna@ti.com>
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Tested-by: Suman Anna <s-anna@ti.com>
Link: https://lkml.kernel.org/r/20161208233310.10329-1-grygorii.strashko@ti.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>

authored by

Strashko, Grygorii and committed by
Jason Cooper
2f884e6e 7ce7d89f

+19 -9
+19 -9
drivers/irqchip/irq-keystone.c
··· 19 19 #include <linux/bitops.h> 20 20 #include <linux/module.h> 21 21 #include <linux/moduleparam.h> 22 + #include <linux/interrupt.h> 22 23 #include <linux/irqdomain.h> 23 24 #include <linux/irqchip.h> 24 - #include <linux/irqchip/chained_irq.h> 25 25 #include <linux/of.h> 26 26 #include <linux/of_platform.h> 27 27 #include <linux/mfd/syscon.h> ··· 39 39 struct irq_domain *irqd; 40 40 struct regmap *devctrl_regs; 41 41 u32 devctrl_offset; 42 + raw_spinlock_t wa_lock; 42 43 }; 43 44 44 45 static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq) ··· 84 83 /* nothing to do here */ 85 84 } 86 85 87 - static void keystone_irq_handler(struct irq_desc *desc) 86 + static irqreturn_t keystone_irq_handler(int irq, void *keystone_irq) 88 87 { 89 - unsigned int irq = irq_desc_get_irq(desc); 90 - struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc); 88 + struct keystone_irq_device *kirq = keystone_irq; 89 + unsigned long wa_lock_flags; 91 90 unsigned long pending; 92 91 int src, virq; 93 92 94 93 dev_dbg(kirq->dev, "start irq %d\n", irq); 95 - 96 - chained_irq_enter(irq_desc_get_chip(desc), desc); 97 94 98 95 pending = keystone_irq_readl(kirq); 99 96 keystone_irq_writel(kirq, pending); ··· 110 111 if (!virq) 111 112 dev_warn(kirq->dev, "spurious irq detected hwirq %d, virq %d\n", 112 113 src, virq); 114 + raw_spin_lock_irqsave(&kirq->wa_lock, wa_lock_flags); 113 115 generic_handle_irq(virq); 116 + raw_spin_unlock_irqrestore(&kirq->wa_lock, 117 + wa_lock_flags); 114 118 } 115 119 } 116 120 117 - chained_irq_exit(irq_desc_get_chip(desc), desc); 118 - 119 121 dev_dbg(kirq->dev, "end irq %d\n", irq); 122 + return IRQ_HANDLED; 120 123 } 121 124 122 125 static int keystone_irq_map(struct irq_domain *h, unsigned int virq, ··· 183 182 return -ENODEV; 184 183 } 185 184 185 + raw_spin_lock_init(&kirq->wa_lock); 186 + 186 187 platform_set_drvdata(pdev, kirq); 187 188 188 - irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq); 189 + ret = request_irq(kirq->irq, keystone_irq_handler, 190 + 0, dev_name(dev), kirq); 191 + if (ret) { 192 + irq_domain_remove(kirq->irqd); 193 + return ret; 194 + } 189 195 190 196 /* clear all source bits */ 191 197 keystone_irq_writel(kirq, ~0x0); ··· 206 198 { 207 199 struct keystone_irq_device *kirq = platform_get_drvdata(pdev); 208 200 int hwirq; 201 + 202 + free_irq(kirq->irq, kirq); 209 203 210 204 for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++) 211 205 irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));