Linux Kernel Workqueue Architecture (cmwq)

Concurrency Managed Workqueues — v7.2-rc5 · kernel/workqueue.c
Driver / Subsystem
work_struct
workqueue_struct
pool_workqueue (pwq)
worker_pool
worker (kworker)
struct my_device Driver's private structure Contains embedded work_struct(s) INIT_WORK() Initialize work_struct data=0, entry=empty, func=handler queue_work(wq, &work) Submit work to workqueue or schedule_work() for system_percpu_wq my_handler(work) Work handler function Runs in process context, can sleep work_struct A func = handler_a PENDING=1 when queued work_struct B func = handler_b PENDING=1 when queued work_struct C func = handler_a Same handler, different data work_struct D func = handler_c Queued on highpri wq system_percpu_wq Default per-CPU workqueue schedule_work() uses this system_dfl_wq "events_unbound" WQ_UNBOUND, max_active=512 system_highpri_wq High priority work WQ_HIGHPRI, nice = -20 alloc_workqueue("my_wq") Driver's custom workqueue Custom flags, max_active per_cpu_ptr(wq->cpu_pwq, cpu) → pwq → pwq->pool → worker_pool pwq [CPU 0] nr_active: 2 → pool(CPU 0, normal) pwq [CPU 1] nr_active: 0 → pool(CPU 1, normal) pwq [CPU 2] nr_active: 1 → pool(CPU 2, normal) pwq [CPU 3] nr_active: 0 → pool(CPU 3, normal) pwq [CPU 0, H] nr_active: 1 → pool(CPU 0, highpri) pwq [unbound] NUMA node 0 → unbound pool CPU 0 · Normal nice = 0, nr_running: 1 worklist → [A] → [B] nr_idle: 1, nr_workers: 2 pool ID: 0 CPU 1 · Normal nice = 0, nr_running: 0 worklist → (empty) nr_idle: 1, nr_workers: 1 pool ID: 2 CPU 2 · Normal nice = 0, nr_running: 1 worklist → [C] nr_idle: 0, nr_workers: 1 pool ID: 4 CPU 3 · Normal nice = 0, nr_running: 0 worklist → (empty) nr_idle: 1, nr_workers: 1 pool ID: 6 CPU 0 · Highpri nice = -20 worklist → [D] nr_idle: 0, nr_workers: 1 pool ID: 1 Unbound Pool NUMA node 0 cpumask: {0,1,2,3} Workers can migrate pool ID: 8 kworker/0:0 BUSY executing A → busy_hash kworker/0:1 IDLE TASK_IDLE → idle_list kworker/1:0 IDLE — no work schedule() sleeping → idle_list kworker/2:0 BUSY executing C nr_idle=0 → may create kworker/3:0 IDLE — no work 5 min timeout active → idle_list kworker/0:H0 BUSY (highpri) executing D nice = -20 kworker/u8:0 IDLE Can run on CPU 0-3 CFS load-balanced worker->current_func(work) Pool lock released · IRQs enabled · Can sleep Concurrency Management wq_worker_sleeping → wake another if nr_running=0 Worker Lifecycle Created on demand · Destroyed after 5 min idle
Component Kernel Structure Role Key Fields / Details
Driver / Subsystem Creates and queues work items via INIT_WORK() + queue_work() Handler function runs in process context when work executes
Work Item struct work_struct Unit of deferred work — a function pointer + metadata data (pool/pwq + PENDING bit), entry (list linkage), func (handler)
Workqueue struct workqueue_struct Named channel for submitting work — does NOT own threads cpu_pwq (per-CPU pwq array), flags (WQ_UNBOUND, WQ_HIGHPRI, etc.), name
Pool Workqueue (pwq) struct pool_workqueue Bridge connecting a workqueue to a worker_pool — holds per-wq-per-pool accounting pool, wq, nr_active, inactive_works, max_active
Worker Pool struct worker_pool Pool of kworker threads that execute work items — 2 per CPU (normal + highpri) + unbound pools worklist, nr_running, nr_idle, busy_hash, idle_list, cpu
Worker (kworker) struct worker Kernel thread that picks work from pool→worklist and calls work→func(work) current_work, current_func, task, pool, flags (IDLE/BUSY/DIE)