162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#define pr_fmt(fmt)       KBUILD_MODNAME ": " fmt
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/ftrace.h>
662306a36Sopenharmony_ci#include <linux/ktime.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <asm/barrier.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Arbitrary large value chosen to be sufficiently large to minimize noise but
1362306a36Sopenharmony_ci * sufficiently small to complete quickly.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_cistatic unsigned int nr_function_calls = 100000;
1662306a36Sopenharmony_cimodule_param(nr_function_calls, uint, 0);
1762306a36Sopenharmony_ciMODULE_PARM_DESC(nr_function_calls, "How many times to call the relevant tracee");
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * The number of ops associated with a call site affects whether a tracer can
2162306a36Sopenharmony_ci * be called directly or whether it's necessary to go via the list func, which
2262306a36Sopenharmony_ci * can be significantly more expensive.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_cistatic unsigned int nr_ops_relevant = 1;
2562306a36Sopenharmony_cimodule_param(nr_ops_relevant, uint, 0);
2662306a36Sopenharmony_ciMODULE_PARM_DESC(nr_ops_relevant, "How many ftrace_ops to associate with the relevant tracee");
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * On architectures where all call sites share the same trampoline, having
3062306a36Sopenharmony_ci * tracers enabled for distinct functions can force the use of the list func
3162306a36Sopenharmony_ci * and incur overhead for all call sites.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic unsigned int nr_ops_irrelevant;
3462306a36Sopenharmony_cimodule_param(nr_ops_irrelevant, uint, 0);
3562306a36Sopenharmony_ciMODULE_PARM_DESC(nr_ops_irrelevant, "How many ftrace_ops to associate with the irrelevant tracee");
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * On architectures with DYNAMIC_FTRACE_WITH_REGS, saving the full pt_regs can
3962306a36Sopenharmony_ci * be more expensive than only saving the minimal necessary regs.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_cistatic bool save_regs;
4262306a36Sopenharmony_cimodule_param(save_regs, bool, 0);
4362306a36Sopenharmony_ciMODULE_PARM_DESC(save_regs, "Register ops with FTRACE_OPS_FL_SAVE_REGS (save all registers in the trampoline)");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic bool assist_recursion;
4662306a36Sopenharmony_cimodule_param(assist_recursion, bool, 0);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(assist_reursion, "Register ops with FTRACE_OPS_FL_RECURSION");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic bool assist_rcu;
5062306a36Sopenharmony_cimodule_param(assist_rcu, bool, 0);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(assist_reursion, "Register ops with FTRACE_OPS_FL_RCU");
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * By default, a trivial tracer is used which immediately returns to mimimize
5562306a36Sopenharmony_ci * overhead. Sometimes a consistency check using a more expensive tracer is
5662306a36Sopenharmony_ci * desireable.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_cistatic bool check_count;
5962306a36Sopenharmony_cimodule_param(check_count, bool, 0);
6062306a36Sopenharmony_ciMODULE_PARM_DESC(check_count, "Check that tracers are called the expected number of times\n");
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * Usually it's not interesting to leave the ops registered after the test
6462306a36Sopenharmony_ci * runs, but sometimes it can be useful to leave them registered so that they
6562306a36Sopenharmony_ci * can be inspected through the tracefs 'enabled_functions' file.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic bool persist;
6862306a36Sopenharmony_cimodule_param(persist, bool, 0);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(persist, "Successfully load module and leave ftrace ops registered after test completes\n");
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * Marked as noinline to ensure that an out-of-line traceable copy is
7362306a36Sopenharmony_ci * generated by the compiler.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * The barrier() ensures the compiler won't elide calls by determining there
7662306a36Sopenharmony_ci * are no side-effects.
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_cistatic noinline void tracee_relevant(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	barrier();
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * Marked as noinline to ensure that an out-of-line traceable copy is
8562306a36Sopenharmony_ci * generated by the compiler.
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * The barrier() ensures the compiler won't elide calls by determining there
8862306a36Sopenharmony_ci * are no side-effects.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic noinline void tracee_irrelevant(void)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	barrier();
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct sample_ops {
9662306a36Sopenharmony_ci	struct ftrace_ops ops;
9762306a36Sopenharmony_ci	unsigned int count;
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void ops_func_nop(unsigned long ip, unsigned long parent_ip,
10162306a36Sopenharmony_ci			 struct ftrace_ops *op,
10262306a36Sopenharmony_ci			 struct ftrace_regs *fregs)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	/* do nothing */
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void ops_func_count(unsigned long ip, unsigned long parent_ip,
10862306a36Sopenharmony_ci			   struct ftrace_ops *op,
10962306a36Sopenharmony_ci			   struct ftrace_regs *fregs)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct sample_ops *self;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	self = container_of(op, struct sample_ops, ops);
11462306a36Sopenharmony_ci	self->count++;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct sample_ops *ops_relevant;
11862306a36Sopenharmony_cistatic struct sample_ops *ops_irrelevant;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic struct sample_ops *ops_alloc_init(void *tracee, ftrace_func_t func,
12162306a36Sopenharmony_ci					 unsigned long flags, int nr)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct sample_ops *ops;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ops = kcalloc(nr, sizeof(*ops), GFP_KERNEL);
12662306a36Sopenharmony_ci	if (WARN_ON_ONCE(!ops))
12762306a36Sopenharmony_ci		return NULL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	for (unsigned int i = 0; i < nr; i++) {
13062306a36Sopenharmony_ci		ops[i].ops.func = func;
13162306a36Sopenharmony_ci		ops[i].ops.flags = flags;
13262306a36Sopenharmony_ci		WARN_ON_ONCE(ftrace_set_filter_ip(&ops[i].ops, (unsigned long)tracee, 0, 0));
13362306a36Sopenharmony_ci		WARN_ON_ONCE(register_ftrace_function(&ops[i].ops));
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return ops;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void ops_destroy(struct sample_ops *ops, int nr)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	if (!ops)
14262306a36Sopenharmony_ci		return;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (unsigned int i = 0; i < nr; i++) {
14562306a36Sopenharmony_ci		WARN_ON_ONCE(unregister_ftrace_function(&ops[i].ops));
14662306a36Sopenharmony_ci		ftrace_free_filter(&ops[i].ops);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	kfree(ops);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void ops_check(struct sample_ops *ops, int nr,
15362306a36Sopenharmony_ci		      unsigned int expected_count)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	if (!ops || !check_count)
15662306a36Sopenharmony_ci		return;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	for (unsigned int i = 0; i < nr; i++) {
15962306a36Sopenharmony_ci		if (ops->count == expected_count)
16062306a36Sopenharmony_ci			continue;
16162306a36Sopenharmony_ci		pr_warn("Counter called %u times (expected %u)\n",
16262306a36Sopenharmony_ci			ops->count, expected_count);
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic ftrace_func_t tracer_relevant = ops_func_nop;
16762306a36Sopenharmony_cistatic ftrace_func_t tracer_irrelevant = ops_func_nop;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int __init ftrace_ops_sample_init(void)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	unsigned long flags = 0;
17262306a36Sopenharmony_ci	ktime_t start, end;
17362306a36Sopenharmony_ci	u64 period;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS) && save_regs) {
17662306a36Sopenharmony_ci		pr_info("this kernel does not support saving registers\n");
17762306a36Sopenharmony_ci		save_regs = false;
17862306a36Sopenharmony_ci	} else if (save_regs) {
17962306a36Sopenharmony_ci		flags |= FTRACE_OPS_FL_SAVE_REGS;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (assist_recursion)
18362306a36Sopenharmony_ci		flags |= FTRACE_OPS_FL_RECURSION;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (assist_rcu)
18662306a36Sopenharmony_ci		flags |= FTRACE_OPS_FL_RCU;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (check_count) {
18962306a36Sopenharmony_ci		tracer_relevant = ops_func_count;
19062306a36Sopenharmony_ci		tracer_irrelevant = ops_func_count;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	pr_info("registering:\n"
19462306a36Sopenharmony_ci		"  relevant ops: %u\n"
19562306a36Sopenharmony_ci		"    tracee: %ps\n"
19662306a36Sopenharmony_ci		"    tracer: %ps\n"
19762306a36Sopenharmony_ci		"  irrelevant ops: %u\n"
19862306a36Sopenharmony_ci		"    tracee: %ps\n"
19962306a36Sopenharmony_ci		"    tracer: %ps\n"
20062306a36Sopenharmony_ci		"  saving registers: %s\n"
20162306a36Sopenharmony_ci		"  assist recursion: %s\n"
20262306a36Sopenharmony_ci		"  assist RCU: %s\n",
20362306a36Sopenharmony_ci		nr_ops_relevant, tracee_relevant, tracer_relevant,
20462306a36Sopenharmony_ci		nr_ops_irrelevant, tracee_irrelevant, tracer_irrelevant,
20562306a36Sopenharmony_ci		save_regs ? "YES" : "NO",
20662306a36Sopenharmony_ci		assist_recursion ? "YES" : "NO",
20762306a36Sopenharmony_ci		assist_rcu ? "YES" : "NO");
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	ops_relevant = ops_alloc_init(tracee_relevant, tracer_relevant,
21062306a36Sopenharmony_ci				      flags, nr_ops_relevant);
21162306a36Sopenharmony_ci	ops_irrelevant = ops_alloc_init(tracee_irrelevant, tracer_irrelevant,
21262306a36Sopenharmony_ci					flags, nr_ops_irrelevant);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	start = ktime_get();
21562306a36Sopenharmony_ci	for (unsigned int i = 0; i < nr_function_calls; i++)
21662306a36Sopenharmony_ci		tracee_relevant();
21762306a36Sopenharmony_ci	end = ktime_get();
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ops_check(ops_relevant, nr_ops_relevant, nr_function_calls);
22062306a36Sopenharmony_ci	ops_check(ops_irrelevant, nr_ops_irrelevant, 0);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	period = ktime_to_ns(ktime_sub(end, start));
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	pr_info("Attempted %u calls to %ps in %lluns (%lluns / call)\n",
22562306a36Sopenharmony_ci		nr_function_calls, tracee_relevant,
22662306a36Sopenharmony_ci		period, div_u64(period, nr_function_calls));
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (persist)
22962306a36Sopenharmony_ci		return 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	ops_destroy(ops_relevant, nr_ops_relevant);
23262306a36Sopenharmony_ci	ops_destroy(ops_irrelevant, nr_ops_irrelevant);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * The benchmark completed sucessfully, but there's no reason to keep
23662306a36Sopenharmony_ci	 * the module around. Return an error do the user doesn't have to
23762306a36Sopenharmony_ci	 * manually unload the module.
23862306a36Sopenharmony_ci	 */
23962306a36Sopenharmony_ci	return -EINVAL;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_cimodule_init(ftrace_ops_sample_init);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void __exit ftrace_ops_sample_exit(void)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	ops_destroy(ops_relevant, nr_ops_relevant);
24662306a36Sopenharmony_ci	ops_destroy(ops_irrelevant, nr_ops_irrelevant);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_cimodule_exit(ftrace_ops_sample_exit);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciMODULE_AUTHOR("Mark Rutland");
25162306a36Sopenharmony_ciMODULE_DESCRIPTION("Example of using custom ftrace_ops");
25262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
253