diff options
author | Steven Rostedt <srostedt@redhat.com> | 2009-04-15 13:24:06 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-04-17 10:59:15 -0400 |
commit | 93eb677d74a4f7d3edfb678c94f6c0544d9fbad2 (patch) | |
tree | 8bbc46895be623a78316230362e94969dbb02135 /kernel | |
parent | f3948f8857ef5de239f28a61dddb1554a0ae4c2c (diff) |
ftrace: use module notifier for function tracer
The hooks in the module code for the function tracer must be called
before any of that module code runs. The function tracer hooks
modify the module (replacing calls to mcount to nops). If the code
is executed while the change occurs, then the CPU can take a GPF.
To handle the above with a bit of paranoia, I originally implemented
the hooks as calls directly from the module code.
After examining the notifier calls, it looks as though the start up
notify is called before any of the module's code is executed. This makes
the use of the notify safe with ftrace.
Only the startup notify is required to be "safe". The shutdown simply
removes the entries from the ftrace function list, and does not modify
any code.
This change has another benefit. It removes a issue with a reverse dependency
in the mutexes of ftrace_lock and module_mutex.
[ Impact: fix lock dependency bug, cleanup ]
Cc: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/module.c | 19 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 90 |
2 files changed, 71 insertions, 38 deletions
diff --git a/kernel/module.c b/kernel/module.c index a0394706f10c..2383e60fcf3f 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -1490,9 +1490,6 @@ static void free_module(struct module *mod) | |||
1490 | /* Free any allocated parameters. */ | 1490 | /* Free any allocated parameters. */ |
1491 | destroy_params(mod->kp, mod->num_kp); | 1491 | destroy_params(mod->kp, mod->num_kp); |
1492 | 1492 | ||
1493 | /* release any pointers to mcount in this module */ | ||
1494 | ftrace_release(mod->module_core, mod->core_size); | ||
1495 | |||
1496 | /* This may be NULL, but that's OK */ | 1493 | /* This may be NULL, but that's OK */ |
1497 | module_free(mod, mod->module_init); | 1494 | module_free(mod, mod->module_init); |
1498 | kfree(mod->args); | 1495 | kfree(mod->args); |
@@ -1893,11 +1890,9 @@ static noinline struct module *load_module(void __user *umod, | |||
1893 | unsigned int symindex = 0; | 1890 | unsigned int symindex = 0; |
1894 | unsigned int strindex = 0; | 1891 | unsigned int strindex = 0; |
1895 | unsigned int modindex, versindex, infoindex, pcpuindex; | 1892 | unsigned int modindex, versindex, infoindex, pcpuindex; |
1896 | unsigned int num_mcount; | ||
1897 | struct module *mod; | 1893 | struct module *mod; |
1898 | long err = 0; | 1894 | long err = 0; |
1899 | void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ | 1895 | void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ |
1900 | unsigned long *mseg; | ||
1901 | mm_segment_t old_fs; | 1896 | mm_segment_t old_fs; |
1902 | 1897 | ||
1903 | DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n", | 1898 | DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n", |
@@ -2179,7 +2174,13 @@ static noinline struct module *load_module(void __user *umod, | |||
2179 | sizeof(*mod->trace_events), | 2174 | sizeof(*mod->trace_events), |
2180 | &mod->num_trace_events); | 2175 | &mod->num_trace_events); |
2181 | #endif | 2176 | #endif |
2182 | 2177 | #ifdef CONFIG_FTRACE_MCOUNT_RECORD | |
2178 | /* sechdrs[0].sh_size is always zero */ | ||
2179 | mod->ftrace_callsites = section_objs(hdr, sechdrs, secstrings, | ||
2180 | "__mcount_loc", | ||
2181 | sizeof(*mod->ftrace_callsites), | ||
2182 | &mod->num_ftrace_callsites); | ||
2183 | #endif | ||
2183 | #ifdef CONFIG_MODVERSIONS | 2184 | #ifdef CONFIG_MODVERSIONS |
2184 | if ((mod->num_syms && !mod->crcs) | 2185 | if ((mod->num_syms && !mod->crcs) |
2185 | || (mod->num_gpl_syms && !mod->gpl_crcs) | 2186 | || (mod->num_gpl_syms && !mod->gpl_crcs) |
@@ -2244,11 +2245,6 @@ static noinline struct module *load_module(void __user *umod, | |||
2244 | dynamic_debug_setup(debug, num_debug); | 2245 | dynamic_debug_setup(debug, num_debug); |
2245 | } | 2246 | } |
2246 | 2247 | ||
2247 | /* sechdrs[0].sh_size is always zero */ | ||
2248 | mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc", | ||
2249 | sizeof(*mseg), &num_mcount); | ||
2250 | ftrace_init_module(mod, mseg, mseg + num_mcount); | ||
2251 | |||
2252 | err = module_finalize(hdr, sechdrs, mod); | 2248 | err = module_finalize(hdr, sechdrs, mod); |
2253 | if (err < 0) | 2249 | if (err < 0) |
2254 | goto cleanup; | 2250 | goto cleanup; |
@@ -2309,7 +2305,6 @@ static noinline struct module *load_module(void __user *umod, | |||
2309 | cleanup: | 2305 | cleanup: |
2310 | kobject_del(&mod->mkobj.kobj); | 2306 | kobject_del(&mod->mkobj.kobj); |
2311 | kobject_put(&mod->mkobj.kobj); | 2307 | kobject_put(&mod->mkobj.kobj); |
2312 | ftrace_release(mod->module_core, mod->core_size); | ||
2313 | free_unload: | 2308 | free_unload: |
2314 | module_unload_free(mod); | 2309 | module_unload_free(mod); |
2315 | #if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP) | 2310 | #if defined(CONFIG_MODULE_UNLOAD) && defined(CONFIG_SMP) |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index a23488988581..5b606f45b6c4 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -916,30 +916,6 @@ static void ftrace_free_rec(struct dyn_ftrace *rec) | |||
916 | rec->flags |= FTRACE_FL_FREE; | 916 | rec->flags |= FTRACE_FL_FREE; |
917 | } | 917 | } |
918 | 918 | ||
919 | void ftrace_release(void *start, unsigned long size) | ||
920 | { | ||
921 | struct dyn_ftrace *rec; | ||
922 | struct ftrace_page *pg; | ||
923 | unsigned long s = (unsigned long)start; | ||
924 | unsigned long e = s + size; | ||
925 | |||
926 | if (ftrace_disabled || !start) | ||
927 | return; | ||
928 | |||
929 | mutex_lock(&ftrace_lock); | ||
930 | do_for_each_ftrace_rec(pg, rec) { | ||
931 | if ((rec->ip >= s) && (rec->ip < e)) { | ||
932 | /* | ||
933 | * rec->ip is changed in ftrace_free_rec() | ||
934 | * It should not between s and e if record was freed. | ||
935 | */ | ||
936 | FTRACE_WARN_ON(rec->flags & FTRACE_FL_FREE); | ||
937 | ftrace_free_rec(rec); | ||
938 | } | ||
939 | } while_for_each_ftrace_rec(); | ||
940 | mutex_unlock(&ftrace_lock); | ||
941 | } | ||
942 | |||
943 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | 919 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) |
944 | { | 920 | { |
945 | struct dyn_ftrace *rec; | 921 | struct dyn_ftrace *rec; |
@@ -2752,14 +2728,72 @@ static int ftrace_convert_nops(struct module *mod, | |||
2752 | return 0; | 2728 | return 0; |
2753 | } | 2729 | } |
2754 | 2730 | ||
2755 | void ftrace_init_module(struct module *mod, | 2731 | #ifdef CONFIG_MODULES |
2756 | unsigned long *start, unsigned long *end) | 2732 | void ftrace_release(void *start, void *end) |
2733 | { | ||
2734 | struct dyn_ftrace *rec; | ||
2735 | struct ftrace_page *pg; | ||
2736 | unsigned long s = (unsigned long)start; | ||
2737 | unsigned long e = (unsigned long)end; | ||
2738 | |||
2739 | if (ftrace_disabled || !start || start == end) | ||
2740 | return; | ||
2741 | |||
2742 | mutex_lock(&ftrace_lock); | ||
2743 | do_for_each_ftrace_rec(pg, rec) { | ||
2744 | if ((rec->ip >= s) && (rec->ip < e)) { | ||
2745 | /* | ||
2746 | * rec->ip is changed in ftrace_free_rec() | ||
2747 | * It should not between s and e if record was freed. | ||
2748 | */ | ||
2749 | FTRACE_WARN_ON(rec->flags & FTRACE_FL_FREE); | ||
2750 | ftrace_free_rec(rec); | ||
2751 | } | ||
2752 | } while_for_each_ftrace_rec(); | ||
2753 | mutex_unlock(&ftrace_lock); | ||
2754 | } | ||
2755 | |||
2756 | static void ftrace_init_module(struct module *mod, | ||
2757 | unsigned long *start, unsigned long *end) | ||
2757 | { | 2758 | { |
2758 | if (ftrace_disabled || start == end) | 2759 | if (ftrace_disabled || start == end) |
2759 | return; | 2760 | return; |
2760 | ftrace_convert_nops(mod, start, end); | 2761 | ftrace_convert_nops(mod, start, end); |
2761 | } | 2762 | } |
2762 | 2763 | ||
2764 | static int ftrace_module_notify(struct notifier_block *self, | ||
2765 | unsigned long val, void *data) | ||
2766 | { | ||
2767 | struct module *mod = data; | ||
2768 | |||
2769 | switch (val) { | ||
2770 | case MODULE_STATE_COMING: | ||
2771 | ftrace_init_module(mod, mod->ftrace_callsites, | ||
2772 | mod->ftrace_callsites + | ||
2773 | mod->num_ftrace_callsites); | ||
2774 | break; | ||
2775 | case MODULE_STATE_GOING: | ||
2776 | ftrace_release(mod->ftrace_callsites, | ||
2777 | mod->ftrace_callsites + | ||
2778 | mod->num_ftrace_callsites); | ||
2779 | break; | ||
2780 | } | ||
2781 | |||
2782 | return 0; | ||
2783 | } | ||
2784 | #else | ||
2785 | static int ftrace_module_notify(struct notifier_block *self, | ||
2786 | unsigned long val, void *data) | ||
2787 | { | ||
2788 | return 0; | ||
2789 | } | ||
2790 | #endif /* CONFIG_MODULES */ | ||
2791 | |||
2792 | struct notifier_block ftrace_module_nb = { | ||
2793 | .notifier_call = ftrace_module_notify, | ||
2794 | .priority = 0, | ||
2795 | }; | ||
2796 | |||
2763 | extern unsigned long __start_mcount_loc[]; | 2797 | extern unsigned long __start_mcount_loc[]; |
2764 | extern unsigned long __stop_mcount_loc[]; | 2798 | extern unsigned long __stop_mcount_loc[]; |
2765 | 2799 | ||
@@ -2791,6 +2825,10 @@ void __init ftrace_init(void) | |||
2791 | __start_mcount_loc, | 2825 | __start_mcount_loc, |
2792 | __stop_mcount_loc); | 2826 | __stop_mcount_loc); |
2793 | 2827 | ||
2828 | ret = register_module_notifier(&ftrace_module_nb); | ||
2829 | if (!ret) | ||
2830 | pr_warning("Failed to register trace ftrace module notifier\n"); | ||
2831 | |||
2794 | return; | 2832 | return; |
2795 | failed: | 2833 | failed: |
2796 | ftrace_disabled = 1; | 2834 | ftrace_disabled = 1; |