What are Linux Kernel Locking Basics: Spinlocks and Mutexes Explained Simply-Free Linux Device Drivers Training

<

Linux Kernel Locking Basics: Spinlocks and Mutexes Explained Simply
Part of the Free Linux Kernel Development Course on EmbeddedPathashala

Linux kernel locking is the next essential skill after atomic operations, and it is a core topic in any free Linux kernel development course, free Linux device drivers course, or free embedded systems course. While atomic operations protect a single variable, real driver code often needs to update several related fields together, safely, across CPUs and interrupt handlers. That’s exactly what locks are for. This tutorial builds the concept of locking from first principles, using clear diagrams instead of dense theory.

Linux Kernel Locking Spinlock Mutex Critical Section Free Kernel Programming Course
What You Will Learn
What a lock actually does, conceptually Difference between spinlocks and mutexes When to use each lock type in a driver A working spinlock code example Common locking mistakes and deadlocks Performance and security implications

Prerequisites

  • Basic C programming and pointer handling
  • Familiarity with kernel modules (build, insert, remove)
  • Ideally, you’ve read the previous lecture on atomic operations and critical sections in this free Linux kernel development course

What Does Linux Kernel Locking Actually Do?

A lock guarantees that exactly one thread of execution can be “inside” a critical section at any given moment. Every other thread that wants to enter must wait until the current owner releases the lock. This turns a section of code that would otherwise run concurrently on multiple CPUs into something that behaves as if it runs one-at-a-time — serialized.

How a lock serializes a critical section
CPU 0: lock()
→
CPU 0 owns the lock, runs critical section
→
CPU 0: unlock()
CPU 1: lock() — must wait here until CPU 0 unlocks

Once CPU 0 calls unlock(), CPU 1 (or whichever CPU was waiting) becomes the new owner and proceeds. This waiting behavior is the heart of every kernel lock — the difference between lock types is mainly about how a CPU waits.

Spinlocks vs Mutexes in Linux Kernel Locking

The Linux kernel offers several lock types, but the two you will use constantly as a beginner are the spinlock and the mutex.

AspectSpinlockMutex
How it waitsBusy-waits (“spins”) in a tight loopPuts the waiting task to sleep
Safe in interrupt context?YesNo — never sleep in interrupt context
Typical critical section lengthVery shortCan be longer
CPU usage while waitingHigh (actively burns cycles)Low (CPU is freed for other tasks)
Common use caseInterrupt handlers, very short updatesProcess-context code, longer operations, code that may sleep
Rule of thumb: If your critical section might sleep (for example, it allocates memory with GFP_KERNEL, or calls a function that can block), you must use a mutex, not a spinlock. Sleeping while holding a spinlock can hang or crash the system.

A Practical Spinlock Example

Here’s how a driver protects a shared structure using a spinlock:

#include <linux/spinlock.h>

struct mydev_data {
    int   pending_requests;
    u32   last_status;
};

static struct mydev_data dev_data;
static DEFINE_SPINLOCK(dev_data_lock);

static void mydev_update_status(u32 status)
{
    unsigned long flags;

    spin_lock_irqsave(&dev_data_lock, flags);

    dev_data.pending_requests--;
    dev_data.last_status = status;

    spin_unlock_irqrestore(&dev_data_lock, flags);
}

spin_lock_irqsave() both takes the lock and disables local interrupts, saving their previous state into flags. This is essential when the same data can also be touched from an interrupt handler — without disabling interrupts, the CPU holding the lock could be interrupted and deadlock against itself.

A Practical Mutex Example

For process-context code that may allocate memory or otherwise sleep, prefer a mutex:

#include <linux/mutex.h>

static DEFINE_MUTEX(config_lock);
static char *config_buffer;

static int mydev_update_config(const char *new_config, size_t len)
{
    char *tmp = kstrdup(new_config, GFP_KERNEL);
    if (!tmp)
        return -ENOMEM;

    mutex_lock(&config_lock);

    kfree(config_buffer);
    config_buffer = tmp;

    mutex_unlock(&config_lock);
    return 0;
}
Tip: Notice the memory allocation happens before taking the lock. Keeping the locked section as short as possible is one of the most important habits in Linux kernel locking.

Checking Lock Contention on a Running System

The kernel provides lock-debugging and profiling facilities you can enable while learning. For example, enabling lock statistics and viewing them:

