Protecting shared data across SMP, preemption and interrupts: atomics, spinlocks (and the interrupt-deadlock trap / spin_lock_irqsave), mutexes/semaphores, RCU and grace periods, seqlocks, memory barriers, per-CPU data, and lockdep.
The kernel runs on many CPUs, is preemptible, and gets interrupted mid-work — so shared data needs protection. Picking the right primitive (and knowing which ones may sleep) is the whole game. Original, interview-focused notes.
The decision tree: is the critical section tiny and can it run in interrupt context? → spinlock. Might it sleep / is it longer? → mutex/semaphore. Read-mostly, hot? → RCU or a seqlock. Just a counter/flag? → atomics.
Concurrency in the kernel comes from several directions at once: multiple CPUs (SMP), kernel preemption (a task can be preempted mid-critical-section), and interrupts (a handler can fire while you hold data). Any unprotected read-modify-write is a race.
| Primitive | Blocks by | Can sleep? | Use for |
|---|---|---|---|
| Atomic ops | Lock-free (CPU instruction) | — | Counters, flags (atomic_t, bit ops). |
| Spinlock | Busy-wait (spin) | No | Short sections; the only choice in interrupt/softirq context. |
| Mutex | Sleep | Yes | Longer sections in process context. |
| Semaphore / rwsem | Sleep | Yes | Counting; reader/writer variants. |
| RCU | Lock-free readers | Readers: no | Read-mostly data (routing tables, lists). |
| seqlock | Retry on writer | — | Rare writers, frequent readers (e.g. time). |
spin_lock_irqsave() disables local
interrupts while held when the data is shared with a handler.copy_from_user, no kmalloc(GFP_KERNEL),
no mutex. That's the #1 rule.Read-Copy-Update lets readers proceed with no locks and no waiting. Writers make a
copy, update it, then swap the pointer atomically; the old version is freed only after a
grace period — once every pre-existing reader has finished (a "quiescent state"). Perfect for hot,
read-mostly structures where locking readers would kill scalability. Readers just wrap access in
rcu_read_lock() (which mostly disables preemption).
CPUs and compilers reorder memory accesses, so on weakly-ordered hardware a lock's correctness
needs memory barriers (smp_mb/rmb/wmb). The locking primitives
embed the right barriers for you — you only reach for explicit barriers in lock-free code. Mention this to show you
know locks aren't just mutual exclusion; they're also ordering.
spin_lock_irqsave add and why? (disable local IRQs; avoid handler self-deadlock)