diff options
Diffstat (limited to 'kernel/rcu/tree.h')
| -rw-r--r-- | kernel/rcu/tree.h | 96 | 
1 files changed, 63 insertions, 33 deletions
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 4adb7ca0bf47..2e991f8361e4 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -27,6 +27,7 @@  #include <linux/threads.h>  #include <linux/cpumask.h>  #include <linux/seqlock.h> +#include <linux/stop_machine.h>  /*   * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and @@ -36,8 +37,6 @@   * Of course, your mileage may vary.   */ -#define MAX_RCU_LVLS 4 -  #ifdef CONFIG_RCU_FANOUT  #define RCU_FANOUT CONFIG_RCU_FANOUT  #else /* #ifdef CONFIG_RCU_FANOUT */ @@ -66,38 +65,53 @@  #if NR_CPUS <= RCU_FANOUT_1  #  define RCU_NUM_LVLS	      1  #  define NUM_RCU_LVL_0	      1 -#  define NUM_RCU_LVL_1	      (NR_CPUS) -#  define NUM_RCU_LVL_2	      0 -#  define NUM_RCU_LVL_3	      0 -#  define NUM_RCU_LVL_4	      0 +#  define NUM_RCU_NODES	      NUM_RCU_LVL_0 +#  define NUM_RCU_LVL_INIT    { NUM_RCU_LVL_0 } +#  define RCU_NODE_NAME_INIT  { "rcu_node_0" } +#  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0" } +#  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0" } +#  define RCU_EXP_SCHED_NAME_INIT \ +			      { "rcu_node_exp_sched_0" }  #elif NR_CPUS <= RCU_FANOUT_2  #  define RCU_NUM_LVLS	      2  #  define NUM_RCU_LVL_0	      1  #  define NUM_RCU_LVL_1	      DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) -#  define NUM_RCU_LVL_2	      (NR_CPUS) -#  define NUM_RCU_LVL_3	      0 -#  define NUM_RCU_LVL_4	      0 +#  define NUM_RCU_NODES	      (NUM_RCU_LVL_0 + NUM_RCU_LVL_1) +#  define NUM_RCU_LVL_INIT    { NUM_RCU_LVL_0, NUM_RCU_LVL_1 } +#  define RCU_NODE_NAME_INIT  { "rcu_node_0", "rcu_node_1" } +#  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0", "rcu_node_fqs_1" } +#  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0", "rcu_node_exp_1" } +#  define RCU_EXP_SCHED_NAME_INIT \ +			      { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1" }  #elif NR_CPUS <= RCU_FANOUT_3  #  define RCU_NUM_LVLS	      3  #  define NUM_RCU_LVL_0	      1  #  define NUM_RCU_LVL_1	      DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2)  #  define NUM_RCU_LVL_2	      DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) -#  define NUM_RCU_LVL_3	      (NR_CPUS) -#  define NUM_RCU_LVL_4	      0 +#  define NUM_RCU_NODES	      (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2) +#  define NUM_RCU_LVL_INIT    { NUM_RCU_LVL_0, NUM_RCU_LVL_1, NUM_RCU_LVL_2 } +#  define RCU_NODE_NAME_INIT  { "rcu_node_0", "rcu_node_1", "rcu_node_2" } +#  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2" } +#  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0", "rcu_node_exp_1", "rcu_node_exp_2" } +#  define RCU_EXP_SCHED_NAME_INIT \ +			      { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1", "rcu_node_exp_sched_2" }  #elif NR_CPUS <= RCU_FANOUT_4  #  define RCU_NUM_LVLS	      4  #  define NUM_RCU_LVL_0	      1  #  define NUM_RCU_LVL_1	      DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_3)  #  define NUM_RCU_LVL_2	      DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2)  #  define NUM_RCU_LVL_3	      DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) -#  define NUM_RCU_LVL_4	      (NR_CPUS) +#  define NUM_RCU_NODES	      (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3) +#  define NUM_RCU_LVL_INIT    { NUM_RCU_LVL_0, NUM_RCU_LVL_1, NUM_RCU_LVL_2, NUM_RCU_LVL_3 } +#  define RCU_NODE_NAME_INIT  { "rcu_node_0", "rcu_node_1", "rcu_node_2", "rcu_node_3" } +#  define RCU_FQS_NAME_INIT   { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2", "rcu_node_fqs_3" } +#  define RCU_EXP_NAME_INIT   { "rcu_node_exp_0", "rcu_node_exp_1", "rcu_node_exp_2", "rcu_node_exp_3" } +#  define RCU_EXP_SCHED_NAME_INIT \ +			      { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1", "rcu_node_exp_sched_2", "rcu_node_exp_sched_3" }  #else  # error "CONFIG_RCU_FANOUT insufficient for NR_CPUS"  #endif /* #if (NR_CPUS) <= RCU_FANOUT_1 */ -#define RCU_SUM (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3 + NUM_RCU_LVL_4) -#define NUM_RCU_NODES (RCU_SUM - NR_CPUS) -  extern int rcu_num_lvls;  extern int rcu_num_nodes; @@ -236,6 +250,8 @@ struct rcu_node {  	int need_future_gp[2];  				/* Counts of upcoming no-CB GP requests. */  	raw_spinlock_t fqslock ____cacheline_internodealigned_in_smp; + +	struct mutex exp_funnel_mutex ____cacheline_internodealigned_in_smp;  } ____cacheline_internodealigned_in_smp;  /* @@ -287,12 +303,13 @@ struct rcu_data {  	bool		gpwrap;		/* Possible gpnum/completed wrap. */  	struct rcu_node *mynode;	/* This CPU's leaf of hierarchy */  	unsigned long grpmask;		/* Mask to apply to leaf qsmask. */ -#ifdef CONFIG_RCU_CPU_STALL_INFO  	unsigned long	ticks_this_gp;	/* The number of scheduling-clock */  					/*  ticks this CPU has handled */  					/*  during and after the last grace */  					/* period it is aware of. */ -#endif /* #ifdef CONFIG_RCU_CPU_STALL_INFO */ +	struct cpu_stop_work exp_stop_work; +					/* Expedited grace-period control */ +					/*  for CPU stopping. */  	/* 2) batch handling */  	/* @@ -355,11 +372,13 @@ struct rcu_data {  	unsigned long n_rp_nocb_defer_wakeup;  	unsigned long n_rp_need_nothing; -	/* 6) _rcu_barrier() and OOM callbacks. */ +	/* 6) _rcu_barrier(), OOM callbacks, and expediting. */  	struct rcu_head barrier_head;  #ifdef CONFIG_RCU_FAST_NO_HZ  	struct rcu_head oom_head;  #endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ +	struct mutex exp_funnel_mutex; +	bool exp_done;			/* Expedited QS for this CPU? */  	/* 7) Callback offloading. */  #ifdef CONFIG_RCU_NOCB_CPU @@ -387,9 +406,7 @@ struct rcu_data {  #endif /* #ifdef CONFIG_RCU_NOCB_CPU */  	/* 8) RCU CPU stall data. */ -#ifdef CONFIG_RCU_CPU_STALL_INFO  	unsigned int softirq_snap;	/* Snapshot of softirq activity. */ -#endif /* #ifdef CONFIG_RCU_CPU_STALL_INFO */  	int cpu;  	struct rcu_state *rsp; @@ -442,9 +459,9 @@ do {									\   */  struct rcu_state {  	struct rcu_node node[NUM_RCU_NODES];	/* Hierarchy. */ -	struct rcu_node *level[RCU_NUM_LVLS];	/* Hierarchy levels. */ -	u32 levelcnt[MAX_RCU_LVLS + 1];		/* # nodes in each level. */ -	u8 levelspread[RCU_NUM_LVLS];		/* kids/node in each level. */ +	struct rcu_node *level[RCU_NUM_LVLS + 1]; +						/* Hierarchy levels (+1 to */ +						/*  shut bogus gcc warning) */  	u8 flavor_mask;				/* bit in flavor mask. */  	struct rcu_data __percpu *rda;		/* pointer of percu rcu_data. */  	void (*call)(struct rcu_head *head,	/* call_rcu() flavor. */ @@ -479,21 +496,18 @@ struct rcu_state {  	struct mutex barrier_mutex;		/* Guards barrier fields. */  	atomic_t barrier_cpu_count;		/* # CPUs waiting on. */  	struct completion barrier_completion;	/* Wake at barrier end. */ -	unsigned long n_barrier_done;		/* ++ at start and end of */ +	unsigned long barrier_sequence;		/* ++ at start and end of */  						/*  _rcu_barrier(). */  	/* End of fields guarded by barrier_mutex. */ -	atomic_long_t expedited_start;		/* Starting ticket. */ -	atomic_long_t expedited_done;		/* Done ticket. */ -	atomic_long_t expedited_wrap;		/* # near-wrap incidents. */ -	atomic_long_t expedited_tryfail;	/* # acquisition failures. */ +	unsigned long expedited_sequence;	/* Take a ticket. */ +	atomic_long_t expedited_workdone0;	/* # done by others #0. */  	atomic_long_t expedited_workdone1;	/* # done by others #1. */  	atomic_long_t expedited_workdone2;	/* # done by others #2. */ +	atomic_long_t expedited_workdone3;	/* # done by others #3. */  	atomic_long_t expedited_normal;		/* # fallbacks to normal. */ -	atomic_long_t expedited_stoppedcpus;	/* # successful stop_cpus. */ -	atomic_long_t expedited_done_tries;	/* # tries to update _done. */ -	atomic_long_t expedited_done_lost;	/* # times beaten to _done. */ -	atomic_long_t expedited_done_exit;	/* # times exited _done loop. */ +	atomic_t expedited_need_qs;		/* # CPUs left to check in. */ +	wait_queue_head_t expedited_wq;		/* Wait for check-ins. */  	unsigned long jiffies_force_qs;		/* Time at which to invoke */  						/*  force_quiescent_state(). */ @@ -527,7 +541,11 @@ struct rcu_state {  /* Values for rcu_state structure's gp_flags field. */  #define RCU_GP_WAIT_INIT 0	/* Initial state. */  #define RCU_GP_WAIT_GPS  1	/* Wait for grace-period start. */ -#define RCU_GP_WAIT_FQS  2	/* Wait for force-quiescent-state time. */ +#define RCU_GP_DONE_GPS  2	/* Wait done for grace-period start. */ +#define RCU_GP_WAIT_FQS  3	/* Wait for force-quiescent-state time. */ +#define RCU_GP_DOING_FQS 4	/* Wait done for force-quiescent-state time. */ +#define RCU_GP_CLEANUP   5	/* Grace-period cleanup started. */ +#define RCU_GP_CLEANED   6	/* Grace-period cleanup complete. */  extern struct list_head rcu_struct_flavors; @@ -635,3 +653,15 @@ static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll)  #endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */  }  #endif /* #ifdef CONFIG_RCU_TRACE */ + +/* + * Place this after a lock-acquisition primitive to guarantee that + * an UNLOCK+LOCK pair act as a full barrier.  This guarantee applies + * if the UNLOCK and LOCK are executed by the same CPU or if the + * UNLOCK and LOCK operate on the same lock variable. + */ +#ifdef CONFIG_PPC +#define smp_mb__after_unlock_lock()	smp_mb()  /* Full ordering for lock. */ +#else /* #ifdef CONFIG_PPC */ +#define smp_mb__after_unlock_lock()	do { } while (0) +#endif /* #else #ifdef CONFIG_PPC */  |