mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	srcu: Add cleanup_srcu_struct_quiesced()
The current cleanup_srcu_struct() flushes work, which prevents it from being invoked from some workqueue contexts, as well as from atomic (non-blocking) contexts. This patch therefore introduced a cleanup_srcu_struct_quiesced(), which can be invoked only after all activity on the specified srcu_struct has completed. This restriction allows cleanup_srcu_struct_quiesced() to be invoked from workqueue contexts as well as from atomic contexts. Suggested-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Tested-by: Nitzan Carmi <nitzanc@mellanox.com> Tested-by: Nicholas Piggin <npiggin@gmail.com>
This commit is contained in:
		
							parent
							
								
									17672480fb
								
							
						
					
					
						commit
						f7194ac32c
					
				
					 4 changed files with 64 additions and 18 deletions
				
			
		| 
						 | 
					@ -69,11 +69,45 @@ struct srcu_struct { };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void call_srcu(struct srcu_struct *sp, struct rcu_head *head,
 | 
					void call_srcu(struct srcu_struct *sp, struct rcu_head *head,
 | 
				
			||||||
		void (*func)(struct rcu_head *head));
 | 
							void (*func)(struct rcu_head *head));
 | 
				
			||||||
void cleanup_srcu_struct(struct srcu_struct *sp);
 | 
					void _cleanup_srcu_struct(struct srcu_struct *sp, bool quiesced);
 | 
				
			||||||
int __srcu_read_lock(struct srcu_struct *sp) __acquires(sp);
 | 
					int __srcu_read_lock(struct srcu_struct *sp) __acquires(sp);
 | 
				
			||||||
void __srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp);
 | 
					void __srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp);
 | 
				
			||||||
