aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/rcu/tree.c
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2017-06-08 19:55:40 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2017-07-25 16:04:45 -0400
commit313517fc44fb2d8403654b2d3e511da7d1c78cd6 (patch)
tree92e41aafaf05341e66aeb10430db9d4d9fe7cce7 /kernel/rcu/tree.c
parenta58163d8ca2c8d288ee9f95989712f98473a5ac2 (diff)
rcu: Make expedited GPs correctly handle hardware CPU insertion
The update of the ->expmaskinitnext and of ->ncpus are unsynchronized, with the value of ->ncpus being incremented long before the corresponding ->expmaskinitnext mask is updated. If an RCU expedited grace period sees ->ncpus change, it will update the ->expmaskinit masks from the new ->expmaskinitnext masks. But it is possible that ->ncpus has already been updated, but the ->expmaskinitnext masks still have their old values. For the current expedited grace period, no harm done. The CPU could not have been online before the grace period started, so there is no need to wait for its non-existent pre-existing readers. But the next RCU expedited grace period is in a world of hurt. The value of ->ncpus has already been updated, so this grace period will assume that the ->expmaskinitnext masks have not changed. But they have, and they won't be taken into account until the next never-been-online CPU comes online. This means that RCU will be ignoring some CPUs that it should be paying attention to. The solution is to update ->ncpus and ->expmaskinitnext while holding the ->lock for the rcu_node structure containing the ->expmaskinitnext mask. Because smp_store_release() is now used to update ->ncpus and smp_load_acquire() is now used to locklessly read it, if the expedited grace period sees ->ncpus change, then the updating CPU has to already be holding the corresponding ->lock. Therefore, when the expedited grace period later acquires that ->lock, it is guaranteed to see the new value of ->expmaskinitnext. On the other hand, if the expedited grace period loads ->ncpus just before an update, earlier full memory barriers guarantee that the incoming CPU isn't far enough along to be running any RCU readers. This commit therefore makes the required change. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/rcu/tree.c')
-rw-r--r--kernel/rcu/tree.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 9bb5dff50815..f431114bc06a 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -3684,8 +3684,6 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
3684 */ 3684 */
3685 rnp = rdp->mynode; 3685 rnp = rdp->mynode;
3686 raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */ 3686 raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
3687 if (!rdp->beenonline)
3688 WRITE_ONCE(rsp->ncpus, READ_ONCE(rsp->ncpus) + 1);
3689 rdp->beenonline = true; /* We have now been online. */ 3687 rdp->beenonline = true; /* We have now been online. */
3690 rdp->gpnum = rnp->completed; /* Make CPU later note any new GP. */ 3688 rdp->gpnum = rnp->completed; /* Make CPU later note any new GP. */
3691 rdp->completed = rnp->completed; 3689 rdp->completed = rnp->completed;
@@ -3789,6 +3787,8 @@ void rcu_cpu_starting(unsigned int cpu)
3789{ 3787{
3790 unsigned long flags; 3788 unsigned long flags;
3791 unsigned long mask; 3789 unsigned long mask;
3790 int nbits;
3791 unsigned long oldmask;
3792 struct rcu_data *rdp; 3792 struct rcu_data *rdp;
3793 struct rcu_node *rnp; 3793 struct rcu_node *rnp;
3794 struct rcu_state *rsp; 3794 struct rcu_state *rsp;
@@ -3799,9 +3799,15 @@ void rcu_cpu_starting(unsigned int cpu)
3799 mask = rdp->grpmask; 3799 mask = rdp->grpmask;
3800 raw_spin_lock_irqsave_rcu_node(rnp, flags); 3800 raw_spin_lock_irqsave_rcu_node(rnp, flags);
3801 rnp->qsmaskinitnext |= mask; 3801 rnp->qsmaskinitnext |= mask;
3802 oldmask = rnp->expmaskinitnext;
3802 rnp->expmaskinitnext |= mask; 3803 rnp->expmaskinitnext |= mask;
3804 oldmask ^= rnp->expmaskinitnext;
3805 nbits = bitmap_weight(&oldmask, BITS_PER_LONG);
3806 /* Allow lockless access for expedited grace periods. */
3807 smp_store_release(&rsp->ncpus, rsp->ncpus + nbits); /* ^^^ */
3803 raw_spin_unlock_irqrestore_rcu_node(rnp, flags); 3808 raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
3804 } 3809 }
3810 smp_mb(); /* Ensure RCU read-side usage follows above initialization. */
3805} 3811}
3806 3812
3807#ifdef CONFIG_HOTPLUG_CPU 3813#ifdef CONFIG_HOTPLUG_CPU