Skip to content

IRQ Bottom Half and Deferred Work

Introduction

Interrupt handlers should execute as quickly as possible.

Linux therefore separates interrupt processing into:

  • Top Half
  • Bottom Half

This design minimizes interrupt latency while allowing more complex processing to execute later in process context.


Top Half

The Top Half executes immediately after an interrupt occurs.

Responsibilities:

  • Acknowledge interrupt source
  • Clear interrupt status
  • Capture critical information
  • Schedule deferred processing

Characteristics:

  • Runs in IRQ context
  • Cannot sleep
  • Must complete quickly

Typical flow:

IRQ
Top Half ISR
Return

Bottom Half

The Bottom Half performs deferred processing after the interrupt handler returns.

Responsibilities:

  • Data processing
  • State machine updates
  • Buffer management
  • Operations that may sleep

Characteristics:

  • Runs in process context
  • Sleep allowed
  • Can perform longer operations

Typical flow:

IRQ
Top Half
Bottom Half

Why Separate Top Half and Bottom Half

Without deferred processing:

IRQ
ISR
Lengthy Processing
Return

Problems:

  • Increased interrupt latency
  • Reduced system responsiveness
  • Longer interrupt-disabled periods

With deferred processing:

IRQ
Top Half
Return
Bottom Half

Benefits:

  • Faster interrupt response
  • Improved system responsiveness
  • Better scalability

Workqueue

Workqueue is the most common Linux Bottom Half mechanism.

Architecture:

queue_work()
+----------------+
|  Workqueue     |
+----------------+
+----------------+
| Worker Thread  |
+----------------+
work->func()

Example:

static irqreturn_t button_isr(int irq, void *dev_id)
{
    queue_work(button_wq, &button_work);

    return IRQ_HANDLED;
}

Characteristics:

  • Shared worker thread
  • Process context
  • Sleep allowed
  • General-purpose deferred execution

Typical use cases:

  • Driver state updates
  • Background processing
  • Data parsing
  • Non-time-critical tasks

Delayed Work

Delayed Work combines:

Timer
 +
Workqueue

Architecture:

schedule_delayed_work()
       Timer Delay
        queue_work()
       Worker Thread

Example:

schedule_delayed_work(&button_work, 500);

Meaning:

Execute Bottom Half
500 ms later

Typical use cases:

  • GPIO debounce
  • Retry operations
  • Delayed state transitions
  • Timeout handling

GPIO Debounce Pattern

Mechanical switches often generate multiple interrupts during a single press.

Example:

IRQ
IRQ
IRQ

Without debounce:

Work
Work
Work

With Delayed Work:

IRQ
mod_delayed_work(500ms)

IRQ
mod_delayed_work(500ms)

IRQ
mod_delayed_work(500ms)

Last IRQ + 500ms
Work

Result:

Only one Bottom Half execution

Threaded IRQ

Threaded IRQ executes interrupt processing inside a dedicated IRQ thread.

Architecture:

IRQ
Top Half
IRQ_WAKE_THREAD
IRQ Thread
thread_fn()

Example:

static irqreturn_t top_handler(int irq,
                               void *dev_id)
{
    return IRQ_WAKE_THREAD;
}
static irqreturn_t thread_fn(int irq,
                             void *dev_id)
{
    process_data();

    return IRQ_HANDLED;
}

Characteristics:

  • Dedicated IRQ thread
  • Process context
  • Sleep allowed
  • Lower scheduling latency than shared workqueues

request_threaded_irq()

Prototype:

int request_threaded_irq(
        unsigned int irq,
        irq_handler_t handler,
        irq_handler_t thread_fn,
        unsigned long flags,
        const char *name,
        void *dev);

Execution flow:

IRQ
handler()
IRQ_WAKE_THREAD
thread_fn()

If:

handler == NULL

Linux automatically uses a default wake-up handler.

Flow:

IRQ
Default Handler
IRQ_WAKE_THREAD
thread_fn()

Workqueue vs Threaded IRQ

Item Workqueue Threaded IRQ
Worker Shared Worker Dedicated IRQ Thread
Trigger queue_work() IRQ_WAKE_THREAD
Context Process Context Process Context
Sleep Allowed Yes Yes
Ownership Workqueue Subsystem IRQ Subsystem
Latency Usually Higher Usually Lower
Common Usage Deferred Work Interrupt Processing

Choosing Between Workqueue and Threaded IRQ

Use Workqueue when:

  • Processing is not time critical
  • Work can be shared across devices
  • General deferred execution is required

Use Threaded IRQ when:

  • Processing belongs directly to an interrupt source
  • Lower scheduling latency is desired
  • Dedicated execution context is preferred

Relationship Between the Mechanisms

IRQ
Top Half
 ├─────────────┐
 │             │
 ▼             ▼

Workqueue   IRQ_WAKE_THREAD
 │             │
 ▼             ▼

Worker      IRQ Thread
 │             │
 └──────┬──────┘

Process Context

All three mechanisms discussed in this topic:

  • Workqueue
  • Delayed Work
  • Threaded IRQ

exist to move work out of IRQ context and into process context while keeping interrupt response time low.