# Enable lock stats (kernel must be built with CONFIG_LOCK_STAT)
echo 1 > /proc/sys/kernel/lock_stat

# View collected lock statistics
cat /proc/lock_stat

This is a great way to see, on a real system, which locks in the kernel are most contended — a useful habit to build early in a free Linux kernel development course.

Real-World Use Cases for Locking

  • Character and block device drivers protecting shared buffers between read/write calls from multiple processes
  • Network drivers protecting transmit/receive ring buffers shared between process context and interrupt handlers
  • Filesystem code protecting inode and metadata updates
  • Power management code protecting device state transitions

Common Locking Mistakes (and Deadlocks)

MistakeConsequenceFix
Using a spinlock around code that sleepsSystem hang or “scheduling while atomic” bugUse a mutex for code paths that can sleep
Taking the same lock twice on the same CPUSelf-deadlockRestructure code so nested calls don’t re-acquire the same lock
Acquiring locks A then B in one path, and B then A in anotherClassic AB-BA deadlock between two CPUsAlways acquire locks in the same global order
Forgetting _irqsave/_irqrestore when data is also touched by an interrupt handlerDeadlock if an interrupt fires on the same CPU while the lock is heldUse the IRQ-safe spinlock variants whenever interrupt context is involved

Best Practices for Linux Kernel Locking

  • Keep critical sections as short as possible — do allocation and heavy work outside the lock
  • Always acquire multiple locks in a consistent, documented order
  • Prefer the narrowest lock type that fits the job (spinlock for short, non-sleeping sections; mutex otherwise)
  • Use lock validators such as lockdep during development to catch ordering bugs early

Performance Considerations

Every lock introduces some overhead and potential contention. Spinlocks are cheap when held briefly but waste CPU cycles under contention, since waiting CPUs spin instead of doing other work. Mutexes avoid wasting CPU while waiting but carry the extra cost of a context switch when a task has to sleep and be woken up. Choosing the right lock type — and keeping critical sections short — has a direct, measurable impact on driver throughput and system responsiveness.

Security Considerations

Incorrect locking is a frequent root cause of kernel security vulnerabilities, particularly race conditions that lead to use-after-free or double-free bugs when reference-counted objects are freed while another CPU still assumes exclusive access. Careful, consistent locking around object lifetime management is just as much a security practice as it is a correctness practice.

Summary and Key Takeaways

  • A lock serializes access to a critical section so only one CPU is “inside” it at a time
  • Spinlocks busy-wait and are safe in interrupt context; mutexes sleep and must never be used in interrupt context
  • Use _irqsave/_irqrestore spinlock variants whenever an interrupt handler can touch the same data
  • Keep critical sections short, and acquire multiple locks in a consistent order to avoid deadlocks

Conclusion

Linux kernel locking is where atomic operations meet real-world driver design. Once you’re comfortable choosing between a spinlock and a mutex, and structuring critical sections correctly, you have the core synchronization skills expected in any free Linux kernel development course, free Linux device drivers course, or free embedded systems course. From here, the natural next steps are reader/writer locks, RCU (Read-Copy-Update), and completion variables — all built on the same fundamental idea introduced in this lecture.

Frequently Asked Questions

Q1. Can I use a mutex inside an interrupt handler?

No. Mutexes can sleep, and interrupt handlers must never sleep. Use a spinlock (with the IRQ-safe variant) for data shared with interrupt context.

Q2. What happens if I hold a spinlock for too long?

Other CPUs waiting on that lock burn CPU cycles spinning, which can noticeably hurt system performance and, on real-time kernels, cause latency spikes.

Q3. What is a deadlock in kernel locking terms?

A situation where two or more CPUs each wait forever for a lock the other is holding, usually caused by acquiring multiple locks in inconsistent order.

Q4. Is atomic_t a replacement for spinlocks?

Only for single-variable updates. As soon as you need to update more than one related field together consistently, you need a lock.

Q5. What tool helps catch locking bugs during development?

Lockdep, the kernel’s built-in lock validator, is the standard tool for detecting potential deadlocks and incorrect lock usage patterns.

Q6. Where can I learn more locking primitives for free?

This lecture is part of EmbeddedPathashala’s free Linux kernel development course, which continues into reader/writer locks, RCU, and completion variables.

Continue Your Free Linux Kernel Development Course

Next up: reader/writer locks and an introduction to RCU in the Linux kernel.

Next Lecture Back to Course Index