From: Song Chen <[email protected]>
Like kprobe, fprobe and btf, this patch attempts to introduce
a notifier_block for ftrace to decouple its initialization from
load_module.
Below is the table of ftrace fucntions calls in different
module state:
MODULE_STATE_UNFORMED ftrace_module_init
MODULE_STATE_COMING ftrace_module_enable
MODULE_STATE_LIVE ftrace_free_mem
MODULE_STATE_GOING ftrace_release_mod
Unlike others, ftrace module notifier must take care of state
MODULE_STATE_UNFORMED to ensure calling ftrace_module_init
before complete_formation which changes module's text property.
That pretty much remains same logic with its original design,
the only thing that changes is blocking_notifier_call_chain
(MODULE_STATE_GOING) has to be moved from coming_cleanup to
ddebug_cleanup in function load_module to ensure
ftrace_release_mod is invoked in case complete_formation fails.
Signed-off-by: Song Chen <[email protected]>
---
kernel/module/main.c | 14 ++++----------
kernel/trace/ftrace.c | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 710ee30b3bea..5dc0a980e9bd 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -45,7 +45,6 @@
#include <linux/license.h>
#include <asm/sections.h>
#include <linux/tracepoint.h>
-#include <linux/ftrace.h>
#include <linux/livepatch.h>
#include <linux/async.h>
#include <linux/percpu.h>
@@ -836,7 +835,6 @@ SYSCALL_DEFINE2(delete_module, const char __user *,
name_user,
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod);
klp_module_going(mod);
- ftrace_release_mod(mod);
async_synchronize_full();
@@ -3067,8 +3065,6 @@ static noinline int do_init_module(struct module *mod)
if (!mod->async_probe_requested)
async_synchronize_full();
- ftrace_free_mem(mod, mod->mem[MOD_INIT_TEXT].base,
- mod->mem[MOD_INIT_TEXT].base +
mod->mem[MOD_INIT_TEXT].size);
mutex_lock(&module_mutex);
/* Drop initial reference. */
module_put(mod);
@@ -3131,7 +3127,6 @@ static noinline int do_init_module(struct module *mod)
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod);
klp_module_going(mod);
- ftrace_release_mod(mod);
free_module(mod);
wake_up_all(&module_wq);
@@ -3278,7 +3273,6 @@ static int prepare_coming_module(struct module *mod)
{
int err;
- ftrace_module_enable(mod);
err = klp_module_coming(mod);
if (err)
return err;
@@ -3461,7 +3455,8 @@ static int load_module(struct load_info *info, const char
__user *uargs,
init_build_id(mod, info);
/* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
- ftrace_module_init(mod);
+ blocking_notifier_call_chain(&module_notify_list,
+ MODULE_STATE_UNFORMED, mod);
/* Finally it's fully formed, ready to start executing. */
err = complete_formation(mod, info);
@@ -3513,8 +3508,6 @@ static int load_module(struct load_info *info, const char
__user *uargs,
coming_cleanup:
mod->state = MODULE_STATE_GOING;
destroy_params(mod->kp, mod->num_kp);
- blocking_notifier_call_chain(&module_notify_list,
- MODULE_STATE_GOING, mod);
klp_module_going(mod);
bug_cleanup:
mod->state = MODULE_STATE_GOING;
@@ -3524,7 +3517,8 @@ static int load_module(struct load_info *info, const char
__user *uargs,
mutex_unlock(&module_mutex);
ddebug_cleanup:
- ftrace_release_mod(mod);
+ blocking_notifier_call_chain(&module_notify_list,
+ MODULE_STATE_GOING, mod);
synchronize_rcu();
kfree(mod->args);
free_arch_cleanup:
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 3ec2033c0774..47c74d4a2425 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -5223,6 +5223,43 @@ static int __init ftrace_mod_cmd_init(void)
}
core_initcall(ftrace_mod_cmd_init);
+static int ftrace_module_callback(struct notifier_block *nb, unsigned long op,
+ void *module)
+{
+ struct module *mod = module;
+
+ switch (op) {
+ case MODULE_STATE_UNFORMED:
+ ftrace_module_init(mod);
+ break;
+ case MODULE_STATE_COMING:
+ ftrace_module_enable(mod);
+ break;
+ case MODULE_STATE_LIVE:
+ ftrace_free_mem(mod, mod->mem[MOD_INIT_TEXT].base,
+ mod->mem[MOD_INIT_TEXT].base +
mod->mem[MOD_INIT_TEXT].size);
+ break;
+ case MODULE_STATE_GOING:
+ ftrace_release_mod(mod);
+ break;
+ default:
+ break;
+ }
+
+ return notifier_from_errno(0);
+}
+
+static struct notifier_block ftrace_module_nb = {
+ .notifier_call = ftrace_module_callback,
+ .priority = 0
+};
+
+static int __init ftrace_register_module_notifier(void)
+{
+ return register_module_notifier(&ftrace_module_nb);
+}
+core_initcall(ftrace_register_module_notifier);
+
static void function_trace_probe_call(unsigned long ip, unsigned long
parent_ip,
struct ftrace_ops *op, struct ftrace_regs
*fregs)
{
--
2.43.0