# Linux Kernel Mutexes: Safe Synchronization with Kernel Threads

Modern Linux kernel development is fundamentally about **correctness under concurrency**. Whether we're writing a character driver, platform driver, network subsystem, or filesystem code, multiple execution contexts often access the same shared data simultaneously. Without proper synchronization, race conditions can corrupt kernel state and produce unpredictable behavior.

One of the most widely used synchronization primitives in Linux is the **mutex**. Unlike spinlocks, mutexes are **sleeping locks**, making them ideal for protecting shared resources in process context where blocking is allowed.

In this article, I'll build a Linux kernel module that demonstrates mutex synchronization using two competing kernel threads while exploring the design principles behind mutexes.

* * *

# Why Do We Need Mutexes?

Consider two kernel threads incrementing the same shared counter.

Without synchronization, both threads may read the same value before either writes it back, causing one update to be lost.

This classic race condition leads to inconsistent program behavior.

A mutex ensures that **only one thread can enter the critical section at a time**, preserving data integrity.

* * *

# Project Overview

The module demonstrates:

*   Linux kernel threads (`kthread_run()`)
    
*   Shared resource protection
    
*   Dynamic memory allocation
    
*   Static mutex initialization
    
*   Dynamic mutex initialization
    
*   Critical sections
    
*   Safe synchronization
    
*   Module initialization and cleanup
    

The shared driver context contains both the protected data and the mutex.

```c
struct demo_context {
    int shared_counter;
    struct mutex lock;
};
```

The counter is accessed by two independent kernel threads, both competing for the same mutex.

* * *

# Static vs Dynamic Mutex Initialization

Linux provides two ways to initialize a mutex.

## Static Initialization

Static mutexes are initialized automatically before the module starts executing.

```c
static DEFINE_MUTEX(global_mutex);
```

This approach is ideal for global or module-wide locks whose lifetime matches the module.

### Advantages

*   No initialization call required
    
*   Simple and efficient
    
*   Lifetime equals module lifetime
    

* * *

## Dynamic Initialization

When mutexes are embedded inside dynamically allocated objects, they must be initialized explicitly.

```c
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);

mutex_init(&ctx->lock);
```

This is the preferred approach for driver-specific objects allocated at runtime.

### Advantages

*   Works with dynamically allocated structures
    
*   Common in device drivers
    
*   Flexible for multiple device instances
    

* * *

# Creating Kernel Threads

The module launches two worker threads.

```c
thread1 = kthread_run(worker, (void *)1, "mutex_thread1");

thread2 = kthread_run(worker, (void *)2, "mutex_thread2");
```

Both execute the same worker function and continuously compete for the mutex.

* * *

# Entering the Critical Section

The critical section begins when the mutex is acquired.

```c
mutex_lock(&ctx->lock);

ctx->shared_counter++;

mutex_unlock(&ctx->lock);
```

If another thread already owns the mutex, the current thread is automatically put to sleep until the lock becomes available.

Unlike busy waiting, sleeping conserves CPU resources and improves overall system efficiency.

* * *

# Sleeping Inside a Mutex

One interesting aspect of this example is that the worker intentionally sleeps while holding the mutex.

```c
msleep(1000);
```

This demonstrates an important property of Linux mutexes:

*   Sleeping **is allowed** while holding a mutex.
    
*   Sleeping **is forbidden** while holding a spinlock.
    

Although sleeping inside a mutex is legal, real production code should keep critical sections as short as possible to minimize lock contention.

* * *

# Understanding the Worker Thread

Each worker repeatedly performs the following sequence:

1.  Acquire the mutex.
    
2.  Increment the shared counter.
    
3.  Print the updated value.
    
4.  Sleep briefly.
    
5.  Release the mutex.
    
6.  Sleep outside the critical section.
    

This repeated competition clearly demonstrates serialized access to shared data.

* * *

# Expected Kernel Output

A typical execution looks like this:

