RCU fundamentals
DISCLAIMER: this is copy pasted verbatim from several articles12 34 . I do this for my own notes and reference. ALL copyrights belong to the the original author(s). Any re-distribution of this post must preserve the references.
- Read-Copy-Update (RCU)
- Read-Copy-Update is a synchronization mechanism. This mechanism is notable in that RCU readers do not directly synchronize with RCU updaters, which makes RCU read paths extremely fast and also permits RCU readers to accomplish useful work even when running concurrently with RCU updaters1.
-
pro, the read path is very fast. In some cases (non-preempt kernels) RCU’s read-side primitives have zero overhead.
RCU supports concurrency between a single updater and multiple readers. RCU ensures that reads are coherent by maintaining multiple versions of objects and ensuring that they are not freed up until all pre-existing read-side critical sections complete2.
The great advantage of RCU is that it can wait for each of (say) 20,000 different things without having to explicitly track each and every one of them, and without having to worry about the performance degradation, scalability limitations, complex deadlock scenarios, and memory-leak hazards that are inherent in schemes using explicit tracking. 2
I’m a little skeptical about this claim: IIUC all RCU protected objects essentially share one single big lock. One RCU object may be blocked by its busy neighbour … even though they are totally unrelated.. right? Isnt’ this the same approach as the big-kernel-lock?
Glossary
- read-side critical section
- An RCU read-side critical section starts with an
rcu_read_lock()
and ends with a correspondingrcu_read_unlock()
; RCU read-side CS can be nested, and may contain pretty muc hany code, as long as that code does not explicitly block or sleep. - SRCU, RCU Clasic / realtime RCU
- /// TODO ///
- Grace Period5
- An RCU grace period is informally defined as any time period such that all RCU read-side critical sections in existence at the beginning of that period have completed before its end6.
-
A time period during which all such pre-existing readers complete (so that the updater can reclaim old versions of data structures.) is called a “grace period”7.
what is
RCU is made up of three fundamental mechanisms
- insertion: Publish-Subscribe Mechanism
- deletion: Wait fore pre-existing RCU readers to complete
- to allow readers to tolerate concurrent insertions and deletions: maintaining multiple versions of recent updated objects.
Part I. Publish-Subscribe Mechanism (insertion)
using example from2
| |||||
Assigning the structure to the global pointer (i.e. to publish the new
structure); with normal code, the pointer may be asigned before the
initialzation is done. Reader may get uninitialized fields via | |||||
Publisher: Normal | Publisher: W. RCU | ||||
|
| ||||
reading the data: i.e. readers subscribe to the data. In the normal version
the values of | |||||
Reader: Normal | Reader: W. RCU | ||||
|
|
- the
rcu_read_{lock,unlock}
never spin or block, nor do they prevent thelist_add_rcu()
from executing concurrently. rcu_{assign_pointer, dereference}
can be used to construct RCU-protected structure but better to use higher-level constructs.
linked list w/ RCU
Linux has 2 variants of doubly linked list: circular struct list_head
(the
first and last elements are linked via their next/prev pointers) and linear
struct hlist_headr/struct hlist_nod
.
Publishing a new element to an RCU-protected hlist:
|
|
Subscribe to an RCU-protected hlist:
|
|
Part II. Wait for Pre-Existing Readers to Complete (deletion)
RCU decide when things have finished indirectly .PREEMPT
, NON_PREEMPT
and RT
kernels’ RCU work differently.
- on
NON_PREEMPT
kernels thercu_read_lock()
andrcu_read_unlock()
don’t generate any code.synchronize_cpu()
works by forcing each CPU to execute at least one context switch. - for
PREEMPT_RT
kernel, realtime RCU uses a different approach based loosely on reference counters.
Part III. Multiple Versions of Recent Updated Objects
APIs
void list_add_rcu(struct list_head * new, struct list_head * head)
;- add a new entry to rcu-protected list
hlist_bl_for_each_entry_rcu (tpos, pos, head, member)
- macro
tpos
:type *
to use as a loop cursorpos
:sturct hlist_bl_node
to use as a loop cursorhead
: the head for the listmember
: the name ofhlist_bl_node
within the struct
Category | Publish | Retract | Subscribe |
---|---|---|---|
Pointers | rcu_assign_pointer() |
rcu_assign_pointer(..., NULL) |
rcu_dereference() |
Lists | list_add_rcu() list_add_tail_rcu() list_replace_rcu() |
list_del_rcu() |
list_for_each_entry_rcu() |
Hlists | hlist_add_after_rcu() hlist_add_before_rcu() hlist_add_head_rcu() hlist_replace_rcu() |
hlist_del_rcu() |
hlist_for_each_entry_rcu() |
-
The RCU API, 2024 edition, https://lwn.net/Articles/988638/ ↩︎ ↩︎
-
What is RCU, Fundamentally? (this is the part 1) https://lwn.net/Articles/262464/ ↩︎ ↩︎ ↩︎ ↩︎
-
What is RCU? Part 2: Usage, https://lwn.net/Articles/263130/ ↩︎
-
RCU part 3: the RCU API https://lwn.net/Articles/264090/ ↩︎
-
A Tour Through TREE_RCU’s expedited Grace Periods, https://docs.kernel.org/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.html ↩︎
-
Multi-Core Systems Modeling for Formal Verification of Parallel Algorithms, Mathieu Desnoyers, Paul E. McKenney, Michel R. Dagenais ↩︎
-
The design of preemptible read-copy-update https://lwn.net/Articles/253651/ ↩︎