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