void synchronize_srcu(struct srcu_struct *sp);
 | 
					void synchronize_srcu(struct srcu_struct *sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * cleanup_srcu_struct - deconstruct a sleep-RCU structure
 | 
				
			||||||
 | 
					 * @sp: structure to clean up.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Must invoke this after you are finished using a given srcu_struct that
 | 
				
			||||||
 | 
					 * was initialized via init_srcu_struct(), else you leak memory.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline void cleanup_srcu_struct(struct srcu_struct *sp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_cleanup_srcu_struct(sp, false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * cleanup_srcu_struct_quiesced - deconstruct a quiesced sleep-RCU structure
 | 
				
			||||||
 | 
					 * @sp: structure to clean up.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Must invoke this after you are finished using a given srcu_struct that
 | 
				
			||||||
 | 
					 * was initialized via init_srcu_struct(), else you leak memory.  Also,
 | 
				
			||||||
 | 
					 * all grace-period processing must have completed.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * "Completed" means that the last synchronize_srcu() and
 | 
				
			||||||
 | 
					 * synchronize_srcu_expedited() calls must have returned before the call
 | 
				
			||||||
 | 
					 * to cleanup_srcu_struct_quiesced().  It also means that the callback
 | 
				
			||||||
 | 
					 * from the last call_srcu() must have been invoked before the call to
 | 
				
			||||||
 | 
					 * cleanup_srcu_struct_quiesced(), but you can use srcu_barrier() to help
 | 
				
			||||||
 | 
					 * with this last.  Violating these rules will get you a WARN_ON() splat
 | 
				
			||||||
 | 
					 * (with high probability, anyway), and will also cause the srcu_struct
 | 
				
			||||||
 | 
					 * to be leaked.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline void cleanup_srcu_struct_quiesced(struct srcu_struct *sp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_cleanup_srcu_struct(sp, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
 | 
					#ifdef CONFIG_DEBUG_LOCK_ALLOC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -593,7 +593,12 @@ static void srcu_torture_init(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void srcu_torture_cleanup(void)
 | 
					static void srcu_torture_cleanup(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	cleanup_srcu_struct(&srcu_ctld);
 | 
						static DEFINE_TORTURE_RANDOM(rand);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (torture_random(&rand) & 0x800)
 | 
				
			||||||
 | 
							cleanup_srcu_struct(&srcu_ctld);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							cleanup_srcu_struct_quiesced(&srcu_ctld);
 | 
				
			||||||
	srcu_ctlp = &srcu_ctl; /* In case of a later rcutorture run. */
 | 
						srcu_ctlp = &srcu_ctl; /* In case of a later rcutorture run. */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,16 +86,19 @@ EXPORT_SYMBOL_GPL(init_srcu_struct);
 | 
				
			||||||
 * Must invoke this after you are finished using a given srcu_struct that
 | 
					 * Must invoke this after you are finished using a given srcu_struct that
 | 
				
			||||||
 * was initialized via init_srcu_struct(), else you leak memory.
 | 
					 * was initialized via init_srcu_struct(), else you leak memory.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void cleanup_srcu_struct(struct srcu_struct *sp)
 | 
					void _cleanup_srcu_struct(struct srcu_struct *sp, bool quiesced)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	WARN_ON(sp->srcu_lock_nesting[0] || sp->srcu_lock_nesting[1]);
 | 
						WARN_ON(sp->srcu_lock_nesting[0] || sp->srcu_lock_nesting[1]);
 | 
				
			||||||
	flush_work(&sp->srcu_work);
 | 
						if (quiesced)
 | 
				
			||||||
 | 
							WARN_ON(work_pending(&sp->srcu_work));
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							flush_work(&sp->srcu_work);
 | 
				
			||||||
	WARN_ON(sp->srcu_gp_running);
 | 
						WARN_ON(sp->srcu_gp_running);
 | 
				
			||||||
	WARN_ON(sp->srcu_gp_waiting);
 | 
						WARN_ON(sp->srcu_gp_waiting);
 | 
				
			||||||
	WARN_ON(sp->srcu_cb_head);
 | 
						WARN_ON(sp->srcu_cb_head);
 | 
				
			||||||
	WARN_ON(&sp->srcu_cb_head != sp->srcu_cb_tail);
 | 
						WARN_ON(&sp->srcu_cb_head != sp->srcu_cb_tail);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(cleanup_srcu_struct);
 | 
					EXPORT_SYMBOL_GPL(_cleanup_srcu_struct);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Removes the count for the old reader from the appropriate element of
 | 
					 * Removes the count for the old reader from the appropriate element of
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -366,24 +366,28 @@ static unsigned long srcu_get_delay(struct srcu_struct *sp)
 | 
				
			||||||
	return SRCU_INTERVAL;
 | 
						return SRCU_INTERVAL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/* Helper for cleanup_srcu_struct() and cleanup_srcu_struct_quiesced(). */
 | 
				
			||||||
 * cleanup_srcu_struct - deconstruct a sleep-RCU structure
 | 
					void _cleanup_srcu_struct(struct srcu_struct *sp, bool quiesced)
 | 
				
			||||||
 * @sp: structure to clean up.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Must invoke this after you are finished using a given srcu_struct that
 | 
					 | 
				
			||||||
 * was initialized via init_srcu_struct(), else you leak memory.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void cleanup_srcu_struct(struct srcu_struct *sp)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int cpu;
 | 
						int cpu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (WARN_ON(!srcu_get_delay(sp)))
 | 
						if (WARN_ON(!srcu_get_delay(sp)))
 | 
				
			||||||
		return; /* Leakage unless caller handles error. */
 | 
							return; /* Just leak it! */
 | 
				
			||||||
	if (WARN_ON(srcu_readers_active(sp)))
 | 
						if (WARN_ON(srcu_readers_active(sp)))
 | 
				
			||||||
		return; /* Leakage unless caller handles error. */
 | 
							return; /* Just leak it! */
 | 
				
			||||||
	flush_delayed_work(&sp->work);
 | 
						if (quiesced) {
 | 
				
			||||||
 | 
							if (WARN_ON(delayed_work_pending(&sp->work)))
 | 
				
			||||||
 | 
								return; /* Just leak it! */
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							flush_delayed_work(&sp->work);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	for_each_possible_cpu(cpu)
 | 
						for_each_possible_cpu(cpu)
 | 
				
			||||||
		flush_delayed_work(&per_cpu_ptr(sp->sda, cpu)->work);
 | 
							if (quiesced) {
 | 
				
			||||||
 | 
								if (WARN_ON(delayed_work_pending(&per_cpu_ptr(sp->sda, cpu)->work)))
 | 
				
			||||||
 | 
									return; /* Just leak it! */
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								flush_delayed_work(&per_cpu_ptr(sp->sda, cpu)->work);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	if (WARN_ON(rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)) != SRCU_STATE_IDLE) ||
 | 
						if (WARN_ON(rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)) != SRCU_STATE_IDLE) ||
 | 
				
			||||||
	    WARN_ON(srcu_readers_active(sp))) {
 | 
						    WARN_ON(srcu_readers_active(sp))) {
 | 
				
			||||||
		pr_info("%s: Active srcu_struct %p state: %d\n", __func__, sp, rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)));
 | 
							pr_info("%s: Active srcu_struct %p state: %d\n", __func__, sp, rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)));
 | 
				
			||||||
| 
						 | 
					@ -392,7 +396,7 @@ void cleanup_srcu_struct(struct srcu_struct *sp)
 | 
				
			||||||
	free_percpu(sp->sda);
 | 
						free_percpu(sp->sda);
 | 
				
			||||||
	sp->sda = NULL;
 | 
						sp->sda = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(cleanup_srcu_struct);
 | 
					EXPORT_SYMBOL_GPL(_cleanup_srcu_struct);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Counts the new reader in the appropriate per-CPU element of the
 | 
					 * Counts the new reader in the appropriate per-CPU element of the
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue