In this free Linux kernel development course lecture, you will learn the linux kernel mutex lock unlock APIs in depth — how mutex_lock() and mutex_unlock() actually work, why the order in which you take multiple locks must be documented and never violated, and how to choose between an interruptible and an uninterruptible sleep when a task blocks on a mutex. This lecture is part of our free embedded systems course and free linux device drivers course series, and every example below targets a modern kernel 6.x build system, not a legacy one.
Before this lecture, you should already be comfortable with basic kernel module structure (module_init/module_exit), the difference between process context and atomic context, and the conceptual difference between a mutex and a spinlock (sleep vs spin), which we covered in the earlier lecture of this free linux kernel development course.
Why Lock Ordering Prevents Deadlocks
When a driver uses more than one mutex, the biggest real-world danger is not forgetting to lock — it is locking two mutexes in an inconsistent order from different code paths. If one thread takes Mutex A then Mutex B, while another thread takes Mutex B then Mutex A, both threads can end up waiting on each other forever. This is a classic AB-BA deadlock.
The actual order you pick does not matter much. What matters is that once you decide on an order, every single code path in your driver — the read path, the write path, the cleanup path, an interrupt-deferred workqueue — follows that exact same order without exception. Kernel developers document this convention directly above the lock declarations so future maintainers do not accidentally break it.
mutex_lock() and mutex_unlock() APIs on Kernel 6.x
The core mutex API surface is deliberately small:
void mutex_lock(struct mutex *lock);
void mutex_unlock(struct mutex *lock);
mutex_lock() acquires the mutex exclusively for the calling task. If it is already held, the calling task is put to sleep until the mutex becomes available. A mutex must always be released by the same task that acquired it — unlike a semaphore, a mutex has an owner. Recursive locking by the same task is not allowed, and the kernel memory backing the mutex must never be freed (or memset() to zero) while it is still locked.
Because mutex_lock() can put the calling task to sleep, it must only ever be called from process context, where sleeping is legal. Calling it from an atomic context — inside a spinlock critical section, inside hardware interrupt context, or with preemption disabled — is a serious bug, and the kernel’s own debug instrumentation is built specifically to catch this class of mistake at runtime when CONFIG_DEBUG_MUTEXES is enabled.
Interruptible vs Uninterruptible Sleep: Choosing the Right API
On Linux, a sleeping task can be in one of two states: an interruptible sleep, where the task will wake up early if a user-space signal arrives, or an uninterruptible sleep, where signals are ignored until the task is woken by whatever it was waiting for.
mutex_lock() always places the calling task into an uninterruptible sleep. If you want the task to respond to a signal (for example, so a user can press Ctrl+C to abort an application blocked on your driver), use the interruptible variant instead:
int mutex_lock_interruptible(struct mutex *lock);
This function returns 0 on success and -EINTR if the wait was aborted by a signal — so your code must always check the return value and unwind cleanly on failure, unlike plain mutex_lock() which never fails.
| Aspect | mutex_lock() | mutex_lock_interruptible() |
|---|---|---|
| Sleep type | Uninterruptible | Interruptible |
| Return value | void (never fails) | 0 on success, -EINTR on signal |
| Best for | Short, non-interactive critical sections | User-facing paths where Ctrl+C should work |
| Relative cost | Slightly faster | Slightly more overhead (signal check) |
mutex_destroy() and the Full Mutex Lifecycle
A mutex object also has a formal end of life. mutex_destroy() marks a mutex as unusable and must only be called once the mutex is unlocked. On a production kernel without debug options it reduces to nothing, but with CONFIG_DEBUG_MUTEXES enabled it performs real sanity checks — so it is good practice to always call it during cleanup, even though it costs nothing when debugging is off.
Put together, every correctly written driver follows this pattern:
DEFINE_MUTEX(my_lock); /* or mutex_init(&my_lock); */
/* critical section, as many times as needed */
mutex_lock(&my_lock); /* or mutex_lock_interruptible() */
/* ... protected code ... */
mutex_unlock(&my_lock);
/* at module/driver exit, once fully unlocked */
mutex_destroy(&my_lock);
Original Kernel 6.x Example: Two Mutexes With Documented Ordering
Below is an original example driver written for this lecture. It protects two independent shared resources with two separate mutexes, documents the required lock order in a comment (following the same convention used throughout the kernel source), and demonstrates both mutex APIs in the same driver: an interruptible lock in the user-facing open() path, and a plain uninterruptible lock in a short background kernel thread.
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/delay.h>
/*
* Lock order (must be followed everywhere in this driver):
* 1. dev_mutex (protects device open/close state)
* 2. stats_mutex (protects the usage-counter structure)
*/
static DEFINE_MUTEX(dev_mutex);
static DEFINE_MUTEX(stats_mutex);
static int open_count;
static unsigned long total_opens;
static struct task_struct *stats_thread;
static int ep_mutex_demo_open(struct inode *inode, struct file *filp)
{
int ret;
/* user-facing path: allow Ctrl+C to abort the wait */
ret = mutex_lock_interruptible(&dev_mutex);
if (ret)
return ret;
open_count++;
mutex_lock(&stats_mutex); /* dev_mutex already held: correct order */
total_opens++;
mutex_unlock(&stats_mutex);
mutex_unlock(&dev_mutex);
return 0;
}
static int ep_mutex_demo_release(struct inode *inode, struct file *filp)
{
mutex_lock(&dev_mutex);
open_count--;
mutex_unlock(&dev_mutex);
return 0;
}
static const struct file_operations ep_mutex_demo_fops = {
.owner = THIS_MODULE,
.open = ep_mutex_demo_open,
.release = ep_mutex_demo_release,
};
static struct miscdevice ep_mutex_demo_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "ep_mutex_demo",
.fops = &ep_mutex_demo_fops,
};
/* short background critical section: plain mutex_lock() is fine here */
static int ep_stats_thread_fn(void *data)
{
while (!kthread_should_stop()) {
mutex_lock(&stats_mutex);
pr_info("ep_mutex_demo: total_opens=%lu\n", total_opens);
mutex_unlock(&stats_mutex);
ssleep(5);
}
return 0;
}
static int __init ep_mutex_demo_init(void)
{
int ret = misc_register(&ep_mutex_demo_dev);
if (ret)
return ret;
stats_thread = kthread_run(ep_stats_thread_fn, NULL, "ep_stats_thread");
if (IS_ERR(stats_thread)) {
misc_deregister(&ep_mutex_demo_dev);
return PTR_ERR(stats_thread);
}
return 0;
}
static void __exit ep_mutex_demo_exit(void)
{
kthread_stop(stats_thread);
misc_deregister(&ep_mutex_demo_dev);
mutex_destroy(&dev_mutex);
mutex_destroy(&stats_mutex);
}
module_init(ep_mutex_demo_init);
module_exit(ep_mutex_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala: mutex lock ordering + lock/unlock API demo (kernel 6.x)");
Notice that ep_mutex_demo_open() always takes dev_mutex before stats_mutex, exactly as the top comment documents, and the background thread only ever touches stats_mutex by itself — so the ordering rule can never be violated by any path in this driver. Both mutexes are properly torn down with mutex_destroy() in the exit function, after they have already been unlocked.
Frequently Asked Questions
Q1. Does the specific lock order I choose matter, as long as I have one?
No. The kernel does not care whether you lock A-then-B or B-then-A. What matters is that the chosen order is documented and followed identically by every code path in the driver, with no exceptions.
Q2. When should I use mutex_lock_interruptible() instead of mutex_lock()?
Use the interruptible variant on paths triggered directly by a user-space process, so the user can abort with a signal like Ctrl+C. Use plain mutex_lock() for short, non-interactive kernel-internal critical sections where waiting indefinitely is acceptable.
Q3. What happens if I call mutex_lock() from interrupt context?
It is a bug. mutex_lock() can sleep, and sleeping is illegal in atomic/interrupt context. The kernel’s might_sleep() debug check, active under CONFIG_DEBUG_MUTEXES-style configs, is designed to catch exactly this mistake.
Q4. Can two different tasks unlock a mutex that a third task locked?
No. A mutex has a single owning task, and only that same task may unlock it. This is a fundamental difference from a counting semaphore.
Q5. Is mutex_destroy() mandatory?
It is good practice to always call it once a mutex is permanently unlocked and no longer needed. On non-debug kernels it does very little, but on debug-enabled kernels it performs real validation, and calling it costs nothing either way.
Q6. Is mutex_lock_interruptible() slower than mutex_lock()?
It carries a small amount of extra overhead because it must check for pending signals, so plain mutex_lock() is preferred for very short critical sections where interactivity does not matter.
Continue the Free Linux Kernel Development Course
More lectures on kernel synchronization, device drivers, and kernel 6.x internals are coming next in this free embedded systems course.

2 Comments