mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	rcu: Add RCU CPU stall notifier
It is sometimes helpful to have a way for the subsystem causing the stall to dump its state when an RCU CPU stall occurs. This commit therefore bases rcu_stall_chain_notifier_register() and rcu_stall_chain_notifier_unregister() on atomic notifiers in order to provide this functionality. Signed-off-by: Paul E. McKenney <paulmck@kernel.org> Cc: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
This commit is contained in:
		
							parent
							
								
									243d5ab344
								
							
						
					
					
						commit
						5b404fdaba
					
				
					 4 changed files with 101 additions and 2 deletions
				
			
		
							
								
								
									
										32
									
								
								include/linux/rcu_notifier.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								include/linux/rcu_notifier.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0+ */ | ||||
| /*
 | ||||
|  * Read-Copy Update notifiers, initially RCU CPU stall notifier. | ||||
|  * Separate from rcupdate.h to avoid #include loops. | ||||
|  * | ||||
|  * Copyright (C) 2023 Paul E. McKenney. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __LINUX_RCU_NOTIFIER_H | ||||
| #define __LINUX_RCU_NOTIFIER_H | ||||
| 
 | ||||
| // Actions for RCU CPU stall notifier calls.
 | ||||
| #define RCU_STALL_NOTIFY_NORM	1 | ||||
| #define RCU_STALL_NOTIFY_EXP	2 | ||||
| 
 | ||||
| #ifdef CONFIG_RCU_STALL_COMMON | ||||
| 
 | ||||
| #include <linux/notifier.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| int rcu_stall_chain_notifier_register(struct notifier_block *n); | ||||
| int rcu_stall_chain_notifier_unregister(struct notifier_block *n); | ||||
| 
 | ||||
| #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| 
 | ||||
| // No RCU CPU stall warnings in Tiny RCU.
 | ||||
| static inline int rcu_stall_chain_notifier_register(struct notifier_block *n) { return -EEXIST; } | ||||
| static inline int rcu_stall_chain_notifier_unregister(struct notifier_block *n) { return -ENOENT; } | ||||
| 
 | ||||
| #endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| 
 | ||||
| #endif /* __LINUX_RCU_NOTIFIER_H */ | ||||
|  | @ -654,4 +654,10 @@ static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; } | |||
| bool rcu_cpu_beenfullyonline(int cpu); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_RCU_STALL_COMMON | ||||
| int rcu_stall_notifier_call_chain(unsigned long val, void *v); | ||||
| #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| static inline int rcu_stall_notifier_call_chain(unsigned long val, void *v) { return NOTIFY_DONE; } | ||||
| #endif // #else // #ifdef CONFIG_RCU_STALL_COMMON
 | ||||
| 
 | ||||
| #endif /* __LINUX_RCU_H */ | ||||
|  |  | |||
|  | @ -621,10 +621,14 @@ static void synchronize_rcu_expedited_wait(void) | |||
| 	} | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		unsigned long j; | ||||
| 
 | ||||
