|
@@ -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;
|