The configuration pointer has been switched to a new object, but old readers have not yet finished. RCU separates the update of the entry point from the reclamation of old memory.
Lookup Target
Reading Location
Publication and Grace Periods
Sections 1–3
Configuration Replacement and Callback Draining
Sections 4–5
SRCU, Escaped References, and Stalls
Sections 6–8
RCU separates "removing an object from a shared structure" from "freeing the object's memory." Readers can access published objects with lower synchronization overhead; after an updater revokes the reachable entry points of the old object, it waits for an appropriate grace period before reclaiming the old memory is allowed.
It does not require copying the entire structure for every update, nor does it automatically provide readers with a globally consistent, up-to-date snapshot across multiple objects. RCU is more like a set of protocols regarding publication and lifetime, which must be designed in conjunction with the update rules of the data structure itself.
1. Three Responsibilities Must Be Handled Separately
Responsibility
Common Mechanism
What It Does Not Handle
Publishing an object after initialization
rcu_assign_pointer, etc.
Serializing multiple updaters
Readers obtaining and using the pointer
rcu_read_lock + rcu_dereference
Guaranteeing that object fields are never modified by other mechanisms
Deferred reclamation after revocation
synchronize_rcu / call_rcu / kfree_rcu
Guaranteeing that all escaped references have disappeared
The __rcu annotation aids static checking; it is not automatic reference counting at runtime. rcu_dereference() must be used according to the corresponding read-side or protected conditions; you cannot assume any object is safe just because this macro is used.
2. Grace Period Waits for More Than Just "All Readers in the World"
After removing the old object, an appropriate grace period ensures that the relevant read-side critical sections that might previously have held the old object have ended. New readers can continue to enter; the system does not need to wait for the instant when "no CPU has any RCU readers at all."
The object must first be revoked from all relevant publication entry points, and readers must only obtain it via agreed-upon entry points. If another linked list still points to the old object, or if a reader takes a raw pointer out of the critical section to continue using it, waiting for a single grace period will not fix the lifetime error.
A grace period does not start a fixed timer from the pointer assignment instruction, nor is it a fixed number of milliseconds. Scheduling, configuration, read-side duration, and implementation all affect the wait time. Synchronous waiting can sleep; asynchronous callbacks avoid blocking the updater synchronously but can accumulate unreclaimed objects.
There is no universal fixed millisecond duration. synchronize_rcu can sleep; call_rcu queues later reclamation, which can accumulate memory if updates outpace reclamation.
Preparing the visual
Which reader sections does a grace period wait for?
Removing the old entry, waiting for reader sections, and executing reclamation are separate boundaries. New readers may enter; the same task name does not mean the same reader-section generation.
{"id":"rcu-reader-generations","title":"Which reader sections does a grace period wait for?","summary":"Removing the old entry, waiting for reader sections, and executing reclamation are separate boundaries. New readers may enter; the same task name does not mean the same reader-section generation.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"actions\"><button id=\"enterA\" data-i18n=\"enterA\"></button><button id=\"exitA\" data-i18n=\"exitA\"></button></div><div class=\"actions\"><button id=\"enterB\" data-i18n=\"enterB\"></button><button id=\"exitB\" data-i18n=\"exitB\"></button></div><div class=\"track-row\"><span data-i18n=\"entry\"></span><div id=\"entry\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"readers\"></span><div id=\"readers\" class=\"track\"></div></div><div class=\"actions\"><button id=\"publish\" data-i18n=\"publish\"></button><button id=\"start\" data-i18n=\"start\"></button></div><div class=\"track-row\"><span data-i18n=\"wait\"></span><div id=\"wait\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"objects\"></span><div id=\"objects\" class=\"track\"></div></div><div class=\"card\"><header data-i18n=\"phase\"></header><div id=\"phase\" class=\"readout\"></div></div><div class=\"actions\"><button id=\"callback\" data-i18n=\"callback\"></button></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"note\"></p></div><div class=\"actions\"><button id=\"reset\" data-i18n=\"reset\"></button></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.track#readers,.track#wait{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.track#wait .cell{min-height:64px}.readout{min-height:5.3em}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}let entry='O',readers=[null,null],serial=0,snapshot=null,alive=true;function done(){return snapshot!==null&&snapshot.every(v=>!readers.some(r=>r&&r.token===v.token));}function draw(){cells('entry',[{text:'entry → '+entry,cls:'on'}]);cells('readers',readers.map((r,i)=>({text:'AB'[i]+' → '+(r?r.token+' → '+r.object:'—'),cls:r?'on':'empty'})));cells('wait',snapshot===null||!snapshot.length?[{text:'—',cls:'empty'},{text:'—',cls:'empty'}]:[0,1].map(i=>{const v=snapshot[i],pending=v&&readers.some(r=>r&&r.token===v.token);return{text:v?v.token+(pending?' …':' ✓'):'—',cls:pending?'gold':'empty'};}));cells('objects',[{text:'O '+(alive?'●':'×'),cls:alive?'gold':'empty'},{text:'N '+(entry==='N'?'●':'—'),cls:entry==='N'?'on':'empty'}]);for(let i=0;i<2;i++){$('#enter'+'AB'[i]).disabled=!!readers[i];$('#exit'+'AB'[i]).disabled=!readers[i];}$('#publish').disabled=entry==='N';$('#start').disabled=entry!=='N'||snapshot!==null;$('#callback').disabled=!done()||!alive;set('phase',t(!alive?'reclaimed':done()?'done':snapshot===null?'initial':'waiting'));set('explanation',t('note'));}for(let i=0;i<2;i++){$('#enter'+'AB'[i]).onclick=()=>{readers[i]={token:'AB'[i]+(++serial),object:entry};draw();};$('#exit'+'AB'[i]).onclick=()=>{readers[i]=null;draw();};}$('#publish').onclick=()=>{entry='N';draw();};$('#start').onclick=()=>{snapshot=readers.filter(Boolean).map(r=>({...r}));draw();};$('#callback').onclick=()=>{if(done())alive=false;draw();};$('#reset').onclick=()=>{entry='O';readers=[null,null];serial=0;snapshot=null;alive=true;draw();};draw();","audio":false,"strings":{"callback":"Run O’s reclaim callback","done":"GP complete; callback not executed","enterA":"A enters read section","enterB":"B enters read section","entry":"Current published entry","exitA":"A leaves read section","exitB":"B leaves read section","heading":"Which reader sections does a grace period wait for?","initial":"Grace period not started","intro":"Enter and leave reader sections A and B; publish a new object and start a grace period. Leave and immediately re-enter: the new section does not replace the old one as a blocker.","note":"Try A holding O, publish N, then B holding N before starting the GP. Both sections active at GP start must end; A’s later new section does not extend this wait.","objects":"Object memory: O / N","phase":"Reclamation phase","publish":"Publish N, remove O entry","readers":"Reader → section token → object","reclaimed":"O reclaimed","reset":"Reset","scope":"Two nonnested readers and one serialized writer; O/N are immutable after publication. Pointers come only from the current entry and never escape sections. The GP snapshot contains all tokens active at its start; later tokens do not join. Real RCU proves this through implementation-specific quiescent-state/task tracking, not this teaching token table.","scopeLabel":"Model scope and assumptions","start":"Start grace period for O","wait":"Fixed sections this grace period waits for","waiting":"Grace period waiting"}}
3. Preemptible RCU and Quiescent States
You cannot uniformly say that rcu_read_lock() is always preempt_disable(). In preemptible RCU configurations, read-side tasks can be preempted, and the kernel tracks relevant readers; the instruction overhead for read-sides differs across implementations.
Normal RCU read-sides should adhere to API rules that prohibit arbitrary blocking; being preempted by the scheduler is not the same as actively calling operations that may sleep. If you need to sleep normally on the read-side, consider appropriate interfaces like SRCU, rather than inserting schedule yourself.
A quiescent state is evidence that the relevant read-side has passed; it may involve user mode, idle, context switches, and other states; it does not require that every CPU undergo a context switch at least once during each grace period. A preempted RCU reader cannot be considered to have exited simply because the CPU switched away. See What is RCU.
4. A Replaceable Configuration Object
The following is an example of an independent translation unit for a kernel component, excluding registration entry points. Once the object is published, its value is not modified in place; updaters use a mutex for serialization, and readers only copy out scalars.
#include<linux/errno.h>#include<linux/mutex.h>#include<linux/rcupdate.h>#include<linux/slab.h>structkb_config{int value;struct rcu_head rcu;};staticDEFINE_MUTEX(config_lock);staticstruct kb_config __rcu *current_config;intkb_config_get(int*value);intkb_config_replace(intvalue);voidkb_config_shutdown(void);staticvoidfree_config(struct rcu_head *head){struct kb_config *p =container_of(head,struct kb_config, rcu);kfree(p);}intkb_config_get(int*value){struct kb_config *p;int ret =-ENOENT;rcu_read_lock();
p =rcu_dereference(current_config);if(p){*value = p->value;
ret =0;}rcu_read_unlock();return ret;}intkb_config_replace(intvalue){struct kb_config *next,*old;
next =kmalloc(sizeof(*next), GFP_KERNEL);if(!next)return-ENOMEM;
next->value = value;mutex_lock(&config_lock);
old =rcu_replace_pointer(current_config, next,lockdep_is_held(&config_lock));mutex_unlock(&config_lock);if(old)call_rcu(&old->rcu, free_config);return0;}/* Callers must first shut down all entry points and wait for executing API calls to exit. */voidkb_config_shutdown(void){struct kb_config *old;mutex_lock(&config_lock);
old =rcu_replace_pointer(current_config,NULL,lockdep_is_held(&config_lock));mutex_unlock(&config_lock);synchronize_rcu();kfree(old);rcu_barrier();/* Wait for previously queued callbacks that will execute this component's code. */}
The value output address is guaranteed to be valid by the caller and should not point to user pointers that require page faults. The shutdown's entry point closure and waiting for call exits are responsibilities of external components: adding config_lock alone cannot prevent a replace operation that hasn't yet reached the lock acquisition point from republishing after shutdown.
Between Writers, RCU Does Not Replace Mutual Exclusion
Suppose two writers both read the old object O, and then publish N1 and N2 respectively. If they each reclaim the O they read, they might double-free the same object. The example serializes the "obtain old value, replace, schedule reclamation" operation using an updater-side mutex.
5. Synchronous Waiting and Asynchronous Callbacks
call_rcu() receives an embedded rcu_head, and the callback parameter is struct rcu_head *; use container_of to retrieve the outer object. kfree_rcu(p, rcu) is a common release interface, but it should not be interpreted as forcibly casting kfree into a callback pointer; their parameters and implementations are different.
Callbacks for call_rcu() must adhere to execution constraints that prohibit arbitrary sleeping. Callbacks become eligible for execution only after the grace period, but there is no guarantee that they will run immediately on a specific CPU the moment the grace period ends.
Module Exit: Reader End and Callback Completion Are Not the Same Condition
Wait For
What Guarantee Is Established
synchronize_rcu
Waits for the reader grace period; does not guarantee that all previous call_rcu callbacks have finished executing
Block new callbacks first, then rcu_barrier
Waits for submitted callbacks to drain, preventing unloaded code from still being called
When module callbacks reside in the module's own code, you must first resolve new submission entry points, then wait for callbacks to complete, and only then can you unload. See RCU and Unloadable Modules.
When asynchronous update rates are high, you must also control the growth of memory pending reclamation. Expedited grace periods can reduce some waiting times but increase system overhead and cannot serve as a universal fix for all RCU stalls.
6. SRCU and Tasks RCU
SRCU uses an independent srcu_struct domain. The index token obtained by the reader must be passed back to the unlock of the same domain:
int idx =srcu_read_lock(&domain);/* Obtain and use the object according to the SRCU protocol; sleeping is allowed under permitted conditions. */srcu_read_unlock(&domain, idx);
Updaters use synchronize_srcu(&domain) or the corresponding callback API. You cannot wait for a grace period containing yourself from within your own same-domain read-side critical section, nor can you destroy the domain before reclamation is complete. Readers and waits from different domains are not interchangeable.
RCU Tasks, Tasks Rude, and Tasks Trace are designed for special task/tracing synchronization needs, with different waiting conditions. They are not a single variant called "trace-specific preempt-disabled RCU." Without corresponding underlying requirements, you should not replace normal RCU just because the name contains "Tasks."
7. What If You Continue Using After the Read-Side Ends?
If you need to hold an object for a long time after exiting the read-side, you can attempt to obtain an independent reference according to the object's protocol within the critical section, and confirm success; the rules for reclamation and zero-initial acquisition must be designed together. Simply storing a raw pointer in a global variable or asynchronous work item, and then exiting the RCU read-side, will lose the original lifetime guarantee.
RCU also does not automatically serialize concurrent updates to the same object field. Immutable object replacement is a more easily reasoned pattern; when in-place updates are needed, you must additionally specify locks, atomic operations, and the consistency acceptable to readers.
8. RCU Stalls Should Be Analyzed Based on Evidence
A stall indicates that RCU did not make necessary progress within the expected time. Causes may include long read-sides, long interrupts/preemptions disabled, CPU starvation, timer or scheduling anomalies, etc. You cannot see an alert and immediately assert that rcu_read_unlock was definitely missed.
Locate the issue by combining CPU/task information, grace periods, repeated stack traces, and running configuration from the report. Increasing the stall timeout only changes the reporting threshold; it does not fix the progress issue. See RCU Stall Warnings and Synchronization Debugging.