Mutexes

Mutexes (mutual exclusion locks) are sleeping locks designed for longer critical sections in process context. Unlike spinlocks, a task waiting for a mutex will sleep instead of busy-waiting.

Basic Mutex Usage

#include <linux/mutex.h>

/* Static initialization */
static DEFINE_MUTEX(my_mutex);

/* Or dynamic initialization */
struct mutex my_mutex;
mutex_init(&my_mutex);

/* Using the mutex */
mutex_lock(&my_mutex);
/* ... critical section (can sleep here) ... */
mutex_unlock(&my_mutex);

Mutex vs Spinlock

Aspect Mutex Spinlock
Waiting Sleeps Busy-waits
Context Process only Any
Can sleep while holding Yes No
Overhead Higher (scheduling) Lower
Best for Longer critical sections Short critical sections
flowchart TD
    Need["Need a lock"]
    CanSleep{"Can you sleep<br/>while waiting?"}
    Short{"Short critical<br/>section?"}
    InIRQ{"In interrupt<br/>context?"}

    Need --> CanSleep
    CanSleep -->|No| Spin["Use Spinlock"]
    CanSleep -->|Yes| Short
    Short -->|Yes| Maybe["Either works"]
    Short -->|No| Mutex["Use Mutex"]
    Need --> InIRQ
    InIRQ -->|Yes| Spin

    style Mutex fill:#738f99,stroke:#0277bd
    style Spin fill:#8f8a73,stroke:#f9a825

Mutex Operations

Basic Lock/Unlock

mutex_lock(&mutex);       /* Sleep if not available */
/* Critical section */
mutex_unlock(&mutex);

Try Lock (Non-blocking)

if (mutex_trylock(&mutex)) {
    /* Got the lock */
    mutex_unlock(&mutex);
} else {
    /* Lock was held - do something else */
}

Interruptible Lock

if (mutex_lock_interruptible(&mutex)) {
    /* Was interrupted by a signal */
    return -ERESTARTSYS;
}
/* Got the lock */
mutex_unlock(&mutex);

Killable Lock

if (mutex_lock_killable(&mutex)) {
    /* Was killed by a fatal signal */
    return -ERESTARTSYS;
}
/* Got the lock */
mutex_unlock(&mutex);

Complete Example

#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

struct my_device {
    struct mutex lock;
    char buffer[4096];
    size_t size;
};

static struct my_device dev;

static ssize_t my_read(struct file *file, char __user *buf,
                       size_t count, loff_t *ppos)
{
    ssize_t ret;

    /* Use interruptible to allow Ctrl-C */
    if (mutex_lock_interruptible(&dev.lock))
        return -ERESTARTSYS;

    if (*ppos >= dev.size) {
        ret = 0;
        goto out;
    }

    if (*ppos + count > dev.size)
        count = dev.size - *ppos;

    /* copy_to_user can sleep - that's OK with mutex! */
    if (copy_to_user(buf, dev.buffer + *ppos, count)) {
        ret = -EFAULT;
        goto out;
    }

    *ppos += count;
    ret = count;

out:
    mutex_unlock(&dev.lock);
    return ret;
}

static ssize_t my_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
{
    ssize_t ret;

    if (mutex_lock_interruptible(&dev.lock))
        return -ERESTARTSYS;

    if (*ppos + count > sizeof(dev.buffer))
        count = sizeof(dev.buffer) - *ppos;

    /* copy_from_user can sleep - OK! */
    if (copy_from_user(dev.buffer + *ppos, buf, count)) {
        ret = -EFAULT;
        goto out;
    }

    *ppos += count;
    if (*ppos > dev.size)
        dev.size = *ppos;
    ret = count;

out:
    mutex_unlock(&dev.lock);
    return ret;
}

static int __init my_init(void)
{
    mutex_init(&dev.lock);
    return 0;
}

Mutex Rules

Rule 1: Only the Owner Can Unlock

/* WRONG - different task unlocking */
void task_a(void)
{
    mutex_lock(&mutex);
    /* ... */
}

void task_b(void)
{
    mutex_unlock(&mutex);  /* Wrong! A locked it */
}

Rule 2: No Recursive Locking

/* WRONG - will deadlock! */
void my_function(void)
{
    mutex_lock(&mutex);
    other_function();  /* If this also locks mutex... deadlock! */
    mutex_unlock(&mutex);
}

void other_function(void)
{
    mutex_lock(&mutex);  /* Deadlock! */
    /* ... */
    mutex_unlock(&mutex);
}

Rule 3: Cannot Use in Interrupt Context

/* WRONG! */
irqreturn_t my_irq_handler(int irq, void *data)
{
    mutex_lock(&mutex);  /* Will sleep! Crash! */
    /* ... */
    mutex_unlock(&mutex);
    return IRQ_HANDLED;
}

Checking Mutex State

/* Check if mutex is locked */
if (mutex_is_locked(&mutex))
    pr_info("Mutex is currently held\n");

/* Check if current task owns the mutex (debug builds) */
#ifdef CONFIG_DEBUG_MUTEXES
if (lockdep_is_held(&mutex))
    pr_info("We are holding the mutex\n");
#endif

Mutex in Device Structures

Common pattern for per-device locking:

struct my_device {
    struct mutex lock;
    /* ... other fields ... */
};

static int my_open(struct inode *inode, struct file *file)
{
    struct my_device *dev;

    dev = container_of(inode->i_cdev, struct my_device, cdev);
    file->private_data = dev;

    return 0;
}

static ssize_t my_read(struct file *file, char __user *buf,
                       size_t count, loff_t *ppos)
{
    struct my_device *dev = file->private_data;

    mutex_lock(&dev->lock);  /* Lock this specific device */
    /* ... */
    mutex_unlock(&dev->lock);

    return count;
}

Nested Locking

Sometimes you need multiple mutexes. Define clear ordering:

/*
 * Lock ordering:
 * 1. global_lock
 * 2. dev->lock (for any device)
 */

void my_function(struct my_device *dev)
{
    mutex_lock(&global_lock);
    mutex_lock(&dev->lock);
    /* ... */
    mutex_unlock(&dev->lock);
    mutex_unlock(&global_lock);
}

Use lockdep annotations for complex cases:

static struct lock_class_key my_lock_key;

void my_init(struct my_device *dev)
{
    mutex_init(&dev->lock);
    lockdep_set_class(&dev->lock, &my_lock_key);
}

Mutex Alternatives

struct rw_semaphore

For read-heavy access patterns:

#include <linux/rwsem.h>

static DECLARE_RWSEM(my_rwsem);

/* Multiple readers */
down_read(&my_rwsem);
/* ... read data ... */
up_read(&my_rwsem);

/* Exclusive writer */
down_write(&my_rwsem);
/* ... modify data ... */
up_write(&my_rwsem);

struct ww_mutex

For wound-wait deadlock avoidance (used in graphics drivers):

#include <linux/ww_mutex.h>

/* Complex usage - see documentation for details */

Debugging

Enable CONFIG_DEBUG_MUTEXES for:

  • Deadlock detection
  • Owner tracking
  • Use-after-free detection
# Check kernel config
grep DEBUG_MUTEXES /boot/config-$(uname -r)

Summary

  • Mutexes are sleeping locks for process context
  • Use when critical sections may be long or involve sleeping
  • Cannot be used in interrupt context
  • Only the owner can unlock
  • Not recursive - don’t try to lock twice
  • Use mutex_lock_interruptible() to allow signal interruption

Next

Learn about semaphores for resource counting.


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.