Skip to content

Commit 30a7806

Browse files
Joel Fernandesneeraju
authored andcommitted
rcu: Document GP init vs hotplug-scan ordering requirements
Add detailed comments explaining the critical ordering constraints during RCU grace period initialization, based on discussions with Frederic. Reviewed-by: "Paul E. McKenney" <[email protected]> Co-developed-by: Frederic Weisbecker <[email protected]> Signed-off-by: Frederic Weisbecker <[email protected]> Signed-off-by: Joel Fernandes <[email protected]> Signed-off-by: Neeraj Upadhyay (AMD) <[email protected]>
1 parent 908a97e commit 30a7806

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

Documentation/RCU/Design/Requirements/Requirements.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,6 +1970,47 @@ corresponding CPU's leaf node lock is held. This avoids race conditions
19701970
between RCU's hotplug notifier hooks, the grace period initialization
19711971
code, and the FQS loop, all of which refer to or modify this bookkeeping.
19721972

1973+
Note that grace period initialization (rcu_gp_init()) must carefully sequence
1974+
CPU hotplug scanning with grace period state changes. For example, the
1975+
following race could occur in rcu_gp_init() if rcu_seq_start() were to happen
1976+
after the CPU hotplug scanning.
1977+
1978+
.. code-block:: none
1979+
1980+
CPU0 (rcu_gp_init) CPU1 CPU2
1981+
--------------------- ---- ----
1982+
// Hotplug scan first (WRONG ORDER)
1983+
rcu_for_each_leaf_node(rnp) {
1984+
rnp->qsmaskinit = rnp->qsmaskinitnext;
1985+
}
1986+
rcutree_report_cpu_starting()
1987+
rnp->qsmaskinitnext |= mask;
1988+
rcu_read_lock()
1989+
r0 = *X;
1990+
r1 = *X;
1991+
X = NULL;
1992+
cookie = get_state_synchronize_rcu();
1993+
// cookie = 8 (future GP)
1994+
rcu_seq_start(&rcu_state.gp_seq);
1995+
// gp_seq = 5
1996+
1997+
// CPU1 now invisible to this GP!
1998+
rcu_for_each_node_breadth_first() {
1999+
rnp->qsmask = rnp->qsmaskinit;
2000+
// CPU1 not included!
2001+
}
2002+
2003+
// GP completes without CPU1
2004+
rcu_seq_end(&rcu_state.gp_seq);
2005+
// gp_seq = 8
2006+
poll_state_synchronize_rcu(cookie);
2007+
// Returns true!
2008+
kfree(r1);
2009+
r2 = *r0; // USE-AFTER-FREE!
2010+
2011+
By incrementing gp_seq first, CPU1's RCU read-side critical section
2012+
is guaranteed to not be missed by CPU2.
2013+
19732014
Scheduler and RCU
19742015
~~~~~~~~~~~~~~~~~
19752016

kernel/rcu/tree.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,14 @@ static noinline_for_stack bool rcu_gp_init(void)
18381838
start_new_poll = rcu_sr_normal_gp_init();
18391839
/* Record GP times before starting GP, hence rcu_seq_start(). */
18401840
old_gp_seq = rcu_state.gp_seq;
1841+
/*
1842+
* Critical ordering: rcu_seq_start() must happen BEFORE the CPU hotplug
1843+
* scan below. Otherwise we risk a race where a newly onlining CPU could
1844+
* be missed by the current grace period, potentially leading to
1845+
* use-after-free errors. For a detailed explanation of this race, see
1846+
* Documentation/RCU/Design/Requirements/Requirements.rst in the
1847+
* "Hotplug CPU" section.
1848+
*/
18411849
rcu_seq_start(&rcu_state.gp_seq);
18421850
/* Ensure that rcu_seq_done_exact() guardband doesn't give false positives. */
18431851
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) &&

0 commit comments

Comments
 (0)