Completions

Completions provide a simple mechanism for one task to signal another that some event has occurred. They’re commonly used to synchronize between a thread and its completion handler, or between setup and actual operation.

Basic Completion Usage

#include <linux/completion.h>

/* Static initialization */
static DECLARE_COMPLETION(my_completion);

/* Or dynamic initialization */
struct completion my_completion;
init_completion(&my_completion);

/* Waiting thread */
wait_for_completion(&my_completion);
/* Continues after completion signaled */

/* Signaling thread */
complete(&my_completion);  /* Wake one waiter */
/* or */
complete_all(&my_completion);  /* Wake all waiters */

Completion Flow

sequenceDiagram
    participant Main as Main Thread
    participant Worker as Worker Thread

    Main->>Main: Start worker
    Main->>Main: wait_for_completion()
    Note over Main: Sleeping...

    Worker->>Worker: Do work...
    Worker->>Worker: complete()

    Main->>Main: Wakes up!
    Main->>Main: Continue processing

Completion Operations

Basic Wait

/* Wait forever */
wait_for_completion(&comp);

/* Wait interruptible (can be interrupted by signals) */
if (wait_for_completion_interruptible(&comp)) {
    /* Was interrupted */
    return -ERESTARTSYS;
}

/* Wait killable (can be killed by fatal signals) */
if (wait_for_completion_killable(&comp)) {
    return -ERESTARTSYS;
}

Wait with Timeout

unsigned long timeout = msecs_to_jiffies(5000);  /* 5 seconds */

/* Returns 0 on timeout, remaining jiffies otherwise */
if (!wait_for_completion_timeout(&comp, timeout)) {
    pr_err("Operation timed out\n");
    return -ETIMEDOUT;
}

/* Interruptible with timeout */
long ret = wait_for_completion_interruptible_timeout(&comp, timeout);
if (ret == 0) {
    return -ETIMEDOUT;
} else if (ret < 0) {
    return -ERESTARTSYS;
}
/* ret > 0: completed successfully */

Try Wait (Non-blocking)

if (try_wait_for_completion(&comp)) {
    /* Completion was already signaled */
} else {
    /* Not yet complete */
}

Signal Completion

/* Wake one waiter */
complete(&comp);

/* Wake all waiters */
complete_all(&comp);

Reinitialize

/* Reset completion for reuse */
reinit_completion(&comp);

Example: Synchronizing with Interrupt

#include <linux/module.h>
#include <linux/completion.h>
#include <linux/interrupt.h>

struct my_device {
    struct completion data_ready;
    int data;
    int irq;
};

static struct my_device dev;

/* Called from process context */
static ssize_t my_read(struct file *file, char __user *buf,
                       size_t count, loff_t *ppos)
{
    int ret;

    /* Start hardware operation */
    start_hardware_read(&dev);

    /* Wait for interrupt to signal completion */
    ret = wait_for_completion_interruptible_timeout(
        &dev.data_ready, msecs_to_jiffies(1000));

    if (ret == 0)
        return -ETIMEDOUT;
    if (ret < 0)
        return -ERESTARTSYS;

    /* Data is ready */
    if (copy_to_user(buf, &dev.data, sizeof(dev.data)))
        return -EFAULT;

    return sizeof(dev.data);
}

/* Interrupt handler */
static irqreturn_t my_irq_handler(int irq, void *data)
{
    struct my_device *d = data;

    /* Read data from hardware */
    d->data = read_hardware_data();

    /* Signal that data is ready */
    complete(&d->data_ready);

    return IRQ_HANDLED;
}

static int __init my_init(void)
{
    init_completion(&dev.data_ready);
    /* ... register IRQ ... */
    return 0;
}

Example: Thread Startup Synchronization

#include <linux/kthread.h>
#include <linux/completion.h>

struct worker {
    struct completion started;
    struct completion stopped;
    struct task_struct *thread;
    bool should_stop;
};

static struct worker worker;

static int worker_thread(void *data)
{
    struct worker *w = data;

    /* Signal that we've started */
    complete(&w->started);

    while (!w->should_stop) {
        /* Do work */
        schedule_timeout_interruptible(HZ);
    }

    /* Signal that we're stopping */
    complete(&w->stopped);
    return 0;
}

