Commit b0d304172f49061b4ff78f9e2b02719ac69c8a7e

Authored by Paul E. McKenney
Committed by Paul E. McKenney
1 parent 620917de59

rcu: Prevent RCU callbacks from executing before scheduler initialized

Under some rare but real combinations of configuration parameters, RCU
callbacks are posted during early boot that use kernel facilities that
are not yet initialized.  Therefore, when these callbacks are invoked,
hard hangs and crashes ensue.  This commit therefore prevents RCU
callbacks from being invoked until after the scheduler is fully up and
running, as in after multiple tasks have been spawned.

It might well turn out that a better approach is to identify the specific
RCU callbacks that are causing this problem, but that discussion will
wait until such time as someone really needs an RCU callback to be invoked
(as opposed to merely registered) during early boot.

Reported-by: julie Sullivan <kernelmail.jms@gmail.com>
Reported-by: RKK <kulkarni.ravi4@gmail.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Tested-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Tested-by: julie Sullivan <kernelmail.jms@gmail.com>
Tested-by: RKK <kulkarni.ravi4@gmail.com>

Showing 2 changed files with 36 additions and 5 deletions Side-by-side Diff

... ... @@ -84,9 +84,32 @@
84 84  
85 85 static struct rcu_state *rcu_state;
86 86  
  87 +/*
  88 + * The rcu_scheduler_active variable transitions from zero to one just
  89 + * before the first task is spawned. So when this variable is zero, RCU
  90 + * can assume that there is but one task, allowing RCU to (for example)
  91 + * optimized synchronize_sched() to a simple barrier(). When this variable
  92 + * is one, RCU must actually do all the hard work required to detect real
  93 + * grace periods. This variable is also used to suppress boot-time false
  94 + * positives from lockdep-RCU error checking.
  95 + */
87 96 int rcu_scheduler_active __read_mostly;
88 97 EXPORT_SYMBOL_GPL(rcu_scheduler_active);
89 98  
  99 +/*
  100 + * The rcu_scheduler_fully_active variable transitions from zero to one
  101 + * during the early_initcall() processing, which is after the scheduler
  102 + * is capable of creating new tasks. So RCU processing (for example,
  103 + * creating tasks for RCU priority boosting) must be delayed until after
  104 + * rcu_scheduler_fully_active transitions from zero to one. We also
  105 + * currently delay invocation of any RCU callbacks until after this point.
  106 + *
  107 + * It might later prove better for people registering RCU callbacks during
  108 + * early boot to take responsibility for these callbacks, but one step at
  109 + * a time.
  110 + */
  111 +static int rcu_scheduler_fully_active __read_mostly;
  112 +
90 113 #ifdef CONFIG_RCU_BOOST
91 114  
92 115 /*
... ... @@ -98,7 +121,6 @@
98 121 DEFINE_PER_CPU(int, rcu_cpu_kthread_cpu);
99 122 DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops);
100 123 DEFINE_PER_CPU(char, rcu_cpu_has_work);
101   -static char rcu_kthreads_spawnable;
102 124  
103 125 #endif /* #ifdef CONFIG_RCU_BOOST */
104 126  
... ... @@ -1467,6 +1489,8 @@
1467 1489 */
1468 1490 static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp)
1469 1491 {
  1492 + if (unlikely(!ACCESS_ONCE(rcu_scheduler_fully_active)))
  1493 + return;
1470 1494 if (likely(!rsp->boost)) {
1471 1495 rcu_do_batch(rsp, rdp);
1472 1496 return;
kernel/rcutree_plugin.h
... ... @@ -1532,7 +1532,7 @@
1532 1532 struct sched_param sp;
1533 1533 struct task_struct *t;
1534 1534  
1535   - if (!rcu_kthreads_spawnable ||
  1535 + if (!rcu_scheduler_fully_active ||
1536 1536 per_cpu(rcu_cpu_kthread_task, cpu) != NULL)
1537 1537 return 0;
1538 1538 t = kthread_create(rcu_cpu_kthread, (void *)(long)cpu, "rcuc%d", cpu);
... ... @@ -1639,7 +1639,7 @@
1639 1639 struct sched_param sp;
1640 1640 struct task_struct *t;
1641 1641  
1642   - if (!rcu_kthreads_spawnable ||
  1642 + if (!rcu_scheduler_fully_active ||
1643 1643 rnp->qsmaskinit == 0)
1644 1644 return 0;
1645 1645 if (rnp->node_kthread_task == NULL) {
... ... @@ -1665,7 +1665,7 @@
1665 1665 int cpu;
1666 1666 struct rcu_node *rnp;
1667 1667  
1668   - rcu_kthreads_spawnable = 1;
  1668 + rcu_scheduler_fully_active = 1;
1669 1669 for_each_possible_cpu(cpu) {
1670 1670 per_cpu(rcu_cpu_has_work, cpu) = 0;
1671 1671 if (cpu_online(cpu))
... ... @@ -1687,7 +1687,7 @@
1687 1687 struct rcu_node *rnp = rdp->mynode;
1688 1688  
1689 1689 /* Fire up the incoming CPU's kthread and leaf rcu_node kthread. */
1690   - if (rcu_kthreads_spawnable) {
  1690 + if (rcu_scheduler_fully_active) {
1691 1691 (void)rcu_spawn_one_cpu_kthread(cpu);
1692 1692 if (rnp->node_kthread_task == NULL)
1693 1693 (void)rcu_spawn_one_node_kthread(rcu_state, rnp);
... ... @@ -1725,6 +1725,13 @@
1725 1725 static void rcu_cpu_kthread_setrt(int cpu, int to_rt)
1726 1726 {
1727 1727 }
  1728 +
  1729 +static int __init rcu_scheduler_really_started(void)
  1730 +{
  1731 + rcu_scheduler_fully_active = 1;
  1732 + return 0;
  1733 +}
  1734 +early_initcall(rcu_scheduler_really_started);
1728 1735  
1729 1736 static void __cpuinit rcu_prepare_kthreads(int cpu)
1730 1737 {