[Linux Kernel] Mutex (3) - struct mutex

What is a Mutex?

A mutex (short for mutual exclusion) is a synchronization mechanism that ensures only one process can access a critical section at a time. It prevents multiple processes from modifying shared resources simultaneously, which helps maintain data integrity.

Although mutexes are widely used in the Linux kernel, they are not exclusive to it. The concept of mutexes exists in various operating systems, each with its own implementation. However, the core idea remains the same: preventing two processes from entering a critical section at the same time.

Example: Protecting a Critical Section Using a Mutex

Let’s analyze an example from the Linux kernel that demonstrates how a mutex protects a critical section. The following code is from the Linux kernel source for CPU frequency scaling (cpufreq_governor.c):

static void dbs_work_handler(struct work_struct *work)
{
    struct policy_dbs_info *policy_dbs;
    struct cpufreq_policy *policy;
    struct dbs_governor *gov;
    
    mutex_lock(&policy_dbs->update_mutex);  // Acquire the mutex lock
    gov_update_sample_delay(policy_dbs, gov->gov_dbs_update(policy));  // Critical section
    mutex_unlock(&policy_dbs->update_mutex);  // Release the mutex lock
}        

Before executing this function, the process acquires the mutex lock using mutex_lock(). Once the function execution is complete, the process releases the mutex using mutex_unlock().

If one process has already locked the mutex and is executing the critical section, any other process attempting to enter it will be put to sleep inside mutex_lock() until the lock is released.

This ensures that only one process at a time can modify the shared resource, preventing race conditions.

Mutex Data Structure in Linux Kernel

The mutex is represented by the struct mutex data structure in the Linux kernel.

struct mutex {
    atomic_long_t owner;      // Stores the task descriptor of the process holding the mutex
    spinlock_t wait_lock;     // Protects the waiting list
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
    struct optimistic_spin_queue osq;  // Used for spin-waiting
#endif
    struct list_head wait_list;  // List of processes waiting for the mutex
#ifdef CONFIG_DEBUG_MUTEXES
    void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map dep_map;
#endif
};        

In Raspbian, the kernel is compiled with CONFIG_DEBUG_MUTEXES and CONFIG_DEBUG_LOCK_ALLOC disabled, so the magic and dep_map fields are not included in the compiled binary. Key Fields in the mutex Structure

  • atomic_long_t owner: Stores the task descriptor (process information) of the process that currently holds the mutex. This is the most important field for checking if the mutex is locked.
  • struct list_head wait_list: Maintains a list of processes waiting to acquire the mutex. If a process finds that another process already owns the mutex, it registers itself in wait_list and enters sleep mode until the mutex is released. This is the first field to check when debugging mutex-related issues.

If the wait_list contains entries, it means some processes are waiting for the mutex.

Processes waiting for a mutex are represented by the struct mutex_waiter structure.

struct mutex_waiter {
    struct list_head list;   // Links waiting processes in a queue
    struct task_struct *task;  // Stores the task descriptor of the waiting process
    struct ww_acquire_ctx *ww_ctx;
#ifdef CONFIG_DEBUG_MUTEXES
    void *magic;
#endif
};        

Key Fields in the mutex_waiter Structure

  • struct list_head list: Forms a linked list of waiting processes. The wait_list field in struct mutex points to this list.
  • struct task_struct *task: Stores the task descriptor of the process waiting for the mutex. This helps determine which process is currently waiting for the mutex.

These fields are essential when analyzing which processes are blocked and why.

Mutex Lock and Unlock Functions

The kernel provides two key functions for mutex operations:

extern void mutex_lock(struct mutex *lock);   // Acquire the mutex
extern void mutex_unlock(struct mutex *lock); // Release the mutex        

Fastpath vs. Slowpath in Mutex Execution

When a process tries to acquire a mutex, the execution follows either the fastpath or slowpath, depending on whether the mutex is free.

  • Fastpath Execution: If no other process holds the mutex, the requesting process immediately acquires the lock. The process executes the critical section and releases the mutex quickly.
  • Slowpath Execution: If another process already holds the mutex, the requesting process: Registers itself in the wait queue (wait_list). Enters sleep mode until the mutex is released. When the current holder releases the mutex, the next process in the wait queue wakes up and acquires the lock. The slowpath ensures that processes do not spin in an infinite loop while waiting for the mutex, improving CPU efficiency.

Next post will cover the implementation of mutex_lock() in more detail. So please stay tuned.


Kishore K.

Software Engineer at Wipro

2mo

Useful tips

Like
Reply
Wesley Mesquita

Software Engineer at Goldman Sachs

2mo

Thank you for the article! Looking for the implementation details!

Alan jonas Piñon

Embedded software Engineer - SDE - Ex Amazon

2mo

Excelent article!

To view or add a comment, sign in

More articles by Austin Kim

Insights from the community

Others also viewed

Explore topics