driver-know-hows

device driver related stuff

View on GitHub

Work Queue Demo

Description

Demonstrates kernel work queues for deferred work execution in process context.

Features

Build & Test

make
sudo insmod workqueue_demo.ko

# Watch work execute (in another terminal)
dmesg -w

# Wait ~10 seconds for all work to complete
sleep 10

# Unload (cancels pending work)
sudo rmmod workqueue_demo
dmesg | tail -20

Expected Output

=== Work Queue Demo ===

1. Scheduling immediate work...
Immediate work scheduled

2. Scheduling delayed work (2 seconds)...
Delayed work scheduled

3. Creating dedicated workqueue...
Dedicated workqueue created
Queued work item 0
Queued work item 1
Queued work item 2

=== Work items scheduled ===

# Shortly after:
Immediate work executed! Count: 1, PID: 1234
Immediate work done
Dedicated work [0]: Processing task_0, PID: 1235
Dedicated work [0]: Done
Dedicated work [1]: Processing task_1, PID: 1235
...

# After 2 seconds:
Delayed work executed! Count: 4, PID: 1236
Rescheduling delayed work (count: 4)

# After 4 seconds:
Delayed work executed! Count: 5, PID: 1236
Delayed work finished

Work Queue Types

1. System Workqueue

schedule_work(&work);  // Shared system workqueue

2. Delayed Work

schedule_delayed_work(&dwork, delay);

3. Dedicated Workqueue

wq = create_singlethread_workqueue("name");
queue_work(wq, &work);

Work Queue vs Other Mechanisms

Mechanism Context Can Sleep Latency Use Case
Interrupt Hardirq No Lowest Quick hardware ack
Tasklet Softirq No Low Fast deferred work
Work Queue Process Yes Medium Blocking operations
Kernel Thread Process Yes Variable Background tasks

Learning Points

Process Context

Work functions run in process context, so they can:

Cancellation

Always cancel work before cleanup:

cancel_work_sync(&work);         // Wait for completion
cancel_delayed_work_sync(&dwork); // Cancel and wait

Container Pattern

Use container_of to get your data:

struct my_work {
    struct work_struct work;
    int data;
};

static void my_work_fn(struct work_struct *work)
{
    struct my_work *mw = container_of(work, struct my_work, work);
    // Use mw->data
}

Common Use Cases

Advanced Options

// Create with specific flags
create_workqueue("name");                    // Multi-threaded
create_singlethread_workqueue("name");       // Single thread
alloc_workqueue("name", flags, max_active);  // Custom

// Flags
WQ_UNBOUND      // Not bound to specific CPU
WQ_HIGHPRI      // High priority
WQ_CPU_INTENSIVE // Long-running work

Debugging

# See active workqueues
cat /proc/workqueues

# See worker threads
ps aux | grep kworker