forked from mirrors/linux
		
	rcu: Annotate SRCU's update-side lockdep dependencies
Although all flavors of RCU readers are annotated correctly with lockdep as recursive read locks, they do not set the lock_acquire 'check' parameter. This means that RCU read locks are not added to the lockdep dependency graph, which in turn means that lockdep cannot detect RCU-based deadlocks. This is not a problem for RCU flavors having atomic read-side critical sections because context-based annotations can catch these deadlocks, see for example the RCU_LOCKDEP_WARN() statement in synchronize_rcu(). But context-based annotations are not helpful for sleepable RCU, especially given that it is perfectly legal to do synchronize_srcu(&srcu1) within an srcu_read_lock(&srcu2). However, we can detect SRCU-based by: (1) Making srcu_read_lock() a 'check'ed recursive read lock and (2) Making synchronize_srcu() a empty write lock critical section. Even better, with the newly introduced lock_sync(), we can avoid false positives about irq-unsafe/safe. This commit therefore makes it so. Note that NMI-safe SRCU read side critical sections are currently not annotated, but might be annotated in the future. Signed-off-by: Boqun Feng <boqun.feng@gmail.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org> [ boqun: Add comments for annotation per Waiman's suggestion ] [ boqun: Fix comment warning reported by Stephen Rothwell ] Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
This commit is contained in:
		
							parent
							
								
									2f1f043e7b
								
							
						
					
					
						commit
						f0f44752f5
					
				
					 3 changed files with 36 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -102,6 +102,32 @@ static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
 | 
			
		|||
	return lock_is_held(&ssp->dep_map);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Annotations provide deadlock detection for SRCU.
 | 
			
		||||
 *
 | 
			
		||||
 * Similar to other lockdep annotations, except there is an additional
 | 
			
		||||
 * srcu_lock_sync(), which is basically an empty *write*-side critical section,
 | 
			
		||||
 * see lock_sync() for more information.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Annotates a srcu_read_lock() */
 | 
			
		||||
static inline void srcu_lock_acquire(struct lockdep_map *map)
 | 
			
		||||
{
 | 
			
		||||
	lock_map_acquire_read(map);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Annotates a srcu_read_lock() */
 | 
			
		||||
static inline void srcu_lock_release(struct lockdep_map *map)
 | 
			
		||||
{
 | 
			
		||||
	lock_map_release(map);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Annotates a synchronize_srcu() */
 | 
			
		||||
static inline void srcu_lock_sync(struct lockdep_map *map)
 | 
			
		||||
{
 | 
			
		||||
	lock_map_sync(map);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 | 
			
		||||
 | 
			
		||||
static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +135,10 @@ static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
 | 
			
		|||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define srcu_lock_acquire(m) do { } while (0)
 | 
			
		||||
#define srcu_lock_release(m) do { } while (0)
 | 
			
		||||
#define srcu_lock_sync(m) do { } while (0)
 | 
			
		||||
 | 
			
		||||
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 | 
			
		||||
 | 
			
		||||
#define SRCU_NMI_UNKNOWN	0x0
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +212,7 @@ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp)
 | 
			
		|||
 | 
			
		||||
	srcu_check_nmi_safety(ssp, false);
 | 
			
		||||
	retval = __srcu_read_lock(ssp);
 | 
			
		||||
	rcu_lock_acquire(&(ssp)->dep_map);
 | 
			
		||||
	srcu_lock_acquire(&(ssp)->dep_map);
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +284,7 @@ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx)
 | 
			
		|||
{
 | 
			
		||||
	WARN_ON_ONCE(idx & ~0x1);
 | 
			
		||||
	srcu_check_nmi_safety(ssp, false);
 | 
			
		||||
	rcu_lock_release(&(ssp)->dep_map);
 | 
			
		||||
	srcu_lock_release(&(ssp)->dep_map);
 | 
			
		||||
	__srcu_read_unlock(ssp, idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -197,6 +197,8 @@ void synchronize_srcu(struct srcu_struct *ssp)
 | 
			
		|||
{
 | 
			
		||||
	struct rcu_synchronize rs;
 | 
			
		||||
 | 
			
		||||
	srcu_lock_sync(&ssp->dep_map);
 | 
			
		||||
 | 
			
		||||
	RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
 | 
			
		||||
			lock_is_held(&rcu_bh_lock_map) ||
 | 
			
		||||
			lock_is_held(&rcu_lock_map) ||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1307,6 +1307,8 @@ static void __synchronize_srcu(struct srcu_struct *ssp, bool do_norm)
 | 
			
		|||
{
 | 
			
		||||
	struct rcu_synchronize rcu;
 | 
			
		||||
 | 
			
		||||
	srcu_lock_sync(&ssp->dep_map);
 | 
			
		||||
 | 
			
		||||
	RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
 | 
			
		||||
			 lock_is_held(&rcu_bh_lock_map) ||
 | 
			
		||||
			 lock_is_held(&rcu_lock_map) ||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue