diff options
Diffstat (limited to 'kernel/rcu/tree.c')
| -rw-r--r-- | kernel/rcu/tree.c | 107 | 
1 files changed, 33 insertions, 74 deletions
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 51f24ecd94b2..bce848e50512 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -74,17 +74,10 @@  /* Data structures. */ -/* - * 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) -  static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data) = {  	.dynticks_nesting = 1,  	.dynticks_nmi_nesting = DYNTICK_IRQ_NONIDLE, -	.dynticks = ATOMIC_INIT(RCU_DYNTICK_CTRL_CTR), +	.dynticks = ATOMIC_INIT(1),  #ifdef CONFIG_RCU_NOCB_CPU  	.cblist.flags = SEGCBLIST_SOFTIRQ_ONLY,  #endif @@ -259,6 +252,15 @@ void rcu_softirq_qs(void)  }  /* + * Increment the current CPU's rcu_data structure's ->dynticks field + * with ordering.  Return the new value. + */ +static noinline noinstr unsigned long rcu_dynticks_inc(int incby) +{ +	return arch_atomic_add_return(incby, this_cpu_ptr(&rcu_data.dynticks)); +} + +/*   * Record entry into an extended quiescent state.  This is only to be   * called when not already in an extended quiescent state, that is,   * RCU is watching prior to the call to this function and is no longer @@ -266,7 +268,6 @@ void rcu_softirq_qs(void)   */  static noinstr void rcu_dynticks_eqs_enter(void)  { -	struct rcu_data *rdp = this_cpu_ptr(&rcu_data);  	int seq;  	/* @@ -275,13 +276,9 @@ static noinstr void rcu_dynticks_eqs_enter(void)  	 * next idle sojourn.  	 */  	rcu_dynticks_task_trace_enter();  // Before ->dynticks update! -	seq = arch_atomic_add_return(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks); +	seq = rcu_dynticks_inc(1);  	// RCU is no longer watching.  Better be in 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! */ -	WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && -		     (seq & RCU_DYNTICK_CTRL_MASK)); +	WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && (seq & 0x1));  }  /* @@ -291,7 +288,6 @@ static noinstr void rcu_dynticks_eqs_enter(void)   */  static noinstr void rcu_dynticks_eqs_exit(void)  { -	struct rcu_data *rdp = this_cpu_ptr(&rcu_data);  	int seq;  	/* @@ -299,15 +295,10 @@ static noinstr void rcu_dynticks_eqs_exit(void)  	 * and we also must force ordering with the next RCU read-side  	 * critical section.  	 */ -	seq = arch_atomic_add_return(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks); +	seq = rcu_dynticks_inc(1);  	// RCU is now watching.  Better not be in an extended quiescent state!  	rcu_dynticks_task_trace_exit();  // After ->dynticks update! -	WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && -		     !(seq & RCU_DYNTICK_CTRL_CTR)); -	if (seq & RCU_DYNTICK_CTRL_MASK) { -		arch_atomic_andnot(RCU_DYNTICK_CTRL_MASK, &rdp->dynticks); -		smp_mb__after_atomic(); /* _exit after clearing mask. */ -	} +	WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !(seq & 0x1));  }  /* @@ -324,9 +315,9 @@ static void rcu_dynticks_eqs_online(void)  {  	struct rcu_data *rdp = this_cpu_ptr(&rcu_data); -	if (atomic_read(&rdp->dynticks) & RCU_DYNTICK_CTRL_CTR) +	if (atomic_read(&rdp->dynticks) & 0x1)  		return; -	atomic_add(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks); +	rcu_dynticks_inc(1);  }  /* @@ -336,9 +327,7 @@ static void rcu_dynticks_eqs_online(void)   */  static __always_inline bool rcu_dynticks_curr_cpu_in_eqs(void)  { -	struct rcu_data *rdp = this_cpu_ptr(&rcu_data); - -	return !(arch_atomic_read(&rdp->dynticks) & RCU_DYNTICK_CTRL_CTR); +	return !(atomic_read(this_cpu_ptr(&rcu_data.dynticks)) & 0x1);  }  /* @@ -347,9 +336,8 @@ static __always_inline bool rcu_dynticks_curr_cpu_in_eqs(void)   */  static int rcu_dynticks_snap(struct rcu_data *rdp)  { -	int snap = atomic_add_return(0, &rdp->dynticks); - -	return snap & ~RCU_DYNTICK_CTRL_MASK; +	smp_mb();  // Fundamental RCU ordering guarantee. +	return atomic_read_acquire(&rdp->dynticks);  }  /* @@ -358,7 +346,7 @@ static int rcu_dynticks_snap(struct rcu_data *rdp)   */  static bool rcu_dynticks_in_eqs(int snap)  { -	return !(snap & RCU_DYNTICK_CTRL_CTR); +	return !(snap & 0x1);  }  /* Return true if the specified CPU is currently idle from an RCU viewpoint.  */ @@ -389,8 +377,7 @@ bool rcu_dynticks_zero_in_eqs(int cpu, int *vp)  	int snap;  	// If not quiescent, force back to earlier extended quiescent state. -	snap = atomic_read(&rdp->dynticks) & ~(RCU_DYNTICK_CTRL_MASK | -					       RCU_DYNTICK_CTRL_CTR); +	snap = atomic_read(&rdp->dynticks) & ~0x1;  	smp_rmb(); // Order ->dynticks and *vp reads.  	if (READ_ONCE(*vp)) @@ -398,32 +385,7 @@ bool rcu_dynticks_zero_in_eqs(int cpu, int *vp)  	smp_rmb(); // Order *vp read and ->dynticks re-read.  	// If still in the same extended quiescent state, we are good! -	return snap == (atomic_read(&rdp->dynticks) & ~RCU_DYNTICK_CTRL_MASK); -} - -/* - * 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; -	int new_old; -	struct rcu_data *rdp = &per_cpu(rcu_data, cpu); - -	new_old = atomic_read(&rdp->dynticks); -	do { -		old = new_old; -		if (old & RCU_DYNTICK_CTRL_CTR) -			return false; -		new = old | RCU_DYNTICK_CTRL_MASK; -		new_old = atomic_cmpxchg(&rdp->dynticks, old, new); -	} while (new_old != old); -	return true; +	return snap == atomic_read(&rdp->dynticks);  }  /* @@ -439,13 +401,12 @@ bool rcu_eqs_special_set(int cpu)   */  notrace void rcu_momentary_dyntick_idle(void)  { -	int special; +	int seq;  	raw_cpu_write(rcu_data.rcu_need_heavy_qs, false); -	special = atomic_add_return(2 * RCU_DYNTICK_CTRL_CTR, -				    &this_cpu_ptr(&rcu_data)->dynticks); +	seq = rcu_dynticks_inc(2);  	/* It is illegal to call this from idle state. */ -	WARN_ON_ONCE(!(special & RCU_DYNTICK_CTRL_CTR)); +	WARN_ON_ONCE(!(seq & 0x1));  	rcu_preempt_deferred_qs(current);  }  EXPORT_SYMBOL_GPL(rcu_momentary_dyntick_idle); @@ -1325,7 +1286,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)  	 */  	jtsq = READ_ONCE(jiffies_to_sched_qs);  	ruqp = per_cpu_ptr(&rcu_data.rcu_urgent_qs, rdp->cpu); -	rnhqp = &per_cpu(rcu_data.rcu_need_heavy_qs, rdp->cpu); +	rnhqp = per_cpu_ptr(&rcu_data.rcu_need_heavy_qs, rdp->cpu);  	if (!READ_ONCE(*rnhqp) &&  	    (time_after(jiffies, rcu_state.gp_start + jtsq * 2) ||  	     time_after(jiffies, rcu_state.jiffies_resched) || @@ -1772,7 +1733,7 @@ static void rcu_strict_gp_boundary(void *unused)  /*   * Initialize a new grace period.  Return false if no grace period required.   */ -static bool rcu_gp_init(void) +static noinline_for_stack bool rcu_gp_init(void)  {  	unsigned long firstseq;  	unsigned long flags; @@ -1966,7 +1927,7 @@ static void rcu_gp_fqs(bool first_time)  /*   * Loop doing repeated quiescent-state forcing until the grace period ends.   */ -static void rcu_gp_fqs_loop(void) +static noinline_for_stack void rcu_gp_fqs_loop(void)  {  	bool first_gp_fqs;  	int gf = 0; @@ -1993,8 +1954,8 @@ static void rcu_gp_fqs_loop(void)  		trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq,  				       TPS("fqswait"));  		WRITE_ONCE(rcu_state.gp_state, RCU_GP_WAIT_FQS); -		ret = swait_event_idle_timeout_exclusive( -				rcu_state.gp_wq, rcu_gp_fqs_check_wake(&gf), j); +		(void)swait_event_idle_timeout_exclusive(rcu_state.gp_wq, +				 rcu_gp_fqs_check_wake(&gf), j);  		rcu_gp_torture_wait();  		WRITE_ONCE(rcu_state.gp_state, RCU_GP_DOING_FQS);  		/* Locking provides needed memory barriers. */ @@ -2471,9 +2432,6 @@ int rcutree_dead_cpu(unsigned int cpu)  	WRITE_ONCE(rcu_state.n_online_cpus, rcu_state.n_online_cpus - 1);  	/* Adjust any no-longer-needed kthreads. */  	rcu_boost_kthread_setaffinity(rnp, -1); -	/* Do any needed no-CB deferred wakeups from this CPU. */ -	do_nocb_deferred_wakeup(per_cpu_ptr(&rcu_data, cpu)); -  	// Stop-machine done, so allow nohz_full to disable tick.  	tick_dep_clear(TICK_DEP_BIT_RCU);  	return 0; @@ -4050,7 +4008,7 @@ void rcu_barrier(void)  	 */  	init_completion(&rcu_state.barrier_completion);  	atomic_set(&rcu_state.barrier_cpu_count, 2); -	get_online_cpus(); +	cpus_read_lock();  	/*  	 * Force each CPU with callbacks to register a new callback. @@ -4081,7 +4039,7 @@ void rcu_barrier(void)  					  rcu_state.barrier_sequence);  		}  	} -	put_online_cpus(); +	cpus_read_unlock();  	/*  	 * Now that we have an rcu_barrier_callback() callback on each @@ -4784,4 +4742,5 @@ void __init rcu_init(void)  #include "tree_stall.h"  #include "tree_exp.h" +#include "tree_nocb.h"  #include "tree_plugin.h"  |