static int start_worker(void)
{
    init_completion(&worker.started);
    init_completion(&worker.stopped);
    worker.should_stop = false;

    worker.thread = kthread_create(worker_thread, &worker, "my_worker");
    if (IS_ERR(worker.thread))
        return PTR_ERR(worker.thread);

    wake_up_process(worker.thread);

    /* Wait for thread to actually start */
    wait_for_completion(&worker.started);

    pr_info("Worker thread is running\n");
    return 0;
}

static void stop_worker(void)
{
    worker.should_stop = true;
    wake_up_process(worker.thread);

    /* Wait for thread to actually stop */
    wait_for_completion(&worker.stopped);

    pr_info("Worker thread has stopped\n");
}

Example: Firmware Loading

struct fw_load_context {
    struct completion done;
    const struct firmware *fw;
    int error;
};

static void fw_callback(const struct firmware *fw, void *context)
{
    struct fw_load_context *ctx = context;

    ctx->fw = fw;
    ctx->error = fw ? 0 : -ENOENT;

    complete(&ctx->done);
}

int load_firmware_sync(struct device *dev, const char *name,
                       const struct firmware **fw)
{
    struct fw_load_context ctx;
    int ret;

    init_completion(&ctx.done);
    ctx.fw = NULL;
    ctx.error = 0;

    /* Request firmware asynchronously */
    ret = request_firmware_nowait(THIS_MODULE, true, name, dev,
                                  GFP_KERNEL, &ctx, fw_callback);
    if (ret)
        return ret;

    /* Wait for callback */
    wait_for_completion(&ctx.done);

    *fw = ctx.fw;
    return ctx.error;
}

Multiple Waiters

DECLARE_COMPLETION(event);

/* Multiple threads can wait */
void waiter_thread1(void) {
    wait_for_completion(&event);
    pr_info("Waiter 1 woken\n");
}

void waiter_thread2(void) {
    wait_for_completion(&event);
    pr_info("Waiter 2 woken\n");
}

/* Wake all */
void trigger_event(void) {
    complete_all(&event);  /* Both waiters wake */
}

/* Or wake one at a time */
void trigger_one(void) {
    complete(&event);  /* Only one waiter wakes */
}

Completion vs Other Primitives

Primitive Use Case
Completion One-shot signaling, thread sync
Mutex Protect shared data
Semaphore Resource counting
Wait queue Complex wait conditions

Checking Completion State

/* Check if completion was signaled (without waiting) */
if (completion_done(&comp)) {
    pr_info("Already completed\n");
}

Common Patterns

Request-Response

struct request {
    struct completion done;
    int result;
    void *data;
};

void submit_request(struct request *req)
{
    init_completion(&req->done);
    queue_request(req);  /* Submit to hardware/thread */
}

int wait_request(struct request *req, unsigned long timeout)
{
    if (!wait_for_completion_timeout(&req->done, timeout))
        return -ETIMEDOUT;
    return req->result;
}

/* In handler */
void complete_request(struct request *req, int result)
{
    req->result = result;
    complete(&req->done);
}

Initialization Barrier

static DECLARE_COMPLETION(subsystem_ready);

void __init subsystem_init(void)
{
    /* ... initialize ... */
    complete_all(&subsystem_ready);
}

void user_function(void)
{
    /* Wait for subsystem to be ready */
    wait_for_completion(&subsystem_ready);
    /* Now safe to use subsystem */
}

Summary

  • Completions provide simple thread signaling
  • Use for one-shot events and synchronization
  • complete() wakes one waiter, complete_all() wakes all
  • Always use timeout variants in production code
  • Use reinit_completion() to reset for reuse
  • Great for IRQ-to-process and thread synchronization

Next

Learn about work queues for deferred processing.


Back to top

Linux Driver Development Guide is a community resource for learning kernel driver development. Not affiliated with the Linux Foundation. Content provided for educational purposes.

This site uses Just the Docs, a documentation theme for Jekyll.