mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	rcu/segcblist: Add debug checks for segment lengths
This commit adds debug checks near the end of rcu_do_batch() that emit warnings if an empty rcu_segcblist structure has non-zero segment counts, or, conversely, if a non-empty structure has all-zero segment counts. Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org> [ paulmck: Fix queue/segment-length checks. ] Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
This commit is contained in:
		
							parent
							
								
									3afe7fa535
								
							
						
					
					
						commit
						b4e6039e8a
					
				
					 3 changed files with 21 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -94,6 +94,18 @@ static long rcu_segcblist_get_seglen(struct rcu_segcblist *rsclp, int seg)
 | 
			
		|||
	return READ_ONCE(rsclp->seglen[seg]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return number of callbacks in segmented callback list by summing seglen. */
 | 
			
		||||
long rcu_segcblist_n_segment_cbs(struct rcu_segcblist *rsclp)
 | 
			
		||||
{
 | 
			
		||||
	long len = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = RCU_DONE_TAIL; i < RCU_CBLIST_NSEGS; i++)
 | 
			
		||||
		len += rcu_segcblist_get_seglen(rsclp, i);
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set the length of a segment of the rcu_segcblist structure. */
 | 
			
		||||
static void rcu_segcblist_set_seglen(struct rcu_segcblist *rsclp, int seg, long v)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,9 @@ static inline long rcu_cblist_n_cbs(struct rcu_cblist *rclp)
 | 
			
		|||
	return READ_ONCE(rclp->len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return number of callbacks in segmented callback list by summing seglen. */
 | 
			
		||||
long rcu_segcblist_n_segment_cbs(struct rcu_segcblist *rsclp);
 | 
			
		||||
 | 
			
		||||
void rcu_cblist_init(struct rcu_cblist *rclp);
 | 
			
		||||
void rcu_cblist_enqueue(struct rcu_cblist *rclp, struct rcu_head *rhp);
 | 
			
		||||
void rcu_cblist_flush_enqueue(struct rcu_cblist *drclp,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2434,6 +2434,7 @@ int rcutree_dead_cpu(unsigned int cpu)
 | 
			
		|||
static void rcu_do_batch(struct rcu_data *rdp)
 | 
			
		||||
{
 | 
			
		||||
	int div;
 | 
			
		||||
	bool __maybe_unused empty;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	const bool offloaded = rcu_segcblist_is_offloaded(&rdp->cblist);
 | 
			
		||||
	struct rcu_head *rhp;
 | 
			
		||||
| 
						 | 
				
			
			@ -2548,9 +2549,12 @@ static void rcu_do_batch(struct rcu_data *rdp)
 | 
			
		|||
	 * The following usually indicates a double call_rcu().  To track
 | 
			
		||||
	 * this down, try building with CONFIG_DEBUG_OBJECTS_RCU_HEAD=y.
 | 
			
		||||
	 */
 | 
			
		||||
	WARN_ON_ONCE(count == 0 && !rcu_segcblist_empty(&rdp->cblist));
 | 
			
		||||
	empty = rcu_segcblist_empty(&rdp->cblist);
 | 
			
		||||
	WARN_ON_ONCE(count == 0 && !empty);
 | 
			
		||||
	WARN_ON_ONCE(!IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
 | 
			
		||||
		     count != 0 && rcu_segcblist_empty(&rdp->cblist));
 | 
			
		||||
		     count != 0 && empty);
 | 
			
		||||
	WARN_ON_ONCE(count == 0 && rcu_segcblist_n_segment_cbs(&rdp->cblist) != 0);
 | 
			
		||||
	WARN_ON_ONCE(!empty && rcu_segcblist_n_segment_cbs(&rdp->cblist) == 0);
 | 
			
		||||
 | 
			
		||||
	rcu_nocb_unlock_irqrestore(rdp, flags);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue