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);
 | 
						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 */
 | 
					#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
 | 
					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;
 | 
						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 */
 | 
					#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SRCU_NMI_UNKNOWN	0x0
 | 
					#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);
 | 
						srcu_check_nmi_safety(ssp, false);
 | 
				
			||||||
	retval = __srcu_read_lock(ssp);
 | 
						retval = __srcu_read_lock(ssp);
 | 
				
			||||||
	rcu_lock_acquire(&(ssp)->dep_map);
 | 
						srcu_lock_acquire(&(ssp)->dep_map);
 | 
				
			||||||
	return retval;
 | 
						return retval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -254,7 +284,7 @@ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	WARN_ON_ONCE(idx & ~0x1);
 | 
						WARN_ON_ONCE(idx & ~0x1);
 | 
				
			||||||
	srcu_check_nmi_safety(ssp, false);
 | 
						srcu_check_nmi_safety(ssp, false);
 | 
				
			||||||
	rcu_lock_release(&(ssp)->dep_map);
 | 
						srcu_lock_release(&(ssp)->dep_map);
 | 
				
			||||||
	__srcu_read_unlock(ssp, idx);
 | 
						__srcu_read_unlock(ssp, idx);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,6 +197,8 @@ void synchronize_srcu(struct srcu_struct *ssp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rcu_synchronize rs;
 | 
						struct rcu_synchronize rs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srcu_lock_sync(&ssp->dep_map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
 | 
						RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
 | 
				
			||||||
			lock_is_held(&rcu_bh_lock_map) ||
 | 
								lock_is_held(&rcu_bh_lock_map) ||
 | 
				
			||||||
			lock_is_held(&rcu_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;
 | 
						struct rcu_synchronize rcu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srcu_lock_sync(&ssp->dep_map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
 | 
						RCU_LOCKDEP_WARN(lockdep_is_held(ssp) ||
 | 
				
			||||||
			 lock_is_held(&rcu_bh_lock_map) ||
 | 
								 lock_is_held(&rcu_bh_lock_map) ||
 | 
				
			||||||
			 lock_is_held(&rcu_lock_map) ||
 | 
								 lock_is_held(&rcu_lock_map) ||
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue