| From 8c189ea64eea01ca20d102ddb74d6936dd16c579 Mon Sep 17 00:00:00 2001 |
| From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org> |
| Date: Wed, 13 Feb 2013 15:18:38 -0500 |
| Subject: ftrace: Call ftrace cleanup module notifier after all other notifiers |
| |
| From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org> |
| |
| commit 8c189ea64eea01ca20d102ddb74d6936dd16c579 upstream. |
| |
| Commit: c1bf08ac "ftrace: Be first to run code modification on modules" |
| |
| changed ftrace module notifier's priority to INT_MAX in order to |
| process the ftrace nops before anything else could touch them |
| (namely kprobes). This was the correct thing to do. |
| |
| Unfortunately, the ftrace module notifier also contains the ftrace |
| clean up code. As opposed to the set up code, this code should be |
| run *after* all the module notifiers have run in case a module is doing |
| correct clean-up and unregisters its ftrace hooks. Basically, ftrace |
| needs to do clean up on module removal, as it needs to know about code |
| being removed so that it doesn't try to modify that code. But after it |
| removes the module from its records, if a ftrace user tries to remove |
| a probe, that removal will fail due as the record of that code segment |
| no longer exists. |
| |
| Nothing really bad happens if the probe removal is called after ftrace |
| did the clean up, but the ftrace removal function will return an error. |
| Correct code (such as kprobes) will produce a WARN_ON() if it fails |
| to remove the probe. As people get annoyed by frivolous warnings, it's |
| best to do the ftrace clean up after everything else. |
| |
| By splitting the ftrace_module_notifier into two notifiers, one that |
| does the module load setup that is run at high priority, and the other |
| that is called for module clean up that is run at low priority, the |
| problem is solved. |
| |
| Reported-by: Frank Ch. Eigler <fche@redhat.com> |
| Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> |
| Signed-off-by: Steven Rostedt <rostedt@goodmis.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/trace/ftrace.c | 46 ++++++++++++++++++++++++++++++++-------------- |
| 1 file changed, 32 insertions(+), 14 deletions(-) |
| |
| --- a/kernel/trace/ftrace.c |
| +++ b/kernel/trace/ftrace.c |
| @@ -3432,37 +3432,51 @@ static void ftrace_init_module(struct mo |
| ftrace_process_locs(mod, start, end); |
| } |
| |
| -static int ftrace_module_notify(struct notifier_block *self, |
| - unsigned long val, void *data) |
| +static int ftrace_module_notify_enter(struct notifier_block *self, |
| + unsigned long val, void *data) |
| { |
| struct module *mod = data; |
| |
| - switch (val) { |
| - case MODULE_STATE_COMING: |
| + if (val == MODULE_STATE_COMING) |
| ftrace_init_module(mod, mod->ftrace_callsites, |
| mod->ftrace_callsites + |
| mod->num_ftrace_callsites); |
| - break; |
| - case MODULE_STATE_GOING: |
| + return 0; |
| +} |
| + |
| +static int ftrace_module_notify_exit(struct notifier_block *self, |
| + unsigned long val, void *data) |
| +{ |
| + struct module *mod = data; |
| + |
| + if (val == MODULE_STATE_GOING) |
| ftrace_release_mod(mod); |
| - break; |
| - } |
| |
| return 0; |
| } |
| #else |
| -static int ftrace_module_notify(struct notifier_block *self, |
| - unsigned long val, void *data) |
| +static int ftrace_module_notify_enter(struct notifier_block *self, |
| + unsigned long val, void *data) |
| +{ |
| + return 0; |
| +} |
| +static int ftrace_module_notify_exit(struct notifier_block *self, |
| + unsigned long val, void *data) |
| { |
| return 0; |
| } |
| #endif /* CONFIG_MODULES */ |
| |
| -struct notifier_block ftrace_module_nb = { |
| - .notifier_call = ftrace_module_notify, |
| +struct notifier_block ftrace_module_enter_nb = { |
| + .notifier_call = ftrace_module_notify_enter, |
| .priority = INT_MAX, /* Run before anything that can use kprobes */ |
| }; |
| |
| +struct notifier_block ftrace_module_exit_nb = { |
| + .notifier_call = ftrace_module_notify_exit, |
| + .priority = INT_MIN, /* Run after anything that can remove kprobes */ |
| +}; |
| + |
| extern unsigned long __start_mcount_loc[]; |
| extern unsigned long __stop_mcount_loc[]; |
| |
| @@ -3494,9 +3508,13 @@ void __init ftrace_init(void) |
| __start_mcount_loc, |
| __stop_mcount_loc); |
| |
| - ret = register_module_notifier(&ftrace_module_nb); |
| + ret = register_module_notifier(&ftrace_module_enter_nb); |
| + if (ret) |
| + pr_warning("Failed to register trace ftrace module enter notifier\n"); |
| + |
| + ret = register_module_notifier(&ftrace_module_exit_nb); |
| if (ret) |
| - pr_warning("Failed to register trace ftrace module notifier\n"); |
| + pr_warning("Failed to register trace ftrace module exit notifier\n"); |
| |
| set_ftrace_early_filters(); |
| |