diff options
Diffstat (limited to 'kernel/trace/trace_selftest.c')
| -rw-r--r-- | kernel/trace/trace_selftest.c | 304 | 
1 files changed, 283 insertions, 21 deletions
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 288541f977f..2c00a691a54 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -103,54 +103,67 @@ static inline void warn_failed_init_tracer(struct tracer *trace, int init_ret)  static int trace_selftest_test_probe1_cnt;  static void trace_selftest_test_probe1_func(unsigned long ip, -					    unsigned long pip) +					    unsigned long pip, +					    struct ftrace_ops *op, +					    struct pt_regs *pt_regs)  {  	trace_selftest_test_probe1_cnt++;  }  static int trace_selftest_test_probe2_cnt;  static void trace_selftest_test_probe2_func(unsigned long ip, -					    unsigned long pip) +					    unsigned long pip, +					    struct ftrace_ops *op, +					    struct pt_regs *pt_regs)  {  	trace_selftest_test_probe2_cnt++;  }  static int trace_selftest_test_probe3_cnt;  static void trace_selftest_test_probe3_func(unsigned long ip, -					    unsigned long pip) +					    unsigned long pip, +					    struct ftrace_ops *op, +					    struct pt_regs *pt_regs)  {  	trace_selftest_test_probe3_cnt++;  }  static int trace_selftest_test_global_cnt;  static void trace_selftest_test_global_func(unsigned long ip, -					    unsigned long pip) +					    unsigned long pip, +					    struct ftrace_ops *op, +					    struct pt_regs *pt_regs)  {  	trace_selftest_test_global_cnt++;  }  static int trace_selftest_test_dyn_cnt;  static void trace_selftest_test_dyn_func(unsigned long ip, -					 unsigned long pip) +					 unsigned long pip, +					 struct ftrace_ops *op, +					 struct pt_regs *pt_regs)  {  	trace_selftest_test_dyn_cnt++;  }  static struct ftrace_ops test_probe1 = {  	.func			= trace_selftest_test_probe1_func, +	.flags			= FTRACE_OPS_FL_RECURSION_SAFE,  };  static struct ftrace_ops test_probe2 = {  	.func			= trace_selftest_test_probe2_func, +	.flags			= FTRACE_OPS_FL_RECURSION_SAFE,  };  static struct ftrace_ops test_probe3 = {  	.func			= trace_selftest_test_probe3_func, +	.flags			= FTRACE_OPS_FL_RECURSION_SAFE,  };  static struct ftrace_ops test_global = { -	.func			= trace_selftest_test_global_func, -	.flags			= FTRACE_OPS_FL_GLOBAL, +	.func		= trace_selftest_test_global_func, +	.flags		= FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,  };  static void print_counts(void) @@ -393,10 +406,253 @@ 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 */ +static enum { +	TRACE_SELFTEST_REGS_START, +	TRACE_SELFTEST_REGS_FOUND, +	TRACE_SELFTEST_REGS_NOT_FOUND, +} trace_selftest_regs_stat; + +static void trace_selftest_test_regs_func(unsigned long ip, +					  unsigned long pip, +					  struct ftrace_ops *op, +					  struct pt_regs *pt_regs) +{ +	if (pt_regs) +		trace_selftest_regs_stat = TRACE_SELFTEST_REGS_FOUND; +	else +		trace_selftest_regs_stat = TRACE_SELFTEST_REGS_NOT_FOUND; +} + +static struct ftrace_ops test_regs_probe = { +	.func		= trace_selftest_test_regs_func, +	.flags		= FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_SAVE_REGS, +}; + +static int +trace_selftest_function_regs(void) +{ +	int save_ftrace_enabled = ftrace_enabled; +	int save_tracer_enabled = tracer_enabled; +	char *func_name; +	int len; +	int ret; +	int supported = 0; + +#ifdef ARCH_SUPPORTS_FTRACE_SAVE_REGS +	supported = 1; +#endif + +	/* The previous test PASSED */ +	pr_cont("PASSED\n"); +	pr_info("Testing ftrace regs%s: ", +		!supported ? "(no arch support)" : ""); + +	/* 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_regs_probe, func_name, len, 1); +	/* +	 * If DYNAMIC_FTRACE is not set, then we just trace all functions. +	 * This test really doesn't care. +	 */ +	if (ret && ret != -ENODEV) { +		pr_cont("*Could not set filter* "); +		goto out; +	} + +	ret = register_ftrace_function(&test_regs_probe); +	/* +	 * Now if the arch does not support passing regs, then this should +	 * have failed. +	 */ +	if (!supported) { +		if (!ret) { +			pr_cont("*registered save-regs without arch support* "); +			goto out; +		} +		test_regs_probe.flags |= FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED; +		ret = register_ftrace_function(&test_regs_probe); +	} +	if (ret) { +		pr_cont("*could not register callback* "); +		goto out; +	} + + +	DYN_FTRACE_TEST_NAME(); + +	unregister_ftrace_function(&test_regs_probe); + +	ret = -1; + +	switch (trace_selftest_regs_stat) { +	case TRACE_SELFTEST_REGS_START: +		pr_cont("*callback never called* "); +		goto out; + +	case TRACE_SELFTEST_REGS_FOUND: +		if (supported) +			break; +		pr_cont("*callback received regs without arch support* "); +		goto out; + +	case TRACE_SELFTEST_REGS_NOT_FOUND: +		if (!supported) +			break; +		pr_cont("*callback received NULL regs* "); +		goto out; +	} + +	ret = 0; +out: +	ftrace_enabled = save_ftrace_enabled; +	tracer_enabled = save_tracer_enabled; + +	return ret; +} +  /*   * Simple verification test of ftrace function tracer.   * Enable ftrace, sleep 1/10 second, and then read the trace @@ -442,7 +698,14 @@ 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(); +	if (ret) +		goto out; + +	ret = trace_selftest_function_regs();   out:  	ftrace_enabled = save_ftrace_enabled;  	tracer_enabled = save_tracer_enabled; @@ -778,6 +1041,8 @@ static int trace_wakeup_test_thread(void *data)  	set_current_state(TASK_INTERRUPTIBLE);  	schedule(); +	complete(x); +  	/* we are awake, now wait to disappear */  	while (!kthread_should_stop()) {  		/* @@ -821,24 +1086,21 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)  	/* reset the max latency */  	tracing_max_latency = 0; -	/* sleep to let the RT thread sleep too */ -	msleep(100); +	while (p->on_rq) { +		/* +		 * Sleep to make sure the RT thread is asleep too. +		 * On virtual machines we can't rely on timings, +		 * but we want to make sure this test still works. +		 */ +		msleep(100); +	} -	/* -	 * Yes this is slightly racy. It is possible that for some -	 * strange reason that the RT thread we created, did not -	 * call schedule for 100ms after doing the completion, -	 * and we do a wakeup on a task that already is awake. -	 * But that is extremely unlikely, and the worst thing that -	 * happens in such a case, is that we disable tracing. -	 * Honestly, if this race does happen something is horrible -	 * wrong with the system. -	 */ +	init_completion(&isrt);  	wake_up_process(p); -	/* give a little time to let the thread wake up */ -	msleep(100); +	/* Wait for the task to wake up */ +	wait_for_completion(&isrt);  	/* stop the tracing. */  	tracing_stop();  |