aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2009-04-15 13:24:06 -0400
committerIngo Molnar <mingo@elte.hu>2009-04-17 10:59:15 -0400
commit93eb677d74a4f7d3edfb678c94f6c0544d9fbad2 (patch)
tree8bbc46895be623a78316230362e94969dbb02135 /kernel
parentf3948f8857ef5de239f28a61dddb1554a0ae4c2c (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.c19
-rw-r--r--kernel/trace/ftrace.c90
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
919void 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
943static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) 919static 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
2755void ftrace_init_module(struct module *mod, 2731#ifdef CONFIG_MODULES
2756 unsigned long *start, unsigned long *end) 2732void 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
2756static 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
2764static 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
2785static int ftrace_module_notify(struct notifier_block *self,
2786 unsigned long val, void *data)
2787{
2788 return 0;
2789}
2790#endif /* CONFIG_MODULES */
2791
2792struct notifier_block ftrace_module_nb = {
2793 .notifier_call = ftrace_module_notify,
2794 .priority = 0,
2795};
2796
2763extern unsigned long __start_mcount_loc[]; 2797extern unsigned long __start_mcount_loc[];
2764extern unsigned long __stop_mcount_loc[]; 2798extern 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;