Skip to content

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.

Thread A
    counter++

Thread B
    counter++

Expected Result

The final counter value is smaller than expected because multiple updates overwrite each other.

Example:

Expected : 2000000
Actual   : 1452873

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:

Expected : 2000000
Actual   : 2000000

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:

Self-deadlock risk

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():

Process
spin_lock()

IRQ

ISR

Lock Busy

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.