forked from mirrors/linux
		
	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
	
	 Paul E. McKenney
						Paul E. McKenney