Linux Kernel Deadlock

Search for a command to run...

No comments yet. Be the first to comment.
Memory and resource allocation are fundamental operations inside the Linux kernel. Whether assigning device IDs, managing CPU masks, allocating interrupt vectors, or tracking hardware resources, the k

The Linux scheduler is one of the most important components of the operating system. Every running program, background service, and kernel thread eventually interacts with the scheduler. In this artic

Linux Kernel Mutexes Explained with a Mini Driver Concurrency is a fundamental aspect of Linux kernel programming. Device drivers often execute in multiple contexts, making synchronization essential w

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 executi

Deadlocks are one of the most common synchronization problems encountered in operating systems and concurrent programming. Although the concept is frequently introduced in textbooks, observing it inside the Linux kernel provides a much deeper understanding of why proper lock ordering is essential.
In this project, I built a Linux Kernel Loadable Module (LKM) that intentionally creates a deadlock using two kernel threads and two mutexes. The same module also contains a corrected implementation that eliminates the deadlock by enforcing a consistent lock acquisition order.
The objective of this project is educational. It demonstrates not only how deadlocks occur, but also how to debug synchronization issues using kernel logs and how to manage kernel thread lifecycles correctly.
The module supports two execution modes.
| Mode | Description |
|---|---|
mode=1 |
Intentionally creates a deadlock |
mode=2 |
Demonstrates the corrected implementation |
Both modes use the same synchronization primitives but differ in the order in which mutexes are acquired.
kthread)pr_info)dmesgThe demonstration creates two worker threads.
The first thread acquires:
LockA
↓
LockB
The second thread acquires:
LockB
↓
LockA
Once each thread acquires its first mutex, both wait forever for the second mutex.
Neither thread can proceed.
This is the classic circular waiting condition that produces a deadlock.
The worker thread acquires the first mutex, waits briefly, and then attempts to acquire the second mutex.
Example:
mutex_lock(&lockA);
msleep(2000);
mutex_lock(&lockB);
The second worker performs the opposite operation.
mutex_lock(&lockB);
msleep(2000);
mutex_lock(&lockA);
Running both threads simultaneously causes each thread to wait indefinitely.
The solution is surprisingly simple.
Both threads acquire the mutexes in exactly the same order.
mutex_lock(&lockA);
mutex_lock(&lockB);
Since every thread follows the same locking sequence, circular waiting is eliminated.
The module creates two kernel threads using kthread_run().
Example:
thread1 = kthread_run(worker1, NULL, "thread1");
Each worker performs its task once and then exits.
This project is intentionally kept small so that the synchronization behavior remains easy to observe.
The module behavior is controlled through a module parameter.
sudo insmod deadlock_demo.ko mode=1
or
sudo insmod deadlock_demo.ko mode=2
This makes it possible to compare the incorrect and corrected implementations without modifying the source code.
Compile the module using the kernel build system.
make
If Secure Boot is enabled, sign the module before loading it.
sudo /usr/src/linux-headers-$(uname -r)/scripts/sign-file \
sha256 \
~/kernel_keys/MOK.key \
~/kernel_keys/MOK.crt \
deadlock_demo.ko
Load the deadlock version.
sudo insmod deadlock_demo.ko mode=1
Monitor kernel messages.
dmesg -w
Load the corrected implementation.
sudo insmod deadlock_demo.ko mode=2
Unload the module.
sudo rmmod deadlock_demo
The Linux kernel provides detailed diagnostic information through dmesg.
Useful commands include:
dmesg | tail -100
dmesg | grep deadlock_demo
Kernel logs make it much easier to understand thread execution, lock acquisition, and module initialization.
While implementing this demonstration, I encountered two issues that were not caused by the deadlock itself.
kthread_stop()The worker threads in the corrected implementation naturally terminate after completing their work.
Attempting to stop already-finished threads during module removal resulted in kernel warnings and crashes.
The fix was to avoid calling kthread_stop() for one-shot worker threads.
IS_ERR() ValidationInitially, the return value of kthread_run() was not validated.
Proper kernel programming requires checking whether thread creation failed.
Example:
if (IS_ERR(thread1))
Adding this validation makes the module more robust and avoids invalid pointer usage.
A detailed analysis of both debugging sessions will be presented in a future article.
By studying this project,I have gained hands-on experience with:
This project is the first step in a larger Linux kernel synchronization series.
Upcoming articles will provide a deeper discussion of:
kthread_run() internalskthread_stop() best practicesIS_ERR(), ERR_PTR(), and PTR_ERR()Deadlocks are easier to understand when they can be reproduced in a controlled environment.
This Linux Kernel Module demonstrates both an incorrect synchronization strategy and its corrected implementation using only two kernel threads and two mutexes. Although the project is intentionally small, it illustrates several important concepts that frequently appear in operating system design and Linux kernel development.
Understanding synchronization primitives at this level provides a solid foundation for exploring more advanced topics such as semaphores, read-write locks, completions, wait queues, workqueues, RCU, and lock-free programming.
Source code for this project is available at: