diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/trace/ftrace.c | 21 | ||||
| -rw-r--r-- | kernel/trace/trace_selftest.c | 136 | 
2 files changed, 157 insertions, 0 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index ad765b4ba42..528d997c7f9 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -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 diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 1fb6da85ff8..86422f91dbe 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -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;  |