Day 88 - Kernel Locking Internals¶
Objective¶
Learn why Linux kernel locking is required to protect shared data accessed from multiple execution contexts.
Implement simplified Linux-style synchronization primitives and compare their behavior under different scenarios, including race conditions, busy waiting, blocking synchronization, and interrupt-safe locking.
Lab Overview¶
This lab consists of four parts:
- Lab 1: Race Condition
- Lab 2: Spinlock
- Lab 3: Mutex
- Lab 4: spin_lock_irqsave()
Lab 1 - Race Condition¶
Objective¶
Demonstrate how concurrent updates to shared data produce incorrect results without synchronization.
Concepts¶
- Shared data
- Race condition
- Critical section
- Lost update
Implementation¶
Two worker threads increment the same shared counter without any synchronization.
Expected Result¶
The final counter value is smaller than expected because multiple updates overwrite each other.
Example:
Lab 2 - Spinlock¶
Objective¶
Implement a simplified Linux-style spinlock using C11 atomic operations.
Concepts¶
- Busy waiting
- Atomic compare-and-exchange
- Mutual exclusion
- Lock contention
Implementation¶
Implement:
spin_lock_init()spin_lock()spin_unlock()
A second version records retry counts during lock acquisition to visualize lock contention.
Expected Result¶
The shared counter is always correct.
Example:
Retry statistics demonstrate that waiting threads repeatedly spin until the lock becomes available.
Lab 3 - Mutex¶
Objective¶
Implement a simplified Linux-style mutex.
Concepts¶
- Blocking synchronization
- Sleepable lock
- Process Context
Implementation¶
Wrap POSIX pthread_mutex_t using a Linux-style mutex interface.
Implement:
mutex_init()mutex_lock()mutex_unlock()
Expected Result¶
The shared counter remains correct while waiting threads are blocked instead of busy waiting.
Lab 4 - spin_lock_irqsave()¶
Objective¶
Understand why interrupt-safe spinlocks are required.
Concepts¶
- Process Context
- IRQ Context
- Local IRQ disable
- Self-deadlock
- Deferred interrupt
Implementation¶
Implement:
spin_trylock()spin_lock_irqsave()spin_unlock_irqrestore()
Integrate the locking framework with the existing IRQ subsystem.
Two scenarios are demonstrated:
Case 1¶
Process Context acquires a spinlock.
While holding the lock, an interrupt occurs.
The interrupt handler attempts to acquire the same spinlock.
Result:
Case 2¶
Process Context acquires the spinlock using spin_lock_irqsave().
Local interrupts are disabled before entering the critical section.
The interrupt is deferred until spin_unlock_irqrestore() restores the previous interrupt state.
Expected Result¶
Without spin_lock_irqsave():
With spin_lock_irqsave():
Process
↓
spin_lock_irqsave()
↓
Local IRQ Disabled
↓
IRQ Deferred
↓
spin_unlock_irqrestore()
↓
Pending IRQ Handled
Summary¶
After completing this lab, you should understand:
- Why race conditions occur.
- How spinlocks protect short critical sections.
- Why mutexes are sleepable while spinlocks are not.
- Why interrupt handlers require spinlocks instead of mutexes.
- How
spin_lock_irqsave()prevents local interrupt re-entry while protecting shared kernel data.