What are Linux Kernel Interrupt Flags (IRQF_*) Explained with Examples-Linux Device Drivers Course

Linux Kernel Interrupt Flags (IRQF_*) Explained with Examples
Free Linux Kernel Programming Course — Interrupt Handling Module, Lecture 2
Kernel Version: 6.x
Reading Time: 13 min
Level: Beginner to Intermediate

← 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.

Topics covered in this free Linux kernel development course lecture:
IRQF_SHARED IRQF_ONESHOT IRQF_TIMER IRQF_NO_SUSPEND IRQF_TRIGGER_* flags Threaded IRQ handlers

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.

Interrupt Flag Categories
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_SHAREDAllows 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_ONESHOTUsed 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_THREADMarks 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_SUSPENDKeeps 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_TIMERA 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_PERCPUIndicates 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_SHAREDTells 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 / LOWSets 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_id must 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 interrupts property, 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_ONESHOT with 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_SHARED enables sharing a physical line between drivers; IRQF_ONESHOT is essential for most threaded handlers.
  • Power-management flags like IRQF_NO_SUSPEND keep 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.

Continue the Free Linux Kernel Programming Course

Next up: understanding level-triggered versus edge-triggered interrupts and how to identify which type your hardware uses.

← Previous Lecture  |  Next Lecture →

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *