Consolidate RCU dyntick-idle counter manipulation
cover Consolidate RCU dyntick-idle counter manipulation
Hello!
The first four patches of this series consolidate RCU's dyntick-idle counter manipulations into a set of access functions. These are currently queued for 4.12 (merge window after next). The last patch in this series uses the bottom bit of RCU's dyntick-idle counter to track a CPU's state, and has been proposed to track the need to flush the local CPU's TLB. This last patch will not be pushed until it has a use.
The patches are as follows:
1. Abstract the dynticks momentary-idle operation.
2. Abstract the dynticks snapshot operation.
3. Abstract dynticks extended quiescent state enter/exit operations.
4. Abstract extended quiescent state determination.
5. Maintain special bits at bottom of ->dynticks counter, which
might be used to track a given CPU's need to flush TLBs.
Thanx, Paul
------------------------------------------------------------------------
include/linux/rcutiny.h | 11 + kernel/rcu/tree.c | 263 ++++++++++++++++++++++++++++++++++------------- kernel/rcu/tree.h | 3 kernel/rcu/tree_exp.h | 12 -- kernel/rcu/tree_plugin.h | 2 kernel/rcu/tree_trace.c | 2 6 files changed, 216 insertions(+), 77 deletions(-)
PATCH 1/5 rcu: Abstract the dynticks momentary-idle operation
This commit is the first step towards full abstraction of all accesses to the ->dynticks counter, implementing the previously open-coded atomic add of two in a new rcu_dynticks_momentary_idle() function. This abstraction will ease changes to the ->dynticks counter operation.
--- kernel/rcu/tree.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-)
kernel/rcu/tree.c +14 -5
@@ -278,6 +278,19 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = {#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ }; +/* +* Do a double-increment of the ->dynticks counter to emulate a +* momentary idle-CPU quiescent state. +*/ +static void rcu_dynticks_momentary_idle (void ) +{ +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); +int special =atomic_add_return (2 , &rdtp->dynticks); + +/* It is illegal to call this from idle state. */ +WARN_ON_ONCE (!(special &0x1 )); +} + DEFINE_PER_CPU_SHARED_ALIGNED(unsigned long, rcu_qs_ctr);EXPORT_PER_CPU_SYMBOL_GPL (rcu_qs_ctr);
@@ -297,7 +310,6 @@ EXPORT_PER_CPU_SYMBOL_GPL(rcu_qs_ctr);static void rcu_momentary_dyntick_idle (void ) {struct rcu_data *rdp; -struct rcu_dynticks *rdtp;int resched_mask;struct rcu_state *rsp;
@@ -324,10 +336,7 @@ static void rcu_momentary_dyntick_idle(void)* quiescent state, with no need for this CPU to do anything * further. */ - rdtp =this_cpu_ptr (&rcu_dynticks); -smp_mb__before_atomic ();/* Earlier stuff before QS. */ -atomic_add (2 , &rdtp->dynticks);/* QS. */ -smp_mb__after_atomic ();/* Later stuff after QS. */ +rcu_dynticks_momentary_idle ();break ; } }
Signature · 1 line
2.5.2
PATCH 2/5 rcu: Abstract the dynticks snapshot operation
In reply to Paul E. McKenneyThis commit is the second step towards full abstraction of all accesses to the ->dynticks counter, implementing the previously open-coded atomic add of zero in a new rcu_dynticks_snap() function. This abstraction will ease changes o the ->dynticks counter operation.
--- kernel/rcu/tree.c | 19 ++++++++++++++++--- kernel/rcu/tree_exp.h | 6 ++---- 2 files changed, 18 insertions(+), 7 deletions(-)
kernel/rcu/tree.c +16 -3
@@ -279,6 +279,17 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { };/* +* Snapshot the ->dynticks counter with full ordering so as to allow +* stable comparison of this counter with past and future snapshots. +*/ +static int rcu_dynticks_snap (struct rcu_dynticks *rdtp) +{ +int snap =atomic_add_return (0 , &rdtp->dynticks); + +return snap; +} + +/* * Do a double-increment of the ->dynticks counter to emulate a * momentary idle-CPU quiescent state. */
@@ -1046,7 +1057,9 @@ void rcu_nmi_exit(void)*/ bool notrace __rcu_is_watching(void) { -return atomic_read (this_cpu_ptr (&rcu_dynticks.dynticks)) &0x1 ; +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); + +return atomic_read (&rdtp->dynticks) &0x1 ; }/**
@@ -1129,7 +1142,7 @@ static int rcu_is_cpu_rrupt_from_idle(void)static int dyntick_save_progress_counter (struct rcu_data *rdp,bool *isidle,unsigned long *maxj) { - rdp->dynticks_snap =atomic_add_return (0 , &rdp->dynticks->dynticks); + rdp->dynticks_snap =rcu_dynticks_snap (rdp->dynticks);rcu_sysidle_check_cpu (rdp, isidle, maxj);if ((rdp->dynticks_snap &0x1 ) ==0 ) {trace_rcu_fqs (rdp->rsp->name, rdp->gpnum, rdp->cpu,TPS ("dti" ));
@@ -1154,7 +1167,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp,int *rcrmp;unsigned int snap; - curr = (unsigned int )atomic_add_return (0 , &rdp->dynticks->dynticks); + curr = (unsigned int )rcu_dynticks_snap (rdp->dynticks); snap = (unsigned int )rdp->dynticks_snap;/*
kernel/rcu/tree_exp.h +2 -4
@@ -356,10 +356,9 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp, mask_ofl_test =0 ;for_each_leaf_node_possible_cpu (rnp, cpu) {struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); -struct rcu_dynticks *rdtp = &per_cpu (rcu_dynticks, cpu); rdp->exp_dynticks_snap = -atomic_add_return (0 , &rdtp->dynticks); +rcu_dynticks_snap (rdp->dynticks);if (raw_smp_processor_id () == cpu || !(rdp->exp_dynticks_snap &0x1 ) || !(rnp->qsmaskinitnext & rdp->grpmask))
@@ -380,12 +379,11 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,for_each_leaf_node_possible_cpu (rnp, cpu) {unsigned long mask =leaf_node_cpu_bit (rnp, cpu);struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); -struct rcu_dynticks *rdtp = &per_cpu (rcu_dynticks, cpu);if (!(mask_ofl_ipi & mask))continue ; retry_ipi: -if (atomic_add_return (0 , &rdtp->dynticks) != +if (rcu_dynticks_snap (rdp->dynticks) != rdp->exp_dynticks_snap) { mask_ofl_test |= mask;continue ;
Signature · 1 line
2.5.2
PATCH 3/5 rcu: Abstract dynticks extended quiescent state enter/exit operations
In reply to Paul E. McKenneyThis commit is the third step towards full abstraction of all accesses to the ->dynticks counter, implementing the previously open-coded atomic add of 1 and entry checks in a new rcu_dynticks_eqs_enter() function, and the same but with exit checks in a new rcu_dynticks_eqs_exit() function. This abstraction will ease changes to the ->dynticks counter operation.
--- kernel/rcu/tree.c | 92 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 26 deletions(-)
kernel/rcu/tree.c +66 -26
@@ -279,6 +279,65 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { };/* +* Record entry into an extended quiescent state. This is only to be +* called when not already in an extended quiescent state. +*/ +static void rcu_dynticks_eqs_enter (void ) +{ +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); + +/* +* CPUs seeing atomic_inc() must see prior RCU read-side critical +* sections, and we also must force ordering with the next idle +* sojourn. +*/ +smp_mb__before_atomic ();/* See above. */ +atomic_inc (&rdtp->dynticks); +smp_mb__after_atomic ();/* See above. */ +WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && +atomic_read (&rdtp->dynticks) &0x1 ); +} + +/* +* Record exit from an extended quiescent state. This is only to be +* called from an extended quiescent state. +*/ +static void rcu_dynticks_eqs_exit (void ) +{ +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); + +/* +* CPUs seeing atomic_inc() must see prior idle sojourns, +* and we also must force ordering with the next RCU read-side +* critical section. +*/ +smp_mb__before_atomic ();/* See above. */ +atomic_inc (&rdtp->dynticks); +smp_mb__after_atomic ();/* See above. */ +WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && + !(atomic_read (&rdtp->dynticks) &0x1 )); +} + +/* +* Reset the current CPU's ->dynticks counter to indicate that the +* newly onlined CPU is no longer in an extended quiescent state. +* This will either leave the counter unchanged, or increment it +* to the next non-quiescent value. +* +* The non-atomic test/increment sequence works because the upper bits +* of the ->dynticks counter are manipulated only by the corresponding CPU, +* or when the corresponding CPU is offline. +*/ +static void rcu_dynticks_eqs_online (void ) +{ +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); + +if (atomic_read (&rdtp->dynticks) &0x1 ) +return ; +atomic_add (0x1 , &rdtp->dynticks); +} + +/* * Snapshot the ->dynticks counter with full ordering so as to allow * stable comparison of this counter with past and future snapshots. */
@@ -690,7 +749,7 @@ static void rcu_eqs_enter_common(long long oldval, bool user) {struct rcu_state *rsp;struct rcu_data *rdp; -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); +struct rcu_dynticks __maybe_unused *rdtp =this_cpu_ptr (&rcu_dynticks);trace_rcu_dyntick (TPS ("Start" ), oldval, rdtp->dynticks_nesting); if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
@@ -709,12 +768,7 @@ static void rcu_eqs_enter_common(long long oldval, bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -/* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ -smp_mb__before_atomic ();/* See above. */ -atomic_inc (&rdtp->dynticks); -smp_mb__after_atomic ();/* Force ordering with next sojourn. */ -WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && -atomic_read (&rdtp->dynticks) &0x1 ); +rcu_dynticks_eqs_enter ();rcu_dynticks_task_enter ();/*
@@ -843,15 +897,10 @@ void rcu_irq_exit_irqson(void)*/ static void rcu_eqs_exit_common (long long oldval,int user) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); +struct rcu_dynticks __maybe_unused *rdtp =this_cpu_ptr (&rcu_dynticks);rcu_dynticks_task_exit (); -smp_mb__before_atomic ();/* Force ordering w/previous sojourn. */ -atomic_inc (&rdtp->dynticks); -/* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ -smp_mb__after_atomic ();/* See above. */ -WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && - !(atomic_read (&rdtp->dynticks) &0x1 )); +rcu_dynticks_eqs_exit ();rcu_cleanup_after_idle ();trace_rcu_dyntick (TPS ("End" ), oldval, rdtp->dynticks_nesting); if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
@@ -998,11 +1047,7 @@ void rcu_nmi_enter(void)* period (observation due to Andy Lutomirski). */ if (!(atomic_read (&rdtp->dynticks) &0x1 )) { -smp_mb__before_atomic ();/* Force delay from prior write. */ -atomic_inc (&rdtp->dynticks); -/* atomic_inc() before later RCU read-side crit sects */ -smp_mb__after_atomic ();/* See above. */ -WARN_ON_ONCE (!(atomic_read (&rdtp->dynticks) &0x1 )); +rcu_dynticks_eqs_exit (); incby =1 ; } rdtp->dynticks_nmi_nesting += incby;
@@ -1040,11 +1085,7 @@ void rcu_nmi_exit(void)/* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ rdtp->dynticks_nmi_nesting =0 ; -/* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ -smp_mb__before_atomic ();/* See above. */ -atomic_inc (&rdtp->dynticks); -smp_mb__after_atomic ();/* Force delay to next write. */ -WARN_ON_ONCE (atomic_read (&rdtp->dynticks) &0x1 ); +rcu_dynticks_eqs_enter (); }/**
@@ -3797,8 +3838,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)init_callback_list (rdp);/* Re-enable callbacks on this CPU. */ rdp->dynticks->dynticks_nesting =DYNTICK_TASK_EXIT_IDLE ;rcu_sysidle_init_percpu_data (rdp->dynticks); -atomic_set (&rdp->dynticks->dynticks, - (atomic_read (&rdp->dynticks->dynticks) & ~0x1 ) +1 ); +rcu_dynticks_eqs_online ();raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ /*
Signature · 1 line
2.5.2
Re: [PATCH tip/core/rcu 3/5] rcu: Abstract dynticks extended quiescent state enter/exit operations
Hi Paul,
Quoted 24 lines · Paul E. McKenney
This commit is the third step towards full abstraction of all accesses to the ->dynticks counter, implementing the previously open-coded atomic add of 1 and entry checks in a new
rcu_dynticks_eqs_enter()function, and the same but with exit checks in a newrcu_dynticks_eqs_exit()function. This abstraction will ease changes to the ->dynticks counter operation.Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 92 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 26 deletions(-)kernel/rcu/tree.c +5
-0@@ -279,6 +279,65 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { };/* +* Record entry into an extended quiescent state. This is only to be +* called when not already in an extended quiescent state. +*/ +static void rcu_dynticks_eqs_enter (void ) +{
kernel/rcu/tree.c static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { · line 296
Quoted 10 lines
kernel/rcu/tree.c +10
-0+struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); + +/* +* CPUs seeing atomic_inc() must see prior RCU read-side critical +* sections, and we also must force ordering with the next idle +* sojourn. +*/ +smp_mb__before_atomic ();/* See above. */ +atomic_inc (&rdtp->dynticks); +smp_mb__after_atomic ();/* See above. */
How about replacing three lines above with:
(void)atomic_inc_return(&rdtp->dynticks);
?
*_return should have the full barrier semantics.
Quoted 10 lines
kernel/rcu/tree.c +10
-0+WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && +atomic_read (&rdtp->dynticks) &0x1 ); +} + +/* +* Record exit from an extended quiescent state. This is only to be +* called from an extended quiescent state. +*/ +static void rcu_dynticks_eqs_exit (void ) +{
kernel/rcu/tree.c static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { · line 316
Quoted 10 lines
kernel/rcu/tree.c +10
-0+struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); + +/* +* CPUs seeing atomic_inc() must see prior idle sojourns, +* and we also must force ordering with the next RCU read-side +* critical section. +*/ +smp_mb__before_atomic ();/* See above. */ +atomic_inc (&rdtp->dynticks); +smp_mb__after_atomic ();/* See above. */
Ditto.
Regards,
Boqun
Quoted 107 lines
kernel/rcu/tree.c +31
-26+WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && + !(atomic_read (&rdtp->dynticks) &0x1 )); +} + +/* +* Reset the current CPU's ->dynticks counter to indicate that the +* newly onlined CPU is no longer in an extended quiescent state. +* This will either leave the counter unchanged, or increment it +* to the next non-quiescent value. +* +* The non-atomic test/increment sequence works because the upper bits +* of the ->dynticks counter are manipulated only by the corresponding CPU, +* or when the corresponding CPU is offline. +*/ +static void rcu_dynticks_eqs_online (void ) +{ +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); + +if (atomic_read (&rdtp->dynticks) &0x1 ) +return ; +atomic_add (0x1 , &rdtp->dynticks); +} + +/* * Snapshot the ->dynticks counter with full ordering so as to allow * stable comparison of this counter with past and future snapshots. */ @@ -690,7 +749,7 @@ static void rcu_eqs_enter_common(long long oldval, bool user) {struct rcu_state *rsp;struct rcu_data *rdp; -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); +struct rcu_dynticks __maybe_unused *rdtp =this_cpu_ptr (&rcu_dynticks);trace_rcu_dyntick (TPS ("Start" ), oldval, rdtp->dynticks_nesting); if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&@@ -709,12 +768,7 @@ static void rcu_eqs_enter_common(long long oldval, bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -/* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ -smp_mb__before_atomic ();/* See above. */ -atomic_inc (&rdtp->dynticks); -smp_mb__after_atomic ();/* Force ordering with next sojourn. */ -WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && -atomic_read (&rdtp->dynticks) &0x1 ); +rcu_dynticks_eqs_enter ();rcu_dynticks_task_enter ();/* @@ -843,15 +897,10 @@ void rcu_irq_exit_irqson(void)*/ static void rcu_eqs_exit_common (long long oldval,int user) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); +struct rcu_dynticks __maybe_unused *rdtp =this_cpu_ptr (&rcu_dynticks);rcu_dynticks_task_exit (); -smp_mb__before_atomic ();/* Force ordering w/previous sojourn. */ -atomic_inc (&rdtp->dynticks); -/* CPUs seeing atomic_inc() must see later RCU read-side crit sects */ -smp_mb__after_atomic ();/* See above. */ -WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && - !(atomic_read (&rdtp->dynticks) &0x1 )); +rcu_dynticks_eqs_exit ();rcu_cleanup_after_idle ();trace_rcu_dyntick (TPS ("End" ), oldval, rdtp->dynticks_nesting); if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&@@ -998,11 +1047,7 @@ void rcu_nmi_enter(void)* period (observation due to Andy Lutomirski). */ if (!(atomic_read (&rdtp->dynticks) &0x1 )) { -smp_mb__before_atomic ();/* Force delay from prior write. */ -atomic_inc (&rdtp->dynticks); -/* atomic_inc() before later RCU read-side crit sects */ -smp_mb__after_atomic ();/* See above. */ -WARN_ON_ONCE (!(atomic_read (&rdtp->dynticks) &0x1 )); +rcu_dynticks_eqs_exit (); incby =1 ; } rdtp->dynticks_nmi_nesting += incby;@@ -1040,11 +1085,7 @@ void rcu_nmi_exit(void)/* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ rdtp->dynticks_nmi_nesting =0 ; -/* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */ -smp_mb__before_atomic ();/* See above. */ -atomic_inc (&rdtp->dynticks); -smp_mb__after_atomic ();/* Force delay to next write. */ -WARN_ON_ONCE (atomic_read (&rdtp->dynticks) &0x1 ); +rcu_dynticks_eqs_enter (); }/** @@ -3797,8 +3838,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)init_callback_list (rdp);/* Re-enable callbacks on this CPU. */ rdp->dynticks->dynticks_nesting =DYNTICK_TASK_EXIT_IDLE ;rcu_sysidle_init_percpu_data (rdp->dynticks); -atomic_set (&rdp->dynticks->dynticks, - (atomic_read (&rdp->dynticks->dynticks) & ~0x1 ) +1 ); +rcu_dynticks_eqs_online ();raw_spin_unlock_rcu_node (rnp);/* irqs remain disabled. */ /* Signature · 1 line
2.5.2
PATCH 4/5 rcu: Abstract extended quiescent state determination
In reply to Paul E. McKenneyThis commit is the fourth step towards full abstraction of all accesses to the ->dynticks counter, implementing previously open-coded checks and comparisons in new rcu_dynticks_in_eqs() and rcu_dynticks_in_eqs_since() functions. This abstraction will ease changes to the ->dynticks counter operation.
--- include/linux/rcutiny.h | 6 ++++++ kernel/rcu/tree.c | 52 +++++++++++++++++++++++++++++++++++------------- kernel/rcu/tree.h | 2 ++ kernel/rcu/tree_exp.h | 6 +++--- kernel/rcu/tree_plugin.h | 2 +- kernel/rcu/tree_trace.c | 2 +- 6 files changed, 51 insertions(+), 19 deletions(-)
include/linux/rcutiny.h +6 -0
@@ -27,6 +27,12 @@#include <linux/cache.h> +struct rcu_dynticks ; +static inline int rcu_dynticks_snap (struct rcu_dynticks *rdtp) +{ +return 0 ; +} +static inline unsigned long get_state_synchronize_rcu (void ) {return 0 ;
kernel/rcu/tree.c +38 -14
110 lines not on this page · raw message
kernel/rcu/tree.h +2 -0
@@ -595,6 +595,8 @@ extern struct rcu_state rcu_bh_state;extern struct rcu_state rcu_preempt_state;#endif /* #ifdef CONFIG_PREEMPT_RCU */ +int rcu_dynticks_snap (struct rcu_dynticks *rdtp); +#ifdef CONFIG_RCU_BOOST DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status);DECLARE_PER_CPU (int, rcu_cpu_kthread_cpu);
kernel/rcu/tree_exp.h +3 -3
@@ -360,7 +360,7 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp, rdp->exp_dynticks_snap =rcu_dynticks_snap (rdp->dynticks);if (raw_smp_processor_id () == cpu || - !(rdp->exp_dynticks_snap &0x1 ) || +rcu_dynticks_in_eqs (rdp->exp_dynticks_snap) || !(rnp->qsmaskinitnext & rdp->grpmask)) mask_ofl_test |= rdp->grpmask; }
@@ -383,8 +383,8 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,if (!(mask_ofl_ipi & mask))continue ; retry_ipi: -if (rcu_dynticks_snap (rdp->dynticks) != - rdp->exp_dynticks_snap) { +if (rcu_dynticks_in_eqs_since (rdp->dynticks, + rdp->exp_dynticks_snap)) { mask_ofl_test |= mask;continue ; }
kernel/rcu/tree_plugin.h +1 -1
@@ -1643,7 +1643,7 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu)"o." [!!(rdp->grpmask & rdp->mynode->qsmaskinit)],"N." [!!(rdp->grpmask & rdp->mynode->qsmaskinitnext)], ticks_value, ticks_title, -atomic_read (&rdtp->dynticks) &0xfff , +rcu_dynticks_snap (rdtp) &0xfff , rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting, rdp->softirq_snap,kstat_softirqs_cpu (RCU_SOFTIRQ , cpu), READ_ONCE(rsp->n_force_qs) - rsp->n_force_qs_gpstart,
kernel/rcu/tree_trace.c +1 -1
@@ -124,7 +124,7 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) rdp->rcu_qs_ctr_snap ==per_cpu (rcu_qs_ctr, rdp->cpu), rdp->core_needs_qs);seq_printf (m," dt=%d/%llx/%d df=%lu" , -atomic_read (&rdp->dynticks->dynticks), +rcu_dynticks_snap (rdp->dynticks), rdp->dynticks->dynticks_nesting, rdp->dynticks->dynticks_nmi_nesting, rdp->dynticks_fqs);
Signature · 1 line
2.5.2
PATCH 5/5 rcu: Maintain special bits at bottom of ->dynticks counter
In reply to Paul E. McKenneyCurrently, IPIs are used to force other CPUs to invalidate their TLBs in response to a kernel virtual-memory mapping change. This works, but degrades both battery lifetime (for idle CPUs) and real-time response (for nohz_full CPUs), and in addition results in unnecessary IPIs due to the fact that CPUs executing in usermode are unaffected by stale kernel mappings. It would be better to cause a CPU executing in usermode to wait until it is entering kernel mode to do the flush, first to avoid interrupting usemode tasks and second to handle multiple flush requests with a single flush in the case of a long-running user task.
This commit therefore reserves a bit at the bottom of the ->dynticks counter, which is checked upon exit from extended quiescent states. If it is set, it is cleared and then a new rcu_eqs_special_exit() macro is invoked, which, if not supplied, is an empty single-pass do-while loop. If this bottom bit is set on -entry- to an extended quiescent state, then a WARN_ON_ONCE() triggers.
This bottom bit may be set using a new rcu_eqs_special_set() function, which returns true if the bit was set, or false if the CPU turned out to not be in an extended quiescent state. Please note that this function refuses to set the bit for a non-nohz_full CPU when that CPU is executing in usermode because usermode execution is tracked by RCU as a dyntick-idle extended quiescent state only for nohz_full CPUs.
--- include/linux/rcutiny.h | 5 +++ kernel/rcu/tree.c | 81 +++++++++++++++++++++++++++++++++++++------------ kernel/rcu/tree.h | 1 + 3 files changed, 67 insertions(+), 20 deletions(-)
include/linux/rcutiny.h +5 -0
@@ -33,6 +33,11 @@ static inline int rcu_dynticks_snap(struct rcu_dynticks *rdtp)return 0 ; } +static inline bool rcu_eqs_special_set (int cpu) +{ +return false;/* Never flag non-existent other CPUs! */ +} +static inline unsigned long get_state_synchronize_rcu (void ) {return 0 ;
kernel/rcu/tree.c +61 -20
144 lines not on this page · raw message
kernel/rcu/tree.h +1 -0
@@ -596,6 +596,7 @@ extern struct rcu_state rcu_preempt_state;#endif /* #ifdef CONFIG_PREEMPT_RCU */ int rcu_dynticks_snap (struct rcu_dynticks *rdtp); +bool rcu_eqs_special_set (int cpu);#ifdef CONFIG_RCU_BOOST DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status);
Signature · 1 line
2.5.2
Re: [PATCH tip/core/rcu 5/5] rcu: Maintain special bits at bottom of ->dynticks counter
Quoted 85 lines · Paul E. McKenney
Currently, IPIs are used to force other CPUs to invalidate their TLBs in response to a kernel virtual-memory mapping change. This works, but degrades both battery lifetime (for idle CPUs) and real-time response (for
nohz_fullCPUs), and in addition results in unnecessary IPIs due to the fact that CPUs executing in usermode are unaffected by stale kernel mappings. It would be better to cause a CPU executing in usermode to wait until it is entering kernel mode to do the flush, first to avoid interrupting usemode tasks and second to handle multiple flush requests with a single flush in the case of a long-running user task.This commit therefore reserves a bit at the bottom of the ->dynticks counter, which is checked upon exit from extended quiescent states. If it is set, it is cleared and then a new
rcu_eqs_special_exit()macro is invoked, which, if not supplied, is an empty single-pass do-while loop. If this bottom bit is set on -entry- to an extended quiescent state, then aWARN_ON_ONCE()triggers.This bottom bit may be set using a new
rcu_eqs_special_set()function, which returns true if the bit was set, or false if the CPU turned out to not be in an extended quiescent state. Please note that this function refuses to set the bit for a non-nohz_full CPU when that CPU is executing in usermode because usermode execution is tracked by RCU as a dyntick-idle extended quiescent state only fornohz_fullCPUs.Reported-by Andy Lutomirski <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- include/linux/rcutiny.h | 5 +++ kernel/rcu/tree.c | 81 +++++++++++++++++++++++++++++++++++++------------ kernel/rcu/tree.h | 1 + 3 files changed, 67 insertions(+), 20 deletions(-)include/linux/rcutiny.h +5
-0@@ -33,6 +33,11 @@ static inline int rcu_dynticks_snap(struct rcu_dynticks *rdtp)return 0 ; } +static inline bool rcu_eqs_special_set (int cpu) +{ +return false;/* Never flag non-existent other CPUs! */ +} +static inline unsigned long get_state_synchronize_rcu (void ) {return 0 ;kernel/rcu/tree.c +14
-4@@ -269,9 +269,19 @@ void rcu_bh_qs(void) static DEFINE_PER_CPU(int, rcu_sched_qs_mask); +/* +* Steal a bit from the bottom of ->dynticks for idle entry/exit +* control. Initially this is for TLB flushing. +*/ +#define RCU_DYNTICK_CTRL_MASK 0x1 +#define RCU_DYNTICK_CTRL_CTR (RCU_DYNTICK_CTRL_MASK + 1) +#ifndef rcu_eqs_special_exit +#define rcu_eqs_special_exit () do { } while (0) +#endif + static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { .dynticks_nesting =DYNTICK_TASK_EXIT_IDLE , - .dynticks =ATOMIC_INIT (1 ), + .dynticks =ATOMIC_INIT (RCU_DYNTICK_CTRL_CTR ), #ifdef CONFIG_NO_HZ_FULL_SYSIDLE .dynticks_idle_nesting =DYNTICK_TASK_NEST_VALUE , .dynticks_idle = ATOMIC_INIT(1),@@ -285,17 +295,20 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = {static void rcu_dynticks_eqs_enter (void ) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); +int seq;/* -* CPUs seeing atomic_inc() must see prior RCU read-side critical -* sections, and we also must force ordering with the next idle -* sojourn. +* CPUs seeing atomic_inc_return() must see prior RCU read-side +* critical sections, and we also must force ordering with the
kernel/rcu/tree.c static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { · line 309
Quoted 10 lines
kernel/rcu/tree.c +6
-3+* next idle sojourn. */ -smp_mb__before_atomic ();/* See above. */ -atomic_inc (&rdtp->dynticks); -smp_mb__after_atomic ();/* See above. */ + seq =atomic_add_return (RCU_DYNTICK_CTRL_CTR , &rdtp->dynticks); +/* Better be in an extended quiescent state! */ +WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && + (seq &RCU_DYNTICK_CTRL_CTR )); +/* Better not have special action (TLB flush) pending! */
Ah.. you did the clean-up here ;-)
Never mind my previous comment on patch #3 ;-)
Regards,
Boqun
Quoted 124 lines
kernel/rcu/tree.c +41
-13WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && -atomic_read (&rdtp->dynticks) &0x1 ); + (seq &RCU_DYNTICK_CTRL_MASK )); }/* @@ -305,17 +318,22 @@ static void rcu_dynticks_eqs_enter(void)static void rcu_dynticks_eqs_exit (void ) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); +int seq;/* -* CPUs seeing atomic_inc() must see prior idle sojourns, +* CPUs seeing atomic_inc_return() must see prior idle sojourns, * and we also must force ordering with the next RCU read-side * critical section. */ -smp_mb__before_atomic ();/* See above. */ -atomic_inc (&rdtp->dynticks); -smp_mb__after_atomic ();/* See above. */ + seq =atomic_add_return (RCU_DYNTICK_CTRL_CTR , &rdtp->dynticks);WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && - !(atomic_read (&rdtp->dynticks) &0x1 )); + !(seq &RCU_DYNTICK_CTRL_CTR )); +if (seq &RCU_DYNTICK_CTRL_MASK ) { +rcu_eqs_special_exit (); +/* Prefer duplicate flushes to losing a flush. */ +smp_mb__before_atomic ();/* NMI safety. */ +atomic_and (~RCU_DYNTICK_CTRL_MASK , &rdtp->dynticks); + } }/* @@ -332,9 +350,9 @@ static void rcu_dynticks_eqs_online(void) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -if (atomic_read (&rdtp->dynticks) &0x1 ) +if (atomic_read (&rdtp->dynticks) &RCU_DYNTICK_CTRL_CTR )return ; -atomic_add (0x1 , &rdtp->dynticks); +atomic_add (RCU_DYNTICK_CTRL_CTR , &rdtp->dynticks); }/* @@ -346,7 +364,7 @@ bool rcu_dynticks_curr_cpu_in_eqs(void) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -return !(atomic_read (&rdtp->dynticks) &0x1 ); +return !(atomic_read (&rdtp->dynticks) &RCU_DYNTICK_CTRL_CTR ); }/* @@ -357,7 +375,7 @@ int rcu_dynticks_snap(struct rcu_dynticks *rdtp) {int snap =atomic_add_return (0 , &rdtp->dynticks); -return snap; +return snap & ~RCU_DYNTICK_CTRL_MASK ; }/* @@ -366,7 +384,7 @@ int rcu_dynticks_snap(struct rcu_dynticks *rdtp)*/ static bool rcu_dynticks_in_eqs (int snap) { -return !(snap &0x1 ); +return !(snap &RCU_DYNTICK_CTRL_CTR ); }/* @@ -386,10 +404,33 @@ static bool rcu_dynticks_in_eqs_since(struct rcu_dynticks *rdtp, int snap)static void rcu_dynticks_momentary_idle (void ) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -int special =atomic_add_return (2 , &rdtp->dynticks); +int special =atomic_add_return (2 *RCU_DYNTICK_CTRL_CTR , + &rdtp->dynticks);/* It is illegal to call this from idle state. */ -WARN_ON_ONCE (!(special &0x1 )); +WARN_ON_ONCE (!(special &RCU_DYNTICK_CTRL_CTR )); +} + +/* +* Set the special (bottom) bit of the specified CPU so that it +* will take special action (such as flushing its TLB) on the +* next exit from an extended quiescent state. Returns true if +* the bit was successfully set, or false if the CPU was not in +* an extended quiescent state. +*/ +bool rcu_eqs_special_set (int cpu) +{ +int old; +int new; +struct rcu_dynticks *rdtp = &per_cpu (rcu_dynticks, cpu); + +do { + old =atomic_read (&rdtp->dynticks); +if (old &RCU_DYNTICK_CTRL_CTR ) +return false; + new = old |RCU_DYNTICK_CTRL_MASK ; + }while (atomic_cmpxchg (&rdtp->dynticks, old, new) != old); +return true; } DEFINE_PER_CPU_SHARED_ALIGNED(unsigned long, rcu_qs_ctr);kernel/rcu/tree.h +1
-0@@ -596,6 +596,7 @@ extern struct rcu_state rcu_preempt_state;#endif /* #ifdef CONFIG_PREEMPT_RCU */ int rcu_dynticks_snap (struct rcu_dynticks *rdtp); +bool rcu_eqs_special_set (int cpu);#ifdef CONFIG_RCU_BOOST DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status);Signature · 1 line
2.5.2