Skip to content

Commit 76ba59f

Browse files
Marc ZyngierJason Cooper
authored andcommitted
genirq: Add irq_domain-aware core IRQ handler
Calling irq_find_mapping from outside a irq_{enter,exit} section is unsafe and produces ugly messages if CONFIG_PROVE_RCU is enabled: If coming from the idle state, the rcu_read_lock call in irq_find_mapping will generate an unpleasant warning: <quote> =============================== [ INFO: suspicious RCU usage. ] 3.16.0-rc1+ #135 Not tainted ------------------------------- include/linux/rcupdate.h:871 rcu_read_lock() used illegally while idle! other info that might help us debug this: RCU used illegally from idle CPU! rcu_scheduler_active = 1, debug_locks = 0 RCU used illegally from extended quiescent state! 1 lock held by swapper/0/0: #0: (rcu_read_lock){......}, at: [<ffffffc00010206c>] irq_find_mapping+0x4c/0x198 </quote> As this issue is fairly widespread and involves at least three different architectures, a possible solution is to add a new handle_domain_irq entry point into the generic IRQ code that the interrupt controller code can call. This new function takes an irq_domain, and calls into irq_find_domain inside the irq_{enter,exit} block. An additional "lookup" parameter is used to allow non-domain architecture code to be replaced by this as well. Interrupt controllers can then be updated to use the new mechanism. This code is sitting behind a new CONFIG_HANDLE_DOMAIN_IRQ, as not all architectures implement set_irq_regs (yes, mn10300, I'm looking at you...). Reported-by: Vladimir Murzin <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Jason Cooper <[email protected]>
1 parent 7d1311b commit 76ba59f

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

include/linux/irqdesc.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ struct irq_affinity_notify;
1212
struct proc_dir_entry;
1313
struct module;
1414
struct irq_desc;
15+
struct irq_domain;
16+
struct pt_regs;
1517

1618
/**
1719
* struct irq_desc - interrupt descriptor
@@ -118,6 +120,23 @@ static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *de
118120

119121
int generic_handle_irq(unsigned int irq);
120122

123+
#ifdef CONFIG_HANDLE_DOMAIN_IRQ
124+
/*
125+
* Convert a HW interrupt number to a logical one using a IRQ domain,
126+
* and handle the result interrupt number. Return -EINVAL if
127+
* conversion failed. Providing a NULL domain indicates that the
128+
* conversion has already been done.
129+
*/
130+
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
131+
bool lookup, struct pt_regs *regs);
132+
133+
static inline int handle_domain_irq(struct irq_domain *domain,
134+
unsigned int hwirq, struct pt_regs *regs)
135+
{
136+
return __handle_domain_irq(domain, hwirq, true, regs);
137+
}
138+
#endif
139+
121140
/* Test to see if a driver has successfully requested an irq */
122141
static inline int irq_has_action(unsigned int irq)
123142
{

kernel/irq/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ config GENERIC_IRQ_CHIP
5555
config IRQ_DOMAIN
5656
bool
5757

58+
config HANDLE_DOMAIN_IRQ
59+
bool
60+
5861
config IRQ_DOMAIN_DEBUG
5962
bool "Expose hardware/virtual IRQ mapping via debugfs"
6063
depends on IRQ_DOMAIN && DEBUG_FS

kernel/irq/irqdesc.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/kernel_stat.h>
1515
#include <linux/radix-tree.h>
1616
#include <linux/bitmap.h>
17+
#include <linux/irqdomain.h>
1718

1819
#include "internals.h"
1920

@@ -336,6 +337,47 @@ int generic_handle_irq(unsigned int irq)
336337
}
337338
EXPORT_SYMBOL_GPL(generic_handle_irq);
338339

340+
#ifdef CONFIG_HANDLE_DOMAIN_IRQ
341+
/**
342+
* __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
343+
* @domain: The domain where to perform the lookup
344+
* @hwirq: The HW irq number to convert to a logical one
345+
* @lookup: Whether to perform the domain lookup or not
346+
* @regs: Register file coming from the low-level handling code
347+
*
348+
* Returns: 0 on success, or -EINVAL if conversion has failed
349+
*/
350+
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
351+
bool lookup, struct pt_regs *regs)
352+
{
353+
struct pt_regs *old_regs = set_irq_regs(regs);
354+
unsigned int irq = hwirq;
355+
int ret = 0;
356+
357+
irq_enter();
358+
359+
#ifdef CONFIG_IRQ_DOMAIN
360+
if (lookup)
361+
irq = irq_find_mapping(domain, hwirq);
362+
#endif
363+
364+
/*
365+
* Some hardware gives randomly wrong interrupts. Rather
366+
* than crashing, do something sensible.
367+
*/
368+
if (unlikely(!irq || irq >= nr_irqs)) {
369+
ack_bad_irq(irq);
370+
ret = -EINVAL;
371+
} else {
372+
generic_handle_irq(irq);
373+
}
374+
375+
irq_exit();
376+
set_irq_regs(old_regs);
377+
return ret;
378+
}
379+
#endif
380+
339381
/* Dynamic interrupt handling */
340382

341383
/**

0 commit comments

Comments
 (0)