Hi,

在 2026/2/26 08:27, Steven Rostedt 写道:
On Wed, 25 Feb 2026 13:46:39 +0800
[email protected] wrote:

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

Is the above safe? klp uses ftrace. That means klp_module_going() may
need to be called before ftrace_release_mod(). That said, I wonder if
klp_module_going() could be moved into ftrace_release_mod()?


I didn't test with klp, so i'm not sure if it's safe. But i consider klp is the other part which should be decoupled after ftrace and klp should introduce its own notifier.

If klp_module_going must be running before ftrace_release_mod, i can try to use priority in notifier_block to ensure their order.

Let me see if there is any way to use notifier and remain below calling sequence:

ftrace_module_enable
klp_module_coming
blocking_notifier_call_chain_robust(MODULE_STATE_COMING)

blocking_notifier_call_chain(MODULE_STATE_GOING)
klp_module_going
ftrace_release_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);

Have you tested the case for why this is called? It has to be called
before the module frees the kallsyms. It's for tracing the module's
init functions.

   cd /sys/kernel/tracing
   echo :mod:<module> > set_ftrace_filter
   echo function > current_tracer
   modprobe <module>
   cat trace

You should see the init functions of the module loaded. If
ftrace_free_mem() is called after the module frees the kallsyms of the
module init functions, you'll just get garbage for the init function
names.



Yes, after applying this patch, i tested it with your above commands, result is:

cat trace_pipe
<...>-4027 [004] ..... 103.171161: mem_blkdev_init <-do_one_initcall insmod-4027 [004] ..... 103.249854: mem_blkdev_queue_rq <-blk_mq_dispatch_rq_list insmod-4027 [004] ..... 103.249865: mem_blkdev_queue_rq <-blk_mq_dispatch_rq_list
....
module init function can be seen when module is being loaded.

As far as my understanding, ftrace_free_mem is called right after blocking_notifier_call_chain(MODULE_STATE_LIVE) originally in do_init_module, after this patch, it's called by notifier, almost nothing changed, so no impact to current calling sequence.

Correct me if I'm wrong.


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

Same issue with ftrace and klp here.

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

Now klp_module_going() may need to be called *after* the
MODULE_STATE_GOING callbacks and *before* ftrace_release_mod(). But
again, if that's moved into ftrace_release_mod() it may be fine.

   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

-- Steve



Reply via email to