Skip to content

Day83 - Deferred Work Architecture and Selection

Objective

Understand how Linux drivers choose between different deferred execution mechanisms.

The goal of this lab is not to learn new APIs, but to understand the design trade-offs between:

  • Threaded IRQ
  • Workqueue
  • kthread_worker
  • Dedicated kthread

and how they fit into real Linux driver architectures.


Background

Interrupt handlers execute in hard IRQ context.

Hard IRQ context has strict limitations:

  • Cannot sleep
  • Cannot block
  • Cannot acquire sleeping locks
  • Should finish as quickly as possible

Therefore, Linux drivers often move processing into a deferred execution context.

Typical architecture:

IRQ
ISR
Deferred Execution
Thread Context

Lab1 - Execution Context Comparison

Goal

Compare where different deferred mechanisms execute.

Mechanisms tested:

  • Threaded IRQ
  • Workqueue
  • kthread_worker
  • Dedicated kthread

Test Code

Each execution context prints:

pthread_self()

to identify the executing thread.

Example:

static void print_context(const char *name)
{
    log_info("[%s] tid=%lu",
             name,
             (unsigned long)pthread_self());
}

Threaded IRQ

Top half:

static irqreturn_t irq_top_half(int irq, void *data)
{
    print_context("TOP_HALF");

    return IRQ_WAKE_THREAD;
}

Thread function:

static irqreturn_t irq_thread_fn(int irq, void *data)
{
    print_context("THREADED_IRQ");

    msleep(100);

    log_info("[THREADED_IRQ] sleep OK");

    return IRQ_HANDLED;
}

Workqueue

static void work_func(struct work_struct *work)
{
    print_context("WORKQUEUE");

    msleep(100);

    log_info("[WORKQUEUE] sleep OK");
}

kthread_worker

static void kthread_work_func(struct kthread_work *work)
{
    print_context("KTHREAD_WORKER");

    msleep(100);

    log_info("[KTHREAD_WORKER] sleep OK");
}

Dedicated kthread

static int demo_thread(void *data)
{
    print_context("KTHREAD");

    msleep(100);

    return 0;
}

Result

Example output:

[TOP_HALF] tid=134842914649920

[THREADED_IRQ] tid=134842912077504

[WORKQUEUE] tid=134842903684800

[KTHREAD_WORKER] tid=134842870113984

[KTHREAD] tid=134842861721280

Observation

Top half executes in IRQ context.

The following execute in thread context:

  • Threaded IRQ
  • Workqueue
  • kthread_worker
  • Dedicated kthread

All of them can sleep.

This demonstrates the primary purpose of deferred execution:

Move work from IRQ context
to thread context.

Lab2 - Driver Pattern Selection

Goal

Map common Linux driver designs to appropriate deferred execution mechanisms.


Button Driver

Scenario

GPIO button debounce.

Architecture:

IRQ
delayed_work
debounce

Implementation:

static void button_work(struct work_struct *work)
{
    log_info("[BUTTON] debounced");
}

Recommended mechanism:

Workqueue

Reason:

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

Sensor Driver

Scenario

Data-ready interrupt from a sensor.

Architecture:

IRQ
thread_fn()
read register
update value

Implementation:

static irqreturn_t sensor_irq_thread_fn(
        int irq,
        void *data)
{
    log_info("[SENSOR] read register");

    msleep(100);

    log_info("[SENSOR] update value");

    return IRQ_HANDLED;
}

Recommended mechanism:

Threaded IRQ

Reason:

  • Strongly tied to a specific IRQ
  • Requires sleepable bus access
  • Processing is relatively simple

Touch Driver

Scenario

Touch packet processing.

Architecture:

IRQ
queue_kthread_work()
read packet
parse packet
report event

Implementation:

static void touch_work_func(
        struct kthread_work *work)
{
    log_info("[TOUCH] read packet");

    log_info("[TOUCH] parse packet");

    log_info("[TOUCH] report event");
}

Recommended mechanism:

kthread_worker

Reason:

  • Ordered packet processing
  • FIFO execution
  • Dedicated worker thread

WiFi Driver

Scenario

Multiple event sources.

Events:

  • RX
  • TX
  • Timeout
  • Recovery
  • Scan

Architecture:

IRQ
Timer
User Request
Event Queue
Dedicated Thread

Implementation:

static int wifi_thread(void *data)
{
    while (!kthread_should_stop()) {

        log_info("[WIFI] process RX");

        log_info("[WIFI] process TX");

        log_info("[WIFI] process timeout");

        msleep(100);
    }

    return 0;
}

Recommended mechanism:

Dedicated kthread

Reason:

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

Deferred Mechanism 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

Key Takeaways

Deferred execution is fundamentally about execution context selection.

Different mechanisms represent different trade-offs:

Mechanism Sleep Ordering Dedicated Thread FSM
ISR No N/A No No
Threaded IRQ Yes IRQ-local Yes No
Workqueue Yes Limited No No
kthread_worker Yes FIFO Yes No
Dedicated kthread Yes Custom Yes Yes

Understanding these trade-offs is more important than memorizing APIs.