Skip to content

Wait Queue Internals

A wait queue is the fundamental Linux kernel mechanism used to put tasks to sleep and wake them when an event occurs.

Many kernel subsystems are built on top of wait queues, including:

  • Blocking I/O
  • poll()
  • Completions
  • Event-driven drivers
  • Various kernel worker mechanisms

Motivation

A task should not continuously poll for an event.

Inefficient approach:

while (!data_ready)
    ;

Problems:

  • Wastes CPU cycles
  • Poor scalability
  • Increases power consumption

A wait queue allows a task to sleep until the required condition becomes true.


Core Components

wait_queue_head_t

Represents a wait queue.

Conceptually:

Event
Wait Queue
Waiting Tasks

Simplified structure:

struct wait_queue_head {
    spinlock_t lock;
    struct list_head head;
};

Responsibilities:

  • Maintain waiting tasks
  • Protect wait queue state
  • Support wake-up operations

wait_queue_entry_t

Represents a single waiting task.

Simplified structure:

struct wait_queue_entry {
    struct task_struct *task;
    struct list_head entry;
};

Responsibilities:

  • Track a waiting task
  • Link into a wait queue

Task States

Wait queues work together with task states.

Common states:

TASK_RUNNING
TASK_INTERRUPTIBLE
TASK_UNINTERRUPTIBLE

TASK_RUNNING

The task is runnable.

The scheduler may execute it immediately or later.


TASK_INTERRUPTIBLE

The task is sleeping.

The task may be awakened by:

  • Events
  • Signals
  • Explicit wake-up operations

Sleep Flow

The general sleep sequence:

TASK_RUNNING
prepare_to_wait()
Insert into Wait Queue
TASK_INTERRUPTIBLE
schedule()
Sleep

prepare_to_wait()

Responsibilities:

Insert task into wait queue
        +
Set task state

Conceptually:

prepare_to_wait(...)

After execution:

Task
Wait Queue

and:

TASK_INTERRUPTIBLE

Wake-up Flow

The wake-up sequence:

Event Occurs
wake_up()
Wake Waiting Tasks
TASK_RUNNING
Scheduler

wake_up()

Responsibilities:

Locate waiting tasks
Mark runnable
Schedule later

Conceptually:

wake_up(...)

finish_wait()

Responsibilities:

Remove task from queue
        +
Restore task state

Conceptually:

finish_wait(...)

wait_event()

The most common wait queue API used by drivers.

Typical usage:

wait_event(
    mydev->read_wq,
    mydev->data_ready);

Conceptually:

Condition True
Continue

Condition False
Sleep

Equivalent logic:

while (!condition) {

    prepare_to_wait();

    schedule();

    finish_wait();
}

Blocking Driver Pattern

One of the most common wait queue applications.

Driver read path:

read()
wait_event()
Sleep

Interrupt path:

IRQ
data_ready = true
wake_up()

Read resumes:

read()
Consume Data

Relationship with poll()

A driver using blocking I/O often also supports poll().

Relationship:

wait queue
poll_wait()
poll()
User Event Loop

The wait queue remains the underlying notification mechanism.


Relationship with Completion

Completions are built on top of wait queues.

Conceptually:

wait_for_completion()
Wait Queue

complete()
wake_up()

Completions provide a higher-level synchronization abstraction.


Design Pattern

The wait queue pattern appears repeatedly in Linux:

Condition
        +
Wait Queue
        +
Wake Up

Examples:

  • Blocking drivers
  • Sensor drivers
  • Network receive paths
  • Completion synchronization
  • Event-driven subsystems

Summary

A wait queue is the kernel's generic sleep/wakeup infrastructure.

Core ideas:

Task
Wait Queue
Sleep

Event
wake_up()
Task Runnable

Understanding wait queues is essential before learning:

  • poll()
  • epoll()
  • Completions
  • Advanced driver synchronization