```text
===== Mutex Demo Loaded =====

Static mutex locked once

Thread 1 acquired mutex
Thread 1 Counter=1
Thread 1 releasing mutex

Thread 2 acquired mutex
Thread 2 Counter=2
Thread 2 releasing mutex
```

Notice that the counter always increases sequentially.

There is never simultaneous access to the protected resource.

* * *

# Important Mutex APIs

| API | Purpose |
| --- | --- |
| `DEFINE_MUTEX()` | Static initialization |
| `mutex_init()` | Dynamic initialization |
| `mutex_lock()` | Acquire mutex |
| `mutex_unlock()` | Release mutex |
| `kthread_run()` | Create kernel thread |
| `kthread_stop()` | Stop kernel thread |
| `msleep()` | Sleep in process context |

* * *

# Essential Mutex Rules

## 1\. Only One Owner

Only one task may own a mutex at any moment.

Any additional thread attempting to acquire it will sleep until it becomes available.

* * *

## 2\. Owner Must Unlock

The thread that acquires the mutex must also release it.

Unlocking a mutex from another thread leads to undefined behavior.

* * *

## 3\. Recursive Locking Is Not Allowed

Attempting to acquire the same mutex twice from the same thread causes a deadlock.

Avoid recursive locking unless a different synchronization primitive is specifically designed for it.

* * *

## 4\. Mutexes May Sleep

Because `mutex_lock()` may block, mutexes are valid only in **process context**.

They are commonly used inside:

*   Kernel threads
    
*   System calls
    
*   Character device operations
    
*   Driver read/write methods
    

* * *

## 5\. Never Use Mutexes in Atomic Context

Mutexes should never be used inside:

*   Interrupt handlers
    
*   SoftIRQs
    
*   Tasklets
    
*   Timers
    
*   Other atomic contexts
    

Blocking is not permitted in these execution contexts.

* * *

## 6\. Keep Critical Sections Small

The protected region should contain only the minimum required operations.

Smaller critical sections improve scalability, reduce contention, and increase overall system responsiveness.

* * *

## 7\. Follow Consistent Lock Ordering

When multiple locks are required, always acquire them in the same order throughout the codebase.

Consistent ordering is one of the simplest and most effective techniques for preventing deadlocks.

* * *

# Module Lifecycle

## Module Initialization

During module loading, the following sequence occurs:

*   Allocate driver context
    
*   Initialize dynamic mutex
    
*   Demonstrate static mutex
    
*   Launch two kernel threads
    

* * *

## Module Cleanup

During module removal:

*   Stop both kernel threads
    
*   Free allocated memory
    
*   Exit cleanly
    

Proper cleanup ensures that no kernel resources are leaked.

* * *

# Where Are Mutexes Used?

Mutexes appear throughout the Linux kernel and are commonly used in:

*   Character device drivers
    
*   Platform drivers
    
*   USB drivers
    
*   PCI drivers
    
*   I2C drivers
    
*   SPI drivers
    
*   Filesystems
    
*   Network drivers
    
*   Virtual device drivers
    
*   Embedded Linux systems
    

Typical protected resources include:

*   Device state
    
*   Hardware registers
    
*   Linked lists
    
*   Queues
    
*   Buffers
    
*   Configuration structures
    
*   Statistics
    
*   Driver context objects
    

* * *

# Final Thoughts

Mutexes are among the most important synchronization primitives in Linux kernel development. Understanding when to use them—and equally important, when **not** to use them—is fundamental for writing reliable kernel modules and production-quality device drivers.

This demonstration illustrates the complete lifecycle of mutex usage, from initialization and lock acquisition to protecting shared resources and performing clean module shutdown. Once these fundamentals are mastered, I can confidently move on to advanced synchronization mechanisms such as semaphores, completions, wait queues, spinlocks, reader-writer locks, RCU, and lock-free programming techniques.

* * *

GitHub Repository: 👉 **[linux_kernel_mutex3](https://github.com/aj333git/linux_kernel_mutex3)** Explore the complete source code, build files, and module implementation on GitHub.
