Understanding Linux Kernel RCU: How Read Locks Work and When to Free Old Pointers
This article explains the design principles of Linux kernel RCU, details the implementation of rcu_read_lock, describes how interrupt handlers update shared data safely, and clarifies when old pointers can be reclaimed using call_rcu or synchronize_rcu.
RCU (Read-Copy Update) achieves lock‑free shared protection by replacing old pointers with new ones. The entry point for readers is rcu_read_lock(), whose inline definition calls __rcu_read_lock(), __acquire(RCU), and rcu_read_acquire(). The real work is done in __rcu_read_lock(), which disables preemption via preempt_disable() while still allowing interrupts.
When an interrupt occurs inside an RCU read‑side critical section, the ISR can allocate a new data structure new_p, copy the contents of the old structure p into it, modify new_p, and finally assign p = new_p. The ISR then registers a callback to free the old p after all readers have finished accessing it. This illustrates the RCU rule that brief inconsistencies caused by pointer swaps are permitted.
The crucial question is when the old pointer can be safely reclaimed. Many texts answer that all CPUs must have performed a context switch, but the deeper reason is the RCU rule: all references to the old pointer must occur only within rcu_read_lock() / rcu_read_unlock() critical sections, and no context switch can happen inside those sections. If a switch occurred, the callback might run while a pre‑empted task still holds a reference, leading to use‑after‑free bugs.
Therefore, rcu_read_lock() only needs to disable kernel preemption; it prevents task scheduling while still allowing interrupts. If a function that may sleep is called inside an RCU read‑side critical section, the RCU guarantees are broken and system stability is compromised.
Compared with rwlock readers, RCU readers have higher freedom because they do not need to coordinate with writers; writers must ensure exclusive updates and later invoke callbacks to free old data. RCU is most beneficial when read operations vastly outnumber updates, offering near‑zero read‑side overhead.
In practice, shared resources are often stored in linked lists, and the kernel provides RCU‑aware list primitives such as list_add_tail_rcu, list_add_rcu, and hlist_replace_rcu. These should be used for safe modifications.
Linux offers two mechanisms for reclaiming old pointers:
call_rcu : registers a callback that is queued on the local CPU’s RCU callback list. The soft‑IRQ rcu_process_callbacks runs in the RCU soft‑IRQ context, checks whether all CPUs have passed a quiescent state (i.e., each has performed a context switch), and then invokes the callback.
synchronize_rcu : inserts a node with a wakeme_after_rcu callback, then sleeps on a wait queue until all CPUs have passed a quiescent state. After waking, it knows the old pointer can be freed.
Consequently, call_rcu returns before the callback runs, so the old pointer may still be in use, whereas synchronize_rcu returns only after the pointer is guaranteed to be reclaimed. The choice depends on context: synchronize_rcu cannot be used in interrupt context, while call_rcu can.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
