The purpose of rcu_dynticks_eqs_online() is to adjust the ->dynticks counter of an incoming CPU when required. It is currently invoked from rcutree_prepare_cpu(), which runs before the incoming CPU is running, and thus on some other CPU. This makes the per-CPU accesses in rcu_dynticks_eqs_online() iffy at best, and it all "works" only because the running CPU cannot possibly be in dyntick-idle mode, which means that rcu_dynticks_eqs_online() never has any effect. It is currently OK for rcu_dynticks_eqs_online() to have no effect, but only because the CPU-offline process just happens to leave ->dynticks in the correct state. After all, if ->dynticks were in the wrong state on a just-onlined CPU, rcutorture would complain bitterly the next time that CPU went idle, at least in kernels built with CONFIG_RCU_EQS_DEBUG=y, for example, those built by rcutorture scenario TREE04. One could argue that this means that rcu_dynticks_eqs_online() is unnecessary, however, removing it would make the CPU-online process vulnerable to slight changes in the CPU-offline process. One could also ask why it is safe to move the rcu_dynticks_eqs_online() call so late in the CPU-online process. Indeed, there was a time when it would not have been safe, which does much to explain its current location. However, the marking of a CPU as online from an RCU perspective has long since moved from rcutree_prepare_cpu() to rcu_cpu_starting(), and all that is required is that ->dynticks be set correctly by the time that the CPU is marked as online from an RCU perspective. After all, the RCU grace-period kthread does not check to see if offline CPUs are also idle. (In case you were curious, this is one reason why there is quiescent-state reporting as part of the offlining process.) This commit therefore moves the call to rcu_dynticks_eqs_online() from rcutree_prepare_cpu() to rcu_cpu_starting(), this latter being guaranteed to be running on the incoming CPU. The call to this function must of course be placed before this rcu_cpu_starting() announces this CPU's presence to RCU.
Unified · Split
| @@ -4129,7 +4129,6 @@ int rcutree_prepare_cpu(unsigned int cpu) · 4128 unmodified lines | |||
| 4129 | rdp->n_force_qs_snap = READ_ONCE(rcu_state.n_force_qs); | 4129 | rdp->n_force_qs_snap = READ_ONCE(rcu_state.n_force_qs); |
| 4130 | rdp->blimit = blimit; | 4130 | rdp->blimit = blimit; |
| 4131 | rdp->dynticks_nesting = 1; /* CPU not up, no tearing. */ | 4131 | rdp->dynticks_nesting = 1; /* CPU not up, no tearing. */ |
| 4132 | rcu_dynticks_eqs_online(); | ||
| 4133 | raw_spin_unlock_rcu_node(rnp); /* irqs remain disabled. */ | 4132 | raw_spin_unlock_rcu_node(rnp); /* irqs remain disabled. */ |
| 4134 | 4133 | ||
| 4135 | /* | 4134 | /* |
| @@ -4249,6 +4248,7 @@ void rcu_cpu_starting(unsigned int cpu) · 113 unmodified lines | |||
| 4249 | mask = rdp->grpmask; | 4248 | mask = rdp->grpmask; |
| 4250 | WRITE_ONCE(rnp->ofl_seq, rnp->ofl_seq + 1); | 4249 | WRITE_ONCE(rnp->ofl_seq, rnp->ofl_seq + 1); |
| 4251 | WARN_ON_ONCE(!(rnp->ofl_seq & 0x1)); | 4250 | WARN_ON_ONCE(!(rnp->ofl_seq & 0x1)); |
| 4251 | rcu_dynticks_eqs_online(); | ||
| 4252 | smp_mb(); // Pair with rcu_gp_cleanup()'s ->ofl_seq barrier(). | 4252 | smp_mb(); // Pair with rcu_gp_cleanup()'s ->ofl_seq barrier(). |
| 4253 | raw_spin_lock_irqsave_rcu_node(rnp, flags); | 4253 | raw_spin_lock_irqsave_rcu_node(rnp, flags); |
| 4254 | WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext | mask); | 4254 | WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext | mask); |