forked from mirrors/linux
		
	ftrace: Add selftest to test function trace recursion protection
Add selftests to test the function tracing recursion protection actually does work. It also tests if a ftrace_ops states it will perform its own protection. Although, even if the ftrace_ops states it will protect itself, the ftrace infrastructure may still provide protection if the arch does not support all features or another ftrace_ops is registered. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
		
							parent
							
								
									47239c4d8d
								
							
						
					
					
						commit
						ea701f11da
					
				
					 3 changed files with 163 additions and 0 deletions
				
			
		|  | @ -220,6 +220,10 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1, | |||
|  */ | ||||
| #define register_ftrace_function(ops) ({ 0; }) | ||||
| #define unregister_ftrace_function(ops) ({ 0; }) | ||||
| static inline int ftrace_nr_registered_ops(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| static inline void clear_ftrace_function(void) { } | ||||
| static inline void ftrace_kill(void) { } | ||||
| static inline void ftrace_stop(void) { } | ||||
|  | @ -275,6 +279,8 @@ extern void unregister_ftrace_function_probe_all(char *glob); | |||
| 
 | ||||
| extern int ftrace_text_reserved(void *start, void *end); | ||||
| 
 | ||||
| extern int ftrace_nr_registered_ops(void); | ||||
| 
 | ||||
| /*
 | ||||
|  * The dyn_ftrace record's flags field is split into two parts. | ||||
|  * the first part which is '0-FTRACE_REF_MAX' is a counter of | ||||
|  |  | |||
|  | @ -111,6 +111,27 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip); | |||
| #define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops) | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * ftrace_nr_registered_ops - return number of ops registered | ||||
|  * | ||||
|  * Returns the number of ftrace_ops registered and tracing functions | ||||
|  */ | ||||
| int ftrace_nr_registered_ops(void) | ||||
| { | ||||
| 	struct ftrace_ops *ops; | ||||
| 	int cnt = 0; | ||||
| 
 | ||||
| 	mutex_lock(&ftrace_lock); | ||||
| 
 | ||||
| 	for (ops = ftrace_ops_list; | ||||
| 	     ops != &ftrace_list_end; ops = ops->next) | ||||
| 		cnt++; | ||||
| 
 | ||||
| 	mutex_unlock(&ftrace_lock); | ||||
| 
 | ||||
| 	return cnt; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Traverse the ftrace_global_list, invoking all entries.  The reason that we | ||||
|  * can use rcu_dereference_raw() is that elements removed from this list | ||||
|  |  | |||
|  | @ -406,8 +406,141 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, | |||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int trace_selftest_recursion_cnt; | ||||
| static void trace_selftest_test_recursion_func(unsigned long ip, | ||||
| 					       unsigned long pip, | ||||
| 					       struct ftrace_ops *op, | ||||
| 					       struct pt_regs *pt_regs) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * This function is registered without the recursion safe flag. | ||||
| 	 * The ftrace infrastructure should provide the recursion | ||||
| 	 * protection. If not, this will crash the kernel! | ||||
| 	 */ | ||||
| 	trace_selftest_recursion_cnt++; | ||||
| 	DYN_FTRACE_TEST_NAME(); | ||||
| } | ||||
| 
 | ||||
| static void trace_selftest_test_recursion_safe_func(unsigned long ip, | ||||
| 						    unsigned long pip, | ||||
| 						    struct ftrace_ops *op, | ||||
| 						    struct pt_regs *pt_regs) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * We said we would provide our own recursion. By calling | ||||
| 	 * this function again, we should recurse back into this function | ||||
| 	 * and count again. But this only happens if the arch supports | ||||
| 	 * all of ftrace features and nothing else is using the function | ||||
| 	 * tracing utility. | ||||
| 	 */ | ||||
| 	if (trace_selftest_recursion_cnt++) | ||||
| 		return; | ||||
| 	DYN_FTRACE_TEST_NAME(); | ||||
| } | ||||
| 
 | ||||
| static struct ftrace_ops test_rec_probe = { | ||||
| 	.func			= trace_selftest_test_recursion_func, | ||||
| }; | ||||
| 
 | ||||
| static struct ftrace_ops test_recsafe_probe = { | ||||
| 	.func			= trace_selftest_test_recursion_safe_func, | ||||
| 	.flags			= FTRACE_OPS_FL_RECURSION_SAFE, | ||||
| }; | ||||
| 
 | ||||
| static int | ||||
| trace_selftest_function_recursion(void) | ||||
| { | ||||
| 	int save_ftrace_enabled = ftrace_enabled; | ||||
| 	int save_tracer_enabled = tracer_enabled; | ||||
| 	char *func_name; | ||||
| 	int len; | ||||
| 	int ret; | ||||
| 	int cnt; | ||||
| 
 | ||||
| 	/* The previous test PASSED */ | ||||
| 	pr_cont("PASSED\n"); | ||||
| 	pr_info("Testing ftrace recursion: "); | ||||
| 
 | ||||
| 
 | ||||
| 	/* enable tracing, and record the filter function */ | ||||
| 	ftrace_enabled = 1; | ||||
| 	tracer_enabled = 1; | ||||
| 
 | ||||
| 	/* Handle PPC64 '.' name */ | ||||
| 	func_name = "*" __stringify(DYN_FTRACE_TEST_NAME); | ||||
| 	len = strlen(func_name); | ||||
| 
 | ||||
| 	ret = ftrace_set_filter(&test_rec_probe, func_name, len, 1); | ||||
| 	if (ret) { | ||||
| 		pr_cont("*Could not set filter* "); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = register_ftrace_function(&test_rec_probe); | ||||
| 	if (ret) { | ||||
| 		pr_cont("*could not register callback* "); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	DYN_FTRACE_TEST_NAME(); | ||||
| 
 | ||||
| 	unregister_ftrace_function(&test_rec_probe); | ||||
| 
 | ||||
| 	ret = -1; | ||||
| 	if (trace_selftest_recursion_cnt != 1) { | ||||
| 		pr_cont("*callback not called once (%d)* ", | ||||
| 			trace_selftest_recursion_cnt); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	trace_selftest_recursion_cnt = 1; | ||||
| 
 | ||||
| 	pr_cont("PASSED\n"); | ||||
| 	pr_info("Testing ftrace recursion safe: "); | ||||
| 
 | ||||
| 	ret = ftrace_set_filter(&test_recsafe_probe, func_name, len, 1); | ||||
| 	if (ret) { | ||||
| 		pr_cont("*Could not set filter* "); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = register_ftrace_function(&test_recsafe_probe); | ||||
| 	if (ret) { | ||||
| 		pr_cont("*could not register callback* "); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	DYN_FTRACE_TEST_NAME(); | ||||
| 
 | ||||
| 	unregister_ftrace_function(&test_recsafe_probe); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If arch supports all ftrace features, and no other task | ||||
| 	 * was on the list, we should be fine. | ||||
| 	 */ | ||||
| 	if (!ftrace_nr_registered_ops() && !FTRACE_FORCE_LIST_FUNC) | ||||
| 		cnt = 2; /* Should have recursed */ | ||||
| 	else | ||||
| 		cnt = 1; | ||||
| 
 | ||||
| 	ret = -1; | ||||
| 	if (trace_selftest_recursion_cnt != cnt) { | ||||
| 		pr_cont("*callback not called expected %d times (%d)* ", | ||||
| 			cnt, trace_selftest_recursion_cnt); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = 0; | ||||
| out: | ||||
| 	ftrace_enabled = save_ftrace_enabled; | ||||
| 	tracer_enabled = save_tracer_enabled; | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| #else | ||||
| # define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; }) | ||||
| # define trace_selftest_function_recursion() ({ 0; }) | ||||
| #endif /* CONFIG_DYNAMIC_FTRACE */ | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -455,7 +588,10 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) | |||
| 
 | ||||
| 	ret = trace_selftest_startup_dynamic_tracing(trace, tr, | ||||
| 						     DYN_FTRACE_TEST_NAME); | ||||
| 	if (ret) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	ret = trace_selftest_function_recursion(); | ||||
|  out: | ||||
| 	ftrace_enabled = save_ftrace_enabled; | ||||
| 	tracer_enabled = save_tracer_enabled; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Steven Rostedt
						Steven Rostedt