| From f7bc8b61f65726ff98f52e286b28e294499d7a08 Mon Sep 17 00:00:00 2001 |
| From: Steven Rostedt <srostedt@redhat.com> |
| Date: Thu, 14 Jul 2011 23:02:27 -0400 |
| Subject: ftrace: Fix regression where ftrace breaks when modules are loaded |
| |
| From: Steven Rostedt <srostedt@redhat.com> |
| |
| commit f7bc8b61f65726ff98f52e286b28e294499d7a08 upstream. |
| |
| Enabling function tracer to trace all functions, then load a module and |
| then disable function tracing will cause ftrace to fail. |
| |
| This can also happen by enabling function tracing on the command line: |
| |
| ftrace=function |
| |
| and during boot up, modules are loaded, then you disable function tracing |
| with 'echo nop > current_tracer' you will trigger a bug in ftrace that |
| will shut itself down. |
| |
| The reason is, the new ftrace code keeps ref counts of all ftrace_ops that |
| are registered for tracing. When one or more ftrace_ops are registered, |
| all the records that represent the functions that the ftrace_ops will |
| trace have a ref count incremented. If this ref count is not zero, |
| when the code modification runs, that function will be enabled for tracing. |
| If the ref count is zero, that function will be disabled from tracing. |
| |
| To make sure the accounting was working, FTRACE_WARN_ON()s were added |
| to updating of the ref counts. |
| |
| If the ref count hits its max (> 2^30 ftrace_ops added), or if |
| the ref count goes below zero, a FTRACE_WARN_ON() is triggered which |
| disables all modification of code. |
| |
| Since it is common for ftrace_ops to trace all functions in the kernel, |
| instead of creating > 20,000 hash items for the ftrace_ops, the hash |
| count is just set to zero, and it represents that the ftrace_ops is |
| to trace all functions. This is where the issues arrise. |
| |
| If you enable function tracing to trace all functions, and then add |
| a module, the modules function records do not get the ref count updated. |
| When the function tracer is disabled, all function records ref counts |
| are subtracted. Since the modules never had their ref counts incremented, |
| they go below zero and the FTRACE_WARN_ON() is triggered. |
| |
| The solution to this is rather simple. When modules are loaded, and |
| their functions are added to the the ftrace pool, look to see if any |
| ftrace_ops are registered that trace all functions. And for those, |
| update the ref count for the module function records. |
| |
| Reported-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Steven Rostedt <rostedt@goodmis.org> |
| |
| --- |
| kernel/trace/ftrace.c | 30 ++++++++++++++++++++++++++++-- |
| 1 file changed, 28 insertions(+), 2 deletions(-) |
| |
| --- a/kernel/trace/ftrace.c |
| +++ b/kernel/trace/ftrace.c |
| @@ -1744,10 +1744,36 @@ static cycle_t ftrace_update_time; |
| static unsigned long ftrace_update_cnt; |
| unsigned long ftrace_update_tot_cnt; |
| |
| +static int ops_traces_mod(struct ftrace_ops *ops) |
| +{ |
| + struct ftrace_hash *hash; |
| + |
| + hash = ops->filter_hash; |
| + return !!(!hash || !hash->count); |
| +} |
| + |
| static int ftrace_update_code(struct module *mod) |
| { |
| struct dyn_ftrace *p; |
| cycle_t start, stop; |
| + unsigned long ref = 0; |
| + |
| + /* |
| + * When adding a module, we need to check if tracers are |
| + * currently enabled and if they are set to trace all functions. |
| + * If they are, we need to enable the module functions as well |
| + * as update the reference counts for those function records. |
| + */ |
| + if (mod) { |
| + struct ftrace_ops *ops; |
| + |
| + for (ops = ftrace_ops_list; |
| + ops != &ftrace_list_end; ops = ops->next) { |
| + if (ops->flags & FTRACE_OPS_FL_ENABLED && |
| + ops_traces_mod(ops)) |
| + ref++; |
| + } |
| + } |
| |
| start = ftrace_now(raw_smp_processor_id()); |
| ftrace_update_cnt = 0; |
| @@ -1760,7 +1786,7 @@ static int ftrace_update_code(struct mod |
| |
| p = ftrace_new_addrs; |
| ftrace_new_addrs = p->newlist; |
| - p->flags = 0L; |
| + p->flags = ref; |
| |
| /* |
| * Do the initial record conversion from mcount jump |
| @@ -1783,7 +1809,7 @@ static int ftrace_update_code(struct mod |
| * conversion puts the module to the correct state, thus |
| * passing the ftrace_make_call check. |
| */ |
| - if (ftrace_start_up) { |
| + if (ftrace_start_up && ref) { |
| int failed = __ftrace_replace_code(p, 1); |
| if (failed) { |
| ftrace_bug(failed, p->ip); |