Skip to content

Commit 2f884e6

Browse files
grygoriySJason Cooper
authored andcommitted
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 <[email protected]> Signed-off-by: Grygorii Strashko <[email protected]> Tested-by: Suman Anna <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Jason Cooper <[email protected]>
1 parent 7ce7d89 commit 2f884e6

File tree

1 file changed

+19
-9
lines changed

1 file changed

+19
-9
lines changed

drivers/irqchip/irq-keystone.c

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
#include <linux/bitops.h>
2020
#include <linux/module.h>
2121
#include <linux/moduleparam.h>
22+
#include <linux/interrupt.h>
2223
#include <linux/irqdomain.h>
2324
#include <linux/irqchip.h>
24-
#include <linux/irqchip/chained_irq.h>
2525
#include <linux/of.h>
2626
#include <linux/of_platform.h>
2727
#include <linux/mfd/syscon.h>
@@ -39,6 +39,7 @@ struct keystone_irq_device {
3939
struct irq_domain *irqd;
4040
struct regmap *devctrl_regs;
4141
u32 devctrl_offset;
42+
raw_spinlock_t wa_lock;
4243
};
4344

4445
static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq)
@@ -83,17 +84,15 @@ static void keystone_irq_ack(struct irq_data *d)
8384
/* nothing to do here */
8485
}
8586

86-
static void keystone_irq_handler(struct irq_desc *desc)
87+
static irqreturn_t keystone_irq_handler(int irq, void *keystone_irq)
8788
{
88-
unsigned int irq = irq_desc_get_irq(desc);
89-
struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc);
89+
struct keystone_irq_device *kirq = keystone_irq;
90+
unsigned long wa_lock_flags;
9091
unsigned long pending;
9192
int src, virq;
9293

9394
dev_dbg(kirq->dev, "start irq %d\n", irq);
9495

95-
chained_irq_enter(irq_desc_get_chip(desc), desc);
96-
9796
pending = keystone_irq_readl(kirq);
9897
keystone_irq_writel(kirq, pending);
9998

@@ -111,13 +110,15 @@ static void keystone_irq_handler(struct irq_desc *desc)
111110
if (!virq)
112111
dev_warn(kirq->dev, "spurious irq detected hwirq %d, virq %d\n",
113112
src, virq);
113+
raw_spin_lock_irqsave(&kirq->wa_lock, wa_lock_flags);
114114
generic_handle_irq(virq);
115+
raw_spin_unlock_irqrestore(&kirq->wa_lock,
116+
wa_lock_flags);
115117
}
116118
}
117119

118-
chained_irq_exit(irq_desc_get_chip(desc), desc);
119-
120120
dev_dbg(kirq->dev, "end irq %d\n", irq);
121+
return IRQ_HANDLED;
121122
}
122123

123124
static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
@@ -182,9 +183,16 @@ static int keystone_irq_probe(struct platform_device *pdev)
182183
return -ENODEV;
183184
}
184185

186+
raw_spin_lock_init(&kirq->wa_lock);
187+
185188
platform_set_drvdata(pdev, kirq);
186189

187-
irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq);
190+
ret = request_irq(kirq->irq, keystone_irq_handler,
191+
0, dev_name(dev), kirq);
192+
if (ret) {
193+
irq_domain_remove(kirq->irqd);
194+
return ret;
195+
}
188196

189197
/* clear all source bits */
190198
keystone_irq_writel(kirq, ~0x0);
@@ -199,6 +207,8 @@ static int keystone_irq_remove(struct platform_device *pdev)
199207
struct keystone_irq_device *kirq = platform_get_drvdata(pdev);
200208
int hwirq;
201209

210+
free_irq(kirq->irq, kirq);
211+
202212
for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++)
203213
irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));
204214

0 commit comments

Comments
 (0)