← Previous Lecture | Next Lecture →
Every call to request_irq() or devm_request_irq() takes a flags argument. In this free Linux device drivers course lecture we go through every commonly used interrupt flag, what it changes about how your handler runs, and when you should actually set it. Understanding these interrupt flags properly is one of the fastest ways to avoid subtle, hard-to-reproduce driver bugs.
What You Will Learn
- Why interrupt flags are a bitmask, and how to combine them
- What each commonly used
IRQF_*flag actually changes at runtime - The difference between sharing-related, threading-related, and power-management-related flags
- A working example that uses threaded interrupts with the correct flags
Prerequisites
This lecture builds directly on the previous one in this module, where we covered request_irq() and devm_request_irq(). If you haven’t gone through that yet, start there first.
Interrupt Flags Are a Bitmask
The flags parameter is declared as unsigned long, and every individual flag is a single bit. That means you combine flags with the bitwise-OR operator to get their combined effect, for example:
unsigned long flags = IRQF_SHARED | IRQF_TRIGGER_RISING;
All of these constants are defined in the linux/interrupt.h header. They roughly fall into three groups: sharing behavior, threading behavior, and power-management/suspend behavior. Let’s go through each group.
| Sharing IRQF_SHARED, IRQF_PROBE_SHARED |
Threading IRQF_ONESHOT, IRQF_NO_THREAD |
Power Management IRQF_NO_SUSPEND, IRQF_COND_SUSPEND |
Common Interrupt Flags Reference Table
| Flag | What It Does |
|---|---|
IRQF_SHARED | Allows more than one device driver to register a handler on the same physical IRQ line. Common on PCI buses where several devices are wired to one line. All sharing drivers must set this flag, or registration fails. |
IRQF_ONESHOT | Used with threaded interrupts. Keeps the hardware IRQ line masked until the threaded handler function has fully finished running, preventing the line from re-firing mid-processing. |
IRQF_NO_THREAD | Marks the interrupt as one that must never be forced into threaded mode, even under a system-wide “force threaded” configuration. Typically used for latency-critical interrupts like the timer tick. |
IRQF_NO_SUSPEND | Keeps the interrupt active even while the system is in a suspend state. Used for interrupts that must keep functioning, such as a wakeup source. |
IRQF_TIMER | A convenience macro combining the internal timer-interrupt flag with IRQF_NO_SUSPEND and IRQF_NO_THREAD, reserved for the kernel’s own periodic timer interrupt. |
IRQF_PERCPU | Indicates the interrupt is per-CPU, meaning each CPU core has its own private instance of this interrupt line rather than sharing one global line. |
IRQF_PROBE_SHARED | Tells the kernel not to print a warning if this handler is later found sharing a line with an interrupt that doesn’t support sharing well. |
IRQF_TRIGGER_RISING / FALLING / HIGH / LOW | Sets the electrical trigger type for the line. On most modern platforms this is instead configured through the device tree, but the flags remain available for cases where it must be set in code. |
Threaded Interrupts and IRQF_ONESHOT in Practice
A very common modern pattern is a threaded interrupt handler, registered with devm_request_threaded_irq(), where a short “primary” handler runs in hard interrupt context and a longer “threaded” handler runs afterward in a dedicated kernel thread. When you use this pattern with a level-triggered line, you almost always need IRQF_ONESHOT so the hardware line stays masked until the thread finishes:
#include <linux/interrupt.h>
static irqreturn_t mydev_irq_primary(int irq, void *dev_id)
{
/* Quick check only - wake the threaded handler */
return IRQ_WAKE_THREAD;
}
static irqreturn_t mydev_irq_thread(int irq, void *dev_id)
{
struct mydev_priv *priv = dev_id;
/* Slower work: I2C/SPI reads, processing, notifying userspace */
dev_info(priv->dev, "threaded handler processing event\n");
return IRQ_HANDLED;
}
/* Inside probe(): */
ret = devm_request_threaded_irq(&pdev->dev, priv->irq,
mydev_irq_primary,
mydev_irq_thread,
IRQF_ONESHOT,
"mydev-irq", priv);
Common Mistakes and Troubleshooting Tips
- Forgetting IRQF_ONESHOT with a threaded handler: On a level-triggered line this can cause the hardirq to keep re-firing before the thread has a chance to service and clear the condition.
- Setting IRQF_SHARED without a unique dev_id: On a shared line, every driver’s
dev_idmust be unique and non-NULL, or the kernel cannot tell handlers apart when freeing. - Using IRQF_TRIGGER_* flags that conflict with the device tree: If the trigger type is already fixed in the device tree’s
interruptsproperty, setting a conflicting flag in code can produce a warning or be silently ignored, depending on the interrupt controller driver.
Best Practices
- Prefer configuring trigger type (rising/falling/level) in the device tree rather than hardcoding
IRQF_TRIGGER_*flags in the driver, unless the hardware genuinely requires runtime control. - Always pair
IRQF_ONESHOTwith threaded handlers on shared or level-triggered lines. - Keep the primary (hardirq) handler as small as possible — a register read and a return value is often all it should do.
Key Takeaways
- Interrupt flags are a bitmask combined with bitwise-OR and passed as the third argument to
request_irq()-family functions. IRQF_SHAREDenables sharing a physical line between drivers;IRQF_ONESHOTis essential for most threaded handlers.- Power-management flags like
IRQF_NO_SUSPENDkeep specific interrupts alive across system suspend.
Frequently Asked Questions
Q1. Can I combine multiple interrupt flags together?
Yes. Since each flag is a distinct bit, combine them with bitwise-OR, for example IRQF_SHARED | IRQF_ONESHOT.
Q2. What happens if I forget IRQF_SHARED on a shared PCI line?
The second driver’s call to request the same IRQ number fails and returns -EBUSY.
Q3. Is IRQF_ONESHOT required for every threaded interrupt?
It’s required whenever the underlying line is level-triggered or shared, to keep it masked until the thread finishes. For simple edge-triggered, non-shared lines it’s often unnecessary but is still commonly set for safety.
Q4. Where should I set the trigger type — in flags or in the device tree?
On modern platform drivers, the device tree’s interrupts property is the preferred place. The IRQF_TRIGGER_* flags exist mainly for legacy or dynamically-configured cases.
Q5. What does IRQF_PERCPU mean in practical terms?
It marks the interrupt as having a separate instance per CPU core, common for things like per-core performance counters or per-core timer interrupts.
Next up: understanding level-triggered versus edge-triggered interrupts and how to identify which type your hardware uses.

2 Comments