Skip to content

Day 81 - Workqueue Internals

Today's Goal

Understand how Linux implements Workqueue internally, including worker pools, concurrency management, work lifecycle control, and the relationship between Workqueue, kthread, and Threaded IRQ.


Topics Covered

Why Linux Uses Workqueue

Workqueue allows deferred work to execute in process context instead of interrupt context.

Benefits:

  • Sleepable context
  • Can use mutexes
  • Can perform blocking operations
  • Simplifies deferred processing

Typical flow:

IRQ Handler
schedule_work()
Workqueue
Worker Thread
Work Function

System Workqueue

Linux provides a global workqueue that drivers can share.

Common APIs:

schedule_work(&work);

Instead of creating a dedicated workqueue:

queue_work(my_wq, &work);

The simulation framework was extended with:

workqueue_system_init()
workqueue_system_exit()
schedule_work()

Worker Pool

A workqueue is not limited to a single worker thread.

Modern Linux workqueues use worker pools:

Workqueue
Worker Pool
    ├─ Worker 1
    ├─ Worker 2
    ├─ Worker 3
    └─ Worker 4

Multiple workers can execute independent work items concurrently.


Work Lifecycle

A work item transitions through several states:

IDLE
PENDING
RUNNING
IDLE

State tracking was implemented using:

work->pending
work->running

This prevents duplicate scheduling of the same work item.


flush_work()

Wait until a specific work item completes.

Typical usage:

flush_work(&work);

Useful during driver removal to ensure worker threads are no longer accessing resources.


flush_workqueue()

Wait until all pending and running work items in a workqueue complete.

flush_workqueue(wq);

The simulation framework tracks active work items using:

wq->active_works

cancel_work_sync()

Cancel a pending work item or wait for a running work item to finish.

Behavior:

State Result
PENDING Removed from queue
RUNNING Wait for completion
IDLE Return immediately

Workqueue vs kthread

Item Workqueue kthread
Thread creation Kernel managed Driver managed
Sleepable Yes Yes
Best for Deferred work Long-running tasks
Resource management Simple More flexible
Typical usage Very common Less common

Labs Completed

Lab1 - System Workqueue

Implemented:

  • system_wq
  • schedule_work()
  • workqueue_system_init()
  • workqueue_system_exit()

Verified:

schedule_work()
system_wq
worker thread
work function

Lab2 - Worker Pool

Converted the framework from:

Single Worker

to:

Worker Pool

Implemented:

#define WORKER_COUNT 4

Each worker waits on the same workqueue and executes work items concurrently.

Verified that multiple work items execute in parallel.


Lab3 - Work Lifecycle

Added work state tracking:

bool pending;
bool running;

Updated scheduling logic:

if (work->pending || work->running)
    return -EEXIST;

Verified:

  • IDLE → queue succeeds
  • PENDING → queue rejected
  • RUNNING → queue rejected
  • IDLE after completion → queue succeeds

Lab4 - Work Synchronization APIs

Implemented:

flush_work()
flush_workqueue()
cancel_work_sync()

Added:

pthread_cond_t work_done;
int active_works;

Verified:

  • flush individual work item
  • flush entire workqueue
  • cancel pending work item
  • wait for running work item

Key Takeaways

  • Workqueue executes deferred work in process context.
  • Linux commonly uses a shared system workqueue.
  • Worker pools provide concurrency and scalability.
  • Work lifecycle management is essential for correctness.
  • flush_work() and cancel_work_sync() are frequently used during driver shutdown.
  • Workqueue internally resembles a kernel-managed worker-thread framework.

Next Step

Day 82 - kthread Internals

Topics:

  • Kernel Thread Architecture
  • kthread_create()
  • kthread_run()
  • kthread_stop()
  • kthread_should_stop()
  • kthread Worker Pattern
  • kthread_work
  • Workqueue vs kthread Internals
  • Driver Usage Patterns