diff options
Diffstat (limited to 'include/linux/rcupdate.h')
| -rw-r--r-- | include/linux/rcupdate.h | 115 | 
1 files changed, 73 insertions, 42 deletions
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 2cf4226ade7..81c04f4348e 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -51,6 +51,8 @@ extern int rcutorture_runnable; /* for sysctl */  #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU)  extern void rcutorture_record_test_transition(void);  extern void rcutorture_record_progress(unsigned long vernum); +extern void do_trace_rcu_torture_read(char *rcutorturename, +				      struct rcu_head *rhp);  #else  static inline void rcutorture_record_test_transition(void)  { @@ -58,6 +60,12 @@ static inline void rcutorture_record_test_transition(void)  static inline void rcutorture_record_progress(unsigned long vernum)  {  } +#ifdef CONFIG_RCU_TRACE +extern void do_trace_rcu_torture_read(char *rcutorturename, +				      struct rcu_head *rhp); +#else +#define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) +#endif  #endif  #define UINT_CMP_GE(a, b)	(UINT_MAX / 2 >= (a) - (b)) @@ -177,23 +185,10 @@ extern void rcu_sched_qs(int cpu);  extern void rcu_bh_qs(int cpu);  extern void rcu_check_callbacks(int cpu, int user);  struct notifier_block; - -#ifdef CONFIG_NO_HZ - -extern void rcu_enter_nohz(void); -extern void rcu_exit_nohz(void); - -#else /* #ifdef CONFIG_NO_HZ */ - -static inline void rcu_enter_nohz(void) -{ -} - -static inline void rcu_exit_nohz(void) -{ -} - -#endif /* #else #ifdef CONFIG_NO_HZ */ +extern void rcu_idle_enter(void); +extern void rcu_idle_exit(void); +extern void rcu_irq_enter(void); +extern void rcu_irq_exit(void);  /*   * Infrastructure to implement the synchronize_() primitives in @@ -233,22 +228,30 @@ static inline void destroy_rcu_head_on_stack(struct rcu_head *head)  #ifdef CONFIG_DEBUG_LOCK_ALLOC -extern struct lockdep_map rcu_lock_map; -# define rcu_read_acquire() \ -		lock_acquire(&rcu_lock_map, 0, 0, 2, 1, NULL, _THIS_IP_) -# define rcu_read_release()	lock_release(&rcu_lock_map, 1, _THIS_IP_) +#ifdef CONFIG_PROVE_RCU +extern int rcu_is_cpu_idle(void); +#else /* !CONFIG_PROVE_RCU */ +static inline int rcu_is_cpu_idle(void) +{ +	return 0; +} +#endif /* else !CONFIG_PROVE_RCU */ -extern struct lockdep_map rcu_bh_lock_map; -# define rcu_read_acquire_bh() \ -		lock_acquire(&rcu_bh_lock_map, 0, 0, 2, 1, NULL, _THIS_IP_) -# define rcu_read_release_bh()	lock_release(&rcu_bh_lock_map, 1, _THIS_IP_) +static inline void rcu_lock_acquire(struct lockdep_map *map) +{ +	WARN_ON_ONCE(rcu_is_cpu_idle()); +	lock_acquire(map, 0, 0, 2, 1, NULL, _THIS_IP_); +} -extern struct lockdep_map rcu_sched_lock_map; -# define rcu_read_acquire_sched() \ -		lock_acquire(&rcu_sched_lock_map, 0, 0, 2, 1, NULL, _THIS_IP_) -# define rcu_read_release_sched() \ -		lock_release(&rcu_sched_lock_map, 1, _THIS_IP_) +static inline void rcu_lock_release(struct lockdep_map *map) +{ +	WARN_ON_ONCE(rcu_is_cpu_idle()); +	lock_release(map, 1, _THIS_IP_); +} +extern struct lockdep_map rcu_lock_map; +extern struct lockdep_map rcu_bh_lock_map; +extern struct lockdep_map rcu_sched_lock_map;  extern int debug_lockdep_rcu_enabled(void);  /** @@ -262,11 +265,18 @@ extern int debug_lockdep_rcu_enabled(void);   *   * Checks debug_lockdep_rcu_enabled() to prevent false positives during boot   * and while lockdep is disabled. + * + * Note that rcu_read_lock() and the matching rcu_read_unlock() must + * occur in the same context, for example, it is illegal to invoke + * rcu_read_unlock() in process context if the matching rcu_read_lock() + * was invoked from within an irq handler.   */  static inline int rcu_read_lock_held(void)  {  	if (!debug_lockdep_rcu_enabled())  		return 1; +	if (rcu_is_cpu_idle()) +		return 0;  	return lock_is_held(&rcu_lock_map);  } @@ -290,6 +300,19 @@ extern int rcu_read_lock_bh_held(void);   *   * Check debug_lockdep_rcu_enabled() to prevent false positives during boot   * and while lockdep is disabled. + * + * Note that if the CPU is in the idle loop from an RCU point of + * view (ie: that we are in the section between rcu_idle_enter() and + * rcu_idle_exit()) then rcu_read_lock_held() returns false even if the CPU + * did an rcu_read_lock().  The reason for this is that RCU ignores CPUs + * that are in such a section, considering these as in extended quiescent + * state, so such a CPU is effectively never in an RCU read-side critical + * section regardless of what RCU primitives it invokes.  This state of + * affairs is required --- we need to keep an RCU-free window in idle + * where the CPU may possibly enter into low power mode. This way we can + * notice an extended quiescent state to other CPUs that started a grace + * period. Otherwise we would delay any grace period as long as we run in + * the idle task.   */  #ifdef CONFIG_PREEMPT_COUNT  static inline int rcu_read_lock_sched_held(void) @@ -298,6 +321,8 @@ static inline int rcu_read_lock_sched_held(void)  	if (!debug_lockdep_rcu_enabled())  		return 1; +	if (rcu_is_cpu_idle()) +		return 0;  	if (debug_locks)  		lockdep_opinion = lock_is_held(&rcu_sched_lock_map);  	return lockdep_opinion || preempt_count() != 0 || irqs_disabled(); @@ -311,12 +336,8 @@ static inline int rcu_read_lock_sched_held(void)  #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ -# define rcu_read_acquire()		do { } while (0) -# define rcu_read_release()		do { } while (0) -# define rcu_read_acquire_bh()		do { } while (0) -# define rcu_read_release_bh()		do { } while (0) -# define rcu_read_acquire_sched()	do { } while (0) -# define rcu_read_release_sched()	do { } while (0) +# define rcu_lock_acquire(a)		do { } while (0) +# define rcu_lock_release(a)		do { } while (0)  static inline int rcu_read_lock_held(void)  { @@ -637,7 +658,7 @@ static inline void rcu_read_lock(void)  {  	__rcu_read_lock();  	__acquire(RCU); -	rcu_read_acquire(); +	rcu_lock_acquire(&rcu_lock_map);  }  /* @@ -657,7 +678,7 @@ static inline void rcu_read_lock(void)   */  static inline void rcu_read_unlock(void)  { -	rcu_read_release(); +	rcu_lock_release(&rcu_lock_map);  	__release(RCU);  	__rcu_read_unlock();  } @@ -673,12 +694,17 @@ static inline void rcu_read_unlock(void)   * critical sections in interrupt context can use just rcu_read_lock(),   * though this should at least be commented to avoid confusing people   * reading the code. + * + * Note that rcu_read_lock_bh() and the matching rcu_read_unlock_bh() + * must occur in the same context, for example, it is illegal to invoke + * rcu_read_unlock_bh() from one task if the matching rcu_read_lock_bh() + * was invoked from some other task.   */  static inline void rcu_read_lock_bh(void)  {  	local_bh_disable();  	__acquire(RCU_BH); -	rcu_read_acquire_bh(); +	rcu_lock_acquire(&rcu_bh_lock_map);  }  /* @@ -688,7 +714,7 @@ static inline void rcu_read_lock_bh(void)   */  static inline void rcu_read_unlock_bh(void)  { -	rcu_read_release_bh(); +	rcu_lock_release(&rcu_bh_lock_map);  	__release(RCU_BH);  	local_bh_enable();  } @@ -700,12 +726,17 @@ static inline void rcu_read_unlock_bh(void)   * are being done using call_rcu_sched() or synchronize_rcu_sched().   * Read-side critical sections can also be introduced by anything that   * disables preemption, including local_irq_disable() and friends. + * + * Note that rcu_read_lock_sched() and the matching rcu_read_unlock_sched() + * must occur in the same context, for example, it is illegal to invoke + * rcu_read_unlock_sched() from process context if the matching + * rcu_read_lock_sched() was invoked from an NMI handler.   */  static inline void rcu_read_lock_sched(void)  {  	preempt_disable();  	__acquire(RCU_SCHED); -	rcu_read_acquire_sched(); +	rcu_lock_acquire(&rcu_sched_lock_map);  }  /* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */ @@ -722,7 +753,7 @@ static inline notrace void rcu_read_lock_sched_notrace(void)   */  static inline void rcu_read_unlock_sched(void)  { -	rcu_read_release_sched(); +	rcu_lock_release(&rcu_sched_lock_map);  	__release(RCU_SCHED);  	preempt_enable();  }  |