Skip to content

Commit

Permalink
srcu: Initialize SRCU after timers
Browse files Browse the repository at this point in the history
Once srcu_init() is called, the SRCU core will make use of delayed
workqueues, which rely on timers.  However init_timers() is called
several steps after rcu_init().  This means that a call_srcu() after
rcu_init() but before init_timers() would find itself within a dangerously
uninitialized timer core.

This commit therefore creates a separate call to srcu_init() after
init_timer() completes, which ensures that we stay in early SRCU mode
until timers are safe(r).

Signed-off-by: Frederic Weisbecker <[email protected]>
Cc: Uladzislau Rezki <[email protected]>
Cc: Boqun Feng <[email protected]>
Cc: Lai Jiangshan <[email protected]>
Cc: Neeraj Upadhyay <[email protected]>
Cc: Josh Triplett <[email protected]>
Cc: Joel Fernandes <[email protected]>
Signed-off-by: Paul E. McKenney <[email protected]>
  • Loading branch information
Frederic Weisbecker authored and paulmckrcu committed May 10, 2021
1 parent 7bf0a61 commit 8e9c01c
Show file tree
Hide file tree
Showing 6 changed files with 13 additions and 8 deletions.
6 changes: 6 additions & 0 deletions include/linux/srcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp);
unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp);
bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie);

#ifdef CONFIG_SRCU
void srcu_init(void);
#else /* #ifdef CONFIG_SRCU */
static inline void srcu_init(void) { }
#endif /* #else #ifdef CONFIG_SRCU */

#ifdef CONFIG_DEBUG_LOCK_ALLOC

/**
Expand Down
2 changes: 2 additions & 0 deletions init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <linux/profile.h>
#include <linux/kfence.h>
#include <linux/rcupdate.h>
#include <linux/srcu.h>
#include <linux/moduleparam.h>
#include <linux/kallsyms.h>
#include <linux/writeback.h>
Expand Down Expand Up @@ -979,6 +980,7 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
tick_init();
rcu_init_nohz();
init_timers();
srcu_init();
hrtimers_init();
softirq_init();
timekeeping_init();
Expand Down
6 changes: 0 additions & 6 deletions kernel/rcu/rcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,6 @@ do { \

#endif /* #if defined(CONFIG_SRCU) || !defined(CONFIG_TINY_RCU) */

#ifdef CONFIG_SRCU
void srcu_init(void);
#else /* #ifdef CONFIG_SRCU */
static inline void srcu_init(void) { }
#endif /* #else #ifdef CONFIG_SRCU */

#ifdef CONFIG_TINY_RCU
/* Tiny RCU doesn't expedite, as its purpose in life is instead to be tiny. */
static inline bool rcu_gp_is_normal(void) { return true; }
Expand Down
5 changes: 5 additions & 0 deletions kernel/rcu/srcutree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1384,6 +1384,11 @@ void __init srcu_init(void)
{
struct srcu_struct *ssp;

/*
* Once that is set, call_srcu() can follow the normal path and
* queue delayed work. This must follow RCU workqueues creation
* and timers initialization.
*/
srcu_init_done = true;
while (!list_empty(&srcu_boot_list)) {
ssp = list_first_entry(&srcu_boot_list, struct srcu_struct,
Expand Down
1 change: 0 additions & 1 deletion kernel/rcu/tiny.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,5 +221,4 @@ void __init rcu_init(void)
{
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
rcu_early_boot_tests();
srcu_init();
}
1 change: 0 additions & 1 deletion kernel/rcu/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -4735,7 +4735,6 @@ void __init rcu_init(void)
WARN_ON(!rcu_gp_wq);
rcu_par_gp_wq = alloc_workqueue("rcu_par_gp", WQ_MEM_RECLAIM, 0);
WARN_ON(!rcu_par_gp_wq);
srcu_init();

/* Fill in default value for rcutree.qovld boot parameter. */
/* -After- the rcu_node ->lock fields are initialized! */
Expand Down

0 comments on commit 8e9c01c

Please sign in to comment.