Skip to content

Deferred Work Selection

Deferred execution is a fundamental design technique in Linux drivers.

The primary goal is not simply to delay work, but to move processing from a restricted execution context to a more appropriate execution context.

Typical example:

IRQ
 ↓
ISR
 ↓
Deferred Execution
 ↓
Thread Context

This allows drivers to perform operations that may sleep, block, or require significant processing time.


Why Deferred Execution Exists

Interrupt handlers execute in hard IRQ context.

Hard IRQ context has strict limitations:

  • Cannot sleep
  • Cannot block
  • Cannot wait for events
  • Should execute as quickly as possible

For example, the following operations are typically not allowed inside an ISR:

I2C transfer
SPI transfer
mutex_lock()
wait_event()
msleep()

Therefore, Linux drivers commonly move work into a thread context.


Execution Context

Hard IRQ Context

Examples:

irqreturn_t my_irq_handler(...)
{
    ...
}

Characteristics:

  • Cannot sleep
  • Cannot block
  • Must return quickly

Typical responsibilities:

  • Acknowledge interrupt
  • Capture minimal state
  • Schedule deferred work

Thread Context

Examples:

  • Threaded IRQ
  • Workqueue worker
  • kthread_worker
  • Dedicated kthread

Characteristics:

  • Can sleep
  • Can block
  • Can use mutexes
  • Can wait for events

Typical responsibilities:

  • Register access
  • Data processing
  • State-machine execution
  • Background work

Deferred Execution Mechanisms

Linux provides multiple mechanisms for deferred execution.


Threaded IRQ

Architecture:

IRQ
 ↓
Top Half
 ↓
IRQ_WAKE_THREAD
 ↓
thread_fn()

Characteristics:

  • Dedicated IRQ thread
  • Closely tied to a specific interrupt source
  • Supports sleeping operations

Typical use cases:

  • I2C sensors
  • SPI sensors
  • GPIO expanders

Example:

IRQ
 ↓
Read Sensor Register
 ↓
Update Driver State

Workqueue

Architecture:

IRQ
 ↓
queue_work()
 ↓
Worker Pool
 ↓
work_func()

Characteristics:

  • Shared worker threads
  • Low management overhead
  • General-purpose deferred execution

Typical use cases:

  • Debounce logic
  • Background cleanup
  • Short deferred work

Example:

IRQ
 ↓
schedule_delayed_work()
 ↓
GPIO Debounce

kthread_worker

Architecture:

IRQ
 ↓
queue_kthread_work()
 ↓
Dedicated Worker
 ↓
work_func()

Characteristics:

  • Dedicated worker thread
  • FIFO ordering
  • Driver-specific execution context

Typical use cases:

  • Touch controllers
  • Packet processing
  • Ordered event handling

Example:

IRQ
 ↓
Read Packet
 ↓
Parse Packet
 ↓
Report Event

Dedicated kthread

Architecture:

IRQ
Timer
User Request
 ↓
Event Queue
 ↓
Dedicated Thread

Typical implementation:

while (!kthread_should_stop()) {
    ...
}

Characteristics:

  • Full control over execution flow
  • Supports state machines
  • Supports multiple event sources

Typical use cases:

  • WiFi drivers
  • Bluetooth drivers
  • Modem drivers
  • Complex protocol stacks

Producer / Queue / Consumer Model

Many Linux drivers follow a common architecture:

Producer
    ↓
 Queue
    ↓
Consumer

Examples:

Component Examples
Producer IRQ, Timer, Hardware Event
Queue FIFO, Ring Buffer, Work List
Consumer Workqueue, kthread_worker, kthread

The deferred execution mechanism often acts as the consumer.


Important Observation

A work item is not necessarily an event.

Example:

IRQ
 ↓
Hardware FIFO
 ↓
Worker

Multiple interrupts may occur while a worker is already running.

This is often acceptable because:

Event
    Stored in FIFO

Worker
    Consumes FIFO

The worker processes all pending data before exiting.

This prevents event loss even when duplicate work scheduling is suppressed.


Driver Design Patterns

GPIO Button

Architecture:

IRQ
 ↓
delayed_work
 ↓
debounce

Recommended mechanism:

Workqueue

Reason:

  • Simple processing
  • No state machine
  • No dedicated thread required

I2C Sensor

Architecture:

IRQ
 ↓
thread_fn()
 ↓
read register

Recommended mechanism:

Threaded IRQ

Reason:

  • IRQ-bound processing
  • Sleepable register access
  • Simple execution flow

Touch Controller

Architecture:

IRQ
 ↓
kthread_worker
 ↓
read packet
 ↓
parse packet

Recommended mechanism:

kthread_worker

Reason:

  • Ordered packet processing
  • FIFO execution
  • Dedicated worker

WiFi Driver

Architecture:

IRQ
Timer
User Request
 ↓
Event Queue
 ↓
Dedicated Thread

Recommended mechanism:

Dedicated kthread

Reason:

  • Multiple event sources
  • Long-running background processing
  • State-machine based design

Selection Matrix

Requirement Recommended Mechanism
Very short work, no sleep ISR
IRQ-bound sleepable work Threaded IRQ
Simple deferred work Workqueue
FIFO ordering required kthread_worker
Multiple event sources Dedicated kthread
State machine required Dedicated kthread

Selection Flow

Need sleep?
    │
    ├─ No
    │    ↓
    │   ISR
    │
    └─ Yes
         │
         ├─ Need FSM?
         │     ↓
         │  Dedicated kthread
         │
         ├─ Need multiple event sources?
         │     ↓
         │  Dedicated kthread
         │
         ├─ Need FIFO ordering?
         │     ↓
         │  kthread_worker
         │
         ├─ IRQ-bound processing?
         │     ↓
         │  Threaded IRQ
         │
         └─ Workqueue

Related Topics

  • IRQ Bottom Half and Deferred Work
  • Workqueue Internals
  • Kthread
  • Wait Queue Internals

Related Labs