aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2013-02-13 15:18:38 -0500
committerSteven Rostedt <rostedt@goodmis.org>2013-02-18 23:09:26 -0500
commit8c189ea64eea01ca20d102ddb74d6936dd16c579 (patch)
tree7836952a838205f0e87327e58fe5e51c48ed01be /kernel
parentf431b634f24d099872e78acc356c7fd35913b36b (diff)
ftrace: Call ftrace cleanup module notifier after all other notifiers
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. Cc: stable@vger.kernel.org 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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/trace/ftrace.c46
1 files changed, 32 insertions, 14 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index ce8c3d68292f..98ca94a41819 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -3996,37 +3996,51 @@ static void ftrace_init_module(struct module *mod,
3996 ftrace_process_locs(mod, start, end); 3996 ftrace_process_locs(mod, start, end);
3997} 3997}
3998 3998
3999static int ftrace_module_notify(struct notifier_block *self, 3999static int ftrace_module_notify_enter(struct notifier_block *self,
4000 unsigned long val, void *data) 4000 unsigned long val, void *data)
4001{ 4001{
4002 struct module *mod = data; 4002 struct module *mod = data;
4003 4003
4004 switch (val) { 4004 if (val == MODULE_STATE_COMING)
4005 case MODULE_STATE_COMING:
4006 ftrace_init_module(mod, mod->ftrace_callsites, 4005 ftrace_init_module(mod, mod->ftrace_callsites,
4007 mod->ftrace_callsites + 4006 mod->ftrace_callsites +
4008 mod->num_ftrace_callsites); 4007 mod->num_ftrace_callsites);
4009 break; 4008 return 0;
4010 case MODULE_STATE_GOING: 4009}
4010
4011static int ftrace_module_notify_exit(struct notifier_block *self,
4012 unsigned long val, void *data)
4013{
4014 struct module *mod = data;
4015
4016 if (val == MODULE_STATE_GOING)
4011 ftrace_release_mod(mod); 4017 ftrace_release_mod(mod);
4012 break;
4013 }
4014 4018
4015 return 0; 4019 return 0;
4016} 4020}
4017#else 4021#else
4018static int ftrace_module_notify(struct notifier_block *self, 4022static int ftrace_module_notify_enter(struct notifier_block *self,
4019 unsigned long val, void *data) 4023 unsigned long val, void *data)
4024{
4025 return 0;
4026}
4027static int ftrace_module_notify_exit(struct notifier_block *self,
4028 unsigned long val, void *data)
4020{ 4029{
4021 return 0; 4030 return 0;
4022} 4031}
4023#endif /* CONFIG_MODULES */ 4032#endif /* CONFIG_MODULES */
4024 4033
4025struct notifier_block ftrace_module_nb = { 4034struct notifier_block ftrace_module_enter_nb = {
4026 .notifier_call = ftrace_module_notify, 4035 .notifier_call = ftrace_module_notify_enter,
4027 .priority = INT_MAX, /* Run before anything that can use kprobes */ 4036 .priority = INT_MAX, /* Run before anything that can use kprobes */
4028}; 4037};
4029 4038
4039struct notifier_block ftrace_module_exit_nb = {
4040 .notifier_call = ftrace_module_notify_exit,
4041 .priority = INT_MIN, /* Run after anything that can remove kprobes */
4042};
4043
4030extern unsigned long __start_mcount_loc[]; 4044extern unsigned long __start_mcount_loc[];
4031extern unsigned long __stop_mcount_loc[]; 4045extern unsigned long __stop_mcount_loc[];
4032 4046
@@ -4058,9 +4072,13 @@ void __init ftrace_init(void)
4058 __start_mcount_loc, 4072 __start_mcount_loc,
4059 __stop_mcount_loc); 4073 __stop_mcount_loc);
4060 4074
4061 ret = register_module_notifier(&ftrace_module_nb); 4075 ret = register_module_notifier(&ftrace_module_enter_nb);
4076 if (ret)
4077 pr_warning("Failed to register trace ftrace module enter notifier\n");
4078
4079 ret = register_module_notifier(&ftrace_module_exit_nb);
4062 if (ret) 4080 if (ret)
4063 pr_warning("Failed to register trace ftrace module notifier\n"); 4081 pr_warning("Failed to register trace ftrace module exit notifier\n");
4064 4082
4065 set_ftrace_early_filters(); 4083 set_ftrace_early_filters();
4066 4084