aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2014-04-24 10:40:12 -0400
committerSteven Rostedt <rostedt@goodmis.org>2014-04-28 10:37:21 -0400
commita949ae560a511fe4e3adf48fa44fefded93e5c2b (patch)
treec47563e7c5696f264050aa5e049ad6f69787f536 /kernel
parenta798c10faf62a505d24e5f6213fbaf904a39623f (diff)
ftrace/module: Hardcode ftrace_module_init() call into load_module()
A race exists between module loading and enabling of function tracer. CPU 1 CPU 2 ----- ----- load_module() module->state = MODULE_STATE_COMING register_ftrace_function() mutex_lock(&ftrace_lock); ftrace_startup() update_ftrace_function(); ftrace_arch_code_modify_prepare() set_all_module_text_rw(); <enables-ftrace> ftrace_arch_code_modify_post_process() set_all_module_text_ro(); [ here all module text is set to RO, including the module that is loading!! ] blocking_notifier_call_chain(MODULE_STATE_COMING); ftrace_init_module() [ tries to modify code, but it's RO, and fails! ftrace_bug() is called] When this race happens, ftrace_bug() will produces a nasty warning and all of the function tracing features will be disabled until reboot. The simple solution is to treate module load the same way the core kernel is treated at boot. To hardcode the ftrace function modification of converting calls to mcount into nops. This is done in init/main.c there's no reason it could not be done in load_module(). This gives a better control of the changes and doesn't tie the state of the module to its notifiers as much. Ftrace is special, it needs to be treated as such. The reason this would work, is that the ftrace_module_init() would be called while the module is in MODULE_STATE_UNFORMED, which is ignored by the set_all_module_text_ro() call. Link: http://lkml.kernel.org/r/1395637826-3312-1-git-send-email-indou.takao@jp.fujitsu.com Reported-by: Takao Indoh <indou.takao@jp.fujitsu.com> Acked-by: Rusty Russell <rusty@rustcorp.com.au> Cc: stable@vger.kernel.org # 2.6.38+ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/module.c3
-rw-r--r--kernel/trace/ftrace.c27
2 files changed, 7 insertions, 23 deletions
diff --git a/kernel/module.c b/kernel/module.c
index 11869408f79b..5f14fec9f825 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3271,6 +3271,9 @@ static int load_module(struct load_info *info, const char __user *uargs,
3271 3271
3272 dynamic_debug_setup(info->debug, info->num_debug); 3272 dynamic_debug_setup(info->debug, info->num_debug);
3273 3273
3274 /* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
3275 ftrace_module_init(mod);
3276
3274 /* Finally it's fully formed, ready to start executing. */ 3277 /* Finally it's fully formed, ready to start executing. */
3275 err = complete_formation(mod, info); 3278 err = complete_formation(mod, info);
3276 if (err) 3279 if (err)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 1fd4b9479210..4a54a25afa2f 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -4330,16 +4330,11 @@ static void ftrace_init_module(struct module *mod,
4330 ftrace_process_locs(mod, start, end); 4330 ftrace_process_locs(mod, start, end);
4331} 4331}
4332 4332
4333static int ftrace_module_notify_enter(struct notifier_block *self, 4333void ftrace_module_init(struct module *mod)
4334 unsigned long val, void *data)
4335{ 4334{
4336 struct module *mod = data; 4335 ftrace_init_module(mod, mod->ftrace_callsites,
4337 4336 mod->ftrace_callsites +
4338 if (val == MODULE_STATE_COMING) 4337 mod->num_ftrace_callsites);
4339 ftrace_init_module(mod, mod->ftrace_callsites,
4340 mod->ftrace_callsites +
4341 mod->num_ftrace_callsites);
4342 return 0;
4343} 4338}
4344 4339
4345static int ftrace_module_notify_exit(struct notifier_block *self, 4340static int ftrace_module_notify_exit(struct notifier_block *self,
@@ -4353,11 +4348,6 @@ static int ftrace_module_notify_exit(struct notifier_block *self,
4353 return 0; 4348 return 0;
4354} 4349}
4355#else 4350#else
4356static int ftrace_module_notify_enter(struct notifier_block *self,
4357 unsigned long val, void *data)
4358{
4359 return 0;
4360}
4361static int ftrace_module_notify_exit(struct notifier_block *self, 4351static int ftrace_module_notify_exit(struct notifier_block *self,
4362 unsigned long val, void *data) 4352 unsigned long val, void *data)
4363{ 4353{
@@ -4365,11 +4355,6 @@ static int ftrace_module_notify_exit(struct notifier_block *self,
4365} 4355}
4366#endif /* CONFIG_MODULES */ 4356#endif /* CONFIG_MODULES */
4367 4357
4368struct notifier_block ftrace_module_enter_nb = {
4369 .notifier_call = ftrace_module_notify_enter,
4370 .priority = INT_MAX, /* Run before anything that can use kprobes */
4371};
4372
4373struct notifier_block ftrace_module_exit_nb = { 4358struct notifier_block ftrace_module_exit_nb = {
4374 .notifier_call = ftrace_module_notify_exit, 4359 .notifier_call = ftrace_module_notify_exit,
4375 .priority = INT_MIN, /* Run after anything that can remove kprobes */ 4360 .priority = INT_MIN, /* Run after anything that can remove kprobes */
@@ -4403,10 +4388,6 @@ void __init ftrace_init(void)
4403 __start_mcount_loc, 4388 __start_mcount_loc,
4404 __stop_mcount_loc); 4389 __stop_mcount_loc);
4405 4390
4406 ret = register_module_notifier(&ftrace_module_enter_nb);
4407 if (ret)
4408 pr_warning("Failed to register trace ftrace module enter notifier\n");
4409
4410 ret = register_module_notifier(&ftrace_module_exit_nb); 4391 ret = register_module_notifier(&ftrace_module_exit_nb);
4411 if (ret) 4392 if (ret)
4412 pr_warning("Failed to register trace ftrace module exit notifier\n"); 4393 pr_warning("Failed to register trace ftrace module exit notifier\n");