This post contains some notes relating to the code execution flow in the Linux kernel when an interrupt request (IRQ) is being addressed by the system. According to the documentation, the IRQ subsystem provides three main layers of abstraction:
- High-level driver API
- High-level IRQ flow handlers
- Chip-level hardware encapsulation
The IRQ handling process deals mostly with flow handlers and chip-level abstraction, so the IRQ driver API will not be explored here.
When some significant hardware change occurs, low-level architecture code either
calls the handle_irq
function stored in the IRQ descriptor (irq_desc
)
directly or call a more generic generic_handle_irq_desc
function. Because
hardware can provide different types of interrupt sources (the rising/falling
edge of an electric signal, the voltage level (low/high) of a circuit line)
different actions may need to be taken in each case. The high-level IRQ flow
handlers provide pre-defined approaches to deal with hardware interrupts. These
flow handlers are assigned to the interrupt descriptors at boot time or during
device initialization. It is also possible for architecture code to implement
specific flow handlers. Whatever function best suits the interrupt flow
handling, irq_desc
holds a pointer to it which is then used to give
appropriate continuation to the IRQ handling.
Assuming the flow handler is one of the high-level IRQ flow handlers, it may do
a few things before proceeding with the IRQ handling. For instance, the hardware
interrupt controller may somehow need an ack from the CPU, signaling that the
interrupt was properly received. It may also be needed to mask (disable) and/or
unmask (enable) interrupts for some chips. The beauty in it is that the flow
handlers don’t need to know architecture-specific details to accomplish these
actions, all of it can be done relying on the irq_chip
abstraction which
encapsulates the hardware relevant functions. Afterward, handle_irq_event
is
called to set the IRQ state as “in progress”, acquire the IRQ description lock,
and then call handle_irq_event_percpu
. If handle_percpu_irq
is the flow
handler being used, the number of IRQs handled by the CPU is increment and
handle_irq_event_percpu
is called directly (per CPU IRQs are not serialized).
handle_irq_event_percpu
does nothing but calling __handle_irq_event_percpu
and waiting for its return to add some randomness to the pool of interrupts
handled by the CPU. __handle_irq_event_percpu
is where the IRQ subsystem
finally let other stakeholders of the interrupt event take actions. The IRQ
descriptor has a list of actions, each one having a handler. The handle irq
event function calls the handler
function for each action as can be seen in
both the diagram and code below. This handler is known as the “top half” of IRQ
handling. The action may have an additional handler function (thread_fn
)
which, if not NULL, is executed in a threaded interrupt context (this is the
bottom half of IRQ handling). Both the handler and the threaded handler
functions are assigned to the irq_desc
struct by calling
request_threaded_irq
, which is often done, direct or indirectly, by device
drivers during device initialization. Therefore, the for_each_action_of_desc
loop in __handle_irq_event_percpu
is very important because it is where
drivers get notified of hardware events and can work over them, for instance,
fetching data from a device.
After all the actions have been executed or any of them have returned
IRQ_HANDLED, __handle_irq_event_percpu
returns an oring with all the flags
returned by each executed handler. These flags are used by
handle_irq_event_percpu
to add randomness to IRQ handling as mentioned above.
After that, the flags are returned back to handle_irq_event
(or to
handle_percpu_irq
) which in turn returns the flags back to the flow handlers.
Though none of the generic flow handlers use the returned flags, the will be
available in case architecture-specific flow handlers want to use them.
Related material
While seeking to understand the IRQ handling process in the Linux kernel, I consulted some related works that, despite not referenced above, helped me to put all the pieces together to the point of writing these notes.
The Linux Device Drivers book has an entire chapter about interrupts.
Alison and Peloton’s talk about IRQs in the Embeded Linux Conference.
In tree files:
- Documentation/core-api/genericirq.rst
- include/linux/interrupt.h
- include/linux/irqdesc.h
- kernel/irq/chip.c
- kernel/irq/handle.c
- kernel/irq/manage.c
Revision History:
- Rev1 (2020-05-07): Release