Understanding Linux IRQ Domains: Mapping Hardware to Virtual Interrupt Numbers
This article explains why Linux uses virtual IRQ numbers, describes the irq_domain framework and its data structures, compares linear, tree, and no‑mapping strategies, and walks through the complete interrupt‑mapping process on an ARM64 platform with code examples.
Hard Interrupts and Virtual IRQ Numbers
In the Linux kernel, the GIC interrupt controller acknowledges a hardware interrupt first, then looks up a corresponding virtual IRQ number before proceeding with handling. The function
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, bool lookup, struct pt_regs *regs)shows the lookup step where irq_find_mapping(domain, hwirq) obtains the virtual IRQ.
Because modern SoCs often contain multiple interrupt controllers (e.g., GIC, GPIO), hardware interrupt numbers can repeat across controllers. A single virtual IRQ abstracts away the controller origin, allowing software to refer to an interrupt without caring which controller generated it.
irq_domain Framework
The kernel provides the irq_domain management framework to map hardware IRQ numbers (hwirq) to virtual IRQ numbers (virq). Each interrupt controller registers its own irq_domain.
irq_domain Data Structure
struct irq_domain {
struct list_head link;
const char *name;
const struct irq_domain_ops *ops;
void *host_data;
unsigned int flags;
unsigned int mapcount;
/* Optional data */
struct fwnode_handle *fwnode;
enum irq_domain_bus_token bus_token;
struct irq_domain_chip_generic *gc;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_domain *parent;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
struct dentry *debugfs_file;
#endif
/* reverse map data */
irq_hw_number_t hwirq_max;
unsigned int revmap_direct_max_irq;
unsigned int revmap_size;
struct radix_tree_root revmap_tree;
struct mutex revmap_tree_mutex;
unsigned int linear_revmap[];
};link: connects the domain to the global irq_domain_list.
name: human‑readable domain name.
ops: set of operations that implement the mapping logic.
mapcount: number of successfully mapped interrupts.
fwnode: device‑tree node of the associated interrupt controller.
parent: pointer to a parent domain for hierarchical domains.
hwirq_max: maximum hardware IRQ number supported by the domain.
revmap_tree: radix‑tree root for tree‑based mappings.
linear_revmap: linear array for hwirq‑to‑virq reverse mapping.
irq_domain Mapping Types
Linear mapping
Linear mapping keeps a fixed table indexed by the hardware IRQ number. It is efficient when the number of hardware IRQs is small and dense (e.g., < 256). Lookup time is constant, but the table size grows with the maximum hardware IRQ number. irq_domain_add_linear Tree mapping
Tree mapping uses a radix tree to store mappings, suitable for very large hardware IRQ ranges because it avoids allocating a huge dense table. Lookup cost depends on the number of entries in the tree. irq_domain_add_tree No mapping
When hardware can program its own IRQ numbers (e.g., via registers), a direct mapping is possible and no software translation is needed. The helper irq_create_direct_mapping() implements this.
irq_domain_add_nomapComplete Interrupt Mapping Process (ARM64 DTB Example)
During interrupt‑controller initialization, the kernel registers an irq_domain. For a GIC controller the sequence is:
+-> gic_of_init
+-> gic_init_bases
+-> irq_domain_add_linear
+-> _irq_domain_addThe function struct irq_domain *__irq_domain_add(...) allocates and initializes the irq_domain structure, sets up the radix tree, mutexes, and links the domain into the global list.
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops, void *host_data) {
// allocation and initialization logic …
domain->ops = ops;
domain->host_data = host_data;
domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;
// add to global list …
return domain;
}Device drivers later create the hardware‑to‑virtual mapping by calling irq_of_parse_and_map(), which parses the device‑tree node and invokes irq_create_of_mapping().
unsigned int irq_of_parse_and_map(struct device_node *dev, int index) {
struct of_phandle_args oirq;
if (of_irq_parse_one(dev, index, &oirq))
return 0;
return irq_create_of_mapping(&oirq);
}The helper of_irq_parse_one() extracts the "interrupts" property from the device‑tree and fills oirq.args. The mapping flow then proceeds through several internal helpers:
+-> irq_create_of_mapping
+-> irq_create_fwspec_mapping
+-> irq_find_matching_fwspec // locate the irq_domain for the device node
+-> irq_domain_translate // translate hardware IRQ number and trigger type
+-> irq_create_mapping // allocate a virtual IRQ number
+-> irq_domain_alloc_descs
+-> irq_domain_alloc_irqs_hierarchy
+-> domain->ops->alloc (gic_irq_domain_alloc)
+-> gic_irq_domain_map // creates the hwirq‑to‑virq mapping
+-> irq_domain_associate
+-> domain->ops->map int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq, struct irq_chip *chip, void *chip_data) {
struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
if (!irq_data)
return -ENOENT;
irq_data->hwirq = hwirq;
irq_data->chip = chip ? chip : &no_irq_chip;
irq_data->chip_data = chip_data;
return 0;
}Finally, the virtual IRQ's irq_data structure stores the original hardware IRQ number, completing the mapping.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