| 		if (synchronize_rcu_expedited_wait_once(jiffies_stall)) | ||||
| 			return; | ||||
| 		if (rcu_stall_is_suppressed()) | ||||
| 			continue; | ||||
| 		j = jiffies; | ||||
| 		rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_EXP, (void *)(j - jiffies_start)); | ||||
| 		trace_rcu_stall_warning(rcu_state.name, TPS("ExpeditedStall")); | ||||
| 		pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {", | ||||
| 		       rcu_state.name); | ||||
|  | @ -647,7 +651,7 @@ static void synchronize_rcu_expedited_wait(void) | |||
| 			} | ||||
| 		} | ||||
| 		pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n", | ||||
| 			jiffies - jiffies_start, rcu_state.expedited_sequence, | ||||
| 			j - jiffies_start, rcu_state.expedited_sequence, | ||||
| 			data_race(rnp_root->expmask), | ||||
| 			".T"[!!data_race(rnp_root->exp_tasks)]); | ||||
| 		if (ndetected) { | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include <linux/kvm_para.h> | ||||
| #include <linux/rcu_notifier.h> | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////////
 | ||||
| //
 | ||||
|  | @ -770,6 +771,7 @@ static void check_cpu_stall(struct rcu_data *rdp) | |||
| 		if (kvm_check_and_clear_guest_paused()) | ||||
| 			return; | ||||
| 
 | ||||
| 		rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_NORM, (void *)j - gps); | ||||
| 		if (self_detected) { | ||||
| 			/* We haven't checked in, so go dump stack. */ | ||||
| 			print_cpu_stall(gps); | ||||
|  | @ -790,7 +792,7 @@ static void check_cpu_stall(struct rcu_data *rdp) | |||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////////
 | ||||
| //
 | ||||
| // RCU forward-progress mechanisms, including of callback invocation.
 | ||||
| // RCU forward-progress mechanisms, including for callback invocation.
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -1042,3 +1044,58 @@ static int __init rcu_sysrq_init(void) | |||
| 	return 0; | ||||
| } | ||||
| early_initcall(rcu_sysrq_init); | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////////////
 | ||||
| //
 | ||||
| // RCU CPU stall-warning notifiers
 | ||||
| 
 | ||||
| static ATOMIC_NOTIFIER_HEAD(rcu_cpu_stall_notifier_list); | ||||
| 
 | ||||
| /**
 | ||||
|  * rcu_stall_chain_notifier_register - Add an RCU CPU stall notifier | ||||
|  * @n: Entry to add. | ||||
|  * | ||||
|  * Adds an RCU CPU stall notifier to an atomic notifier chain. | ||||
|  * The @action passed to a notifier will be @RCU_STALL_NOTIFY_NORM or | ||||
|  * friends.  The @data will be the duration of the stalled grace period, | ||||
|  * in jiffies, coerced to a void* pointer. | ||||
|  * | ||||
|  * Returns 0 on success, %-EEXIST on error. | ||||
|  */ | ||||
| int rcu_stall_chain_notifier_register(struct notifier_block *n) | ||||
| { | ||||
| 	return atomic_notifier_chain_register(&rcu_cpu_stall_notifier_list, n); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_register); | ||||
| 
 | ||||
| /**
 | ||||
|  * rcu_stall_chain_notifier_unregister - Remove an RCU CPU stall notifier | ||||
|  * @n: Entry to add. | ||||
|  * | ||||
|  * Removes an RCU CPU stall notifier from an atomic notifier chain. | ||||
|  * | ||||
|  * Returns zero on success, %-ENOENT on failure. | ||||
|  */ | ||||
| int rcu_stall_chain_notifier_unregister(struct notifier_block *n) | ||||
| { | ||||
| 	return atomic_notifier_chain_unregister(&rcu_cpu_stall_notifier_list, n); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(rcu_stall_chain_notifier_unregister); | ||||
| 
 | ||||
| /*
 | ||||
|  * rcu_stall_notifier_call_chain - Call functions in an RCU CPU stall notifier chain | ||||
|  * @val: Value passed unmodified to notifier function | ||||
|  * @v: Pointer passed unmodified to notifier function | ||||
|  * | ||||
|  * Calls each function in the RCU CPU stall notifier chain in turn, which | ||||
|  * is an atomic call chain.  See atomic_notifier_call_chain() for more | ||||
|  * information. | ||||
|  * | ||||
|  * This is for use within RCU, hence the omission of the extra asterisk | ||||
|  * to indicate a non-kerneldoc format header comment. | ||||
|  */ | ||||
| int rcu_stall_notifier_call_chain(unsigned long val, void *v) | ||||
| { | ||||
| 	return atomic_notifier_call_chain(&rcu_cpu_stall_notifier_list, val, v); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Paul E. McKenney
						Paul E. McKenney