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>
This commit is contained in:
parent
a58163d8ca
commit
313517fc44
|
@ -3684,8 +3684,6 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||||
*/
|
*/
|
||||||
rnp = rdp->mynode;
|
rnp = rdp->mynode;
|
||||||
raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
|
raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
|
||||||
if (!rdp->beenonline)
|
|
||||||
WRITE_ONCE(rsp->ncpus, READ_ONCE(rsp->ncpus) + 1);
|
|
||||||
rdp->beenonline = true; /* We have now been online. */
|
rdp->beenonline = true; /* We have now been online. */
|
||||||
rdp->gpnum = rnp->completed; /* Make CPU later note any new GP. */
|
rdp->gpnum = rnp->completed; /* Make CPU later note any new GP. */
|
||||||
rdp->completed = rnp->completed;
|
rdp->completed = rnp->completed;
|
||||||
|
@ -3789,6 +3787,8 @@ void rcu_cpu_starting(unsigned int cpu)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long mask;
|
unsigned long mask;
|
||||||
|
int nbits;
|
||||||
|
unsigned long oldmask;
|
||||||
struct rcu_data *rdp;
|
struct rcu_data *rdp;
|
||||||
struct rcu_node *rnp;
|
struct rcu_node *rnp;
|
||||||
struct rcu_state *rsp;
|
struct rcu_state *rsp;
|
||||||
|
@ -3799,9 +3799,15 @@ void rcu_cpu_starting(unsigned int cpu)
|
||||||
mask = rdp->grpmask;
|
mask = rdp->grpmask;
|
||||||
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
||||||
rnp->qsmaskinitnext |= mask;
|
rnp->qsmaskinitnext |= mask;
|
||||||
|
oldmask = rnp->expmaskinitnext;
|
||||||
rnp->expmaskinitnext |= mask;
|
rnp->expmaskinitnext |= mask;
|
||||||
|
oldmask ^= rnp->expmaskinitnext;
|
||||||
|
nbits = bitmap_weight(&oldmask, BITS_PER_LONG);
|
||||||
|
/* Allow lockless access for expedited grace periods. */
|
||||||
|
smp_store_release(&rsp->ncpus, rsp->ncpus + nbits); /* ^^^ */
|
||||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||||
}
|
}
|
||||||
|
smp_mb(); /* Ensure RCU read-side usage follows above initialization. */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HOTPLUG_CPU
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
|
|
@ -73,7 +73,7 @@ static void sync_exp_reset_tree_hotplug(struct rcu_state *rsp)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long mask;
|
unsigned long mask;
|
||||||
unsigned long oldmask;
|
unsigned long oldmask;
|
||||||
int ncpus = READ_ONCE(rsp->ncpus);
|
int ncpus = smp_load_acquire(&rsp->ncpus); /* Order against locking. */
|
||||||
struct rcu_node *rnp;
|
struct rcu_node *rnp;
|
||||||
struct rcu_node *rnp_up;
|
struct rcu_node *rnp_up;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue