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:
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:
Why Separate Top Half and Bottom Half¶
Without deferred processing:
Problems:
- Increased interrupt latency
- Reduced system responsiveness
- Longer interrupt-disabled periods
With deferred processing:
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:
Architecture:
Example:
Meaning:
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:
Without debounce:
With Delayed Work:
IRQ
↓
mod_delayed_work(500ms)
IRQ
↓
mod_delayed_work(500ms)
IRQ
↓
mod_delayed_work(500ms)
Last IRQ + 500ms
↓
Work
Result:
Threaded IRQ¶
Threaded IRQ executes interrupt processing inside a dedicated IRQ thread.
Architecture:
Example:
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:
If:
Linux automatically uses a default wake-up handler.
Flow:
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.