Commit 48538861 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-v4.11-rc5-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull ftrace fix from Steven Rostedt:
 "While rewriting the function probe code, I stumbled over a long
  standing bug. This bug has been there sinc function tracing was added
  way back when. But my new development depends on this bug being fixed,
  and it should be fixed regardless as it causes ftrace to disable
  itself when triggered, and a reboot is required to enable it again.

  The bug is that the function probe does not disable itself properly if
  there's another probe of its type still enabled. For example:

     # cd /sys/kernel/debug/tracing
     # echo schedule:traceoff > set_ftrace_filter
     # echo do_IRQ:traceoff > set_ftrace_filter
     # echo \!do_IRQ:traceoff > /debug/tracing/set_ftrace_filter
     # echo do_IRQ:traceoff > set_ftrace_filter

  The above registers two traceoff probes (one for schedule and one for
  do_IRQ, and then removes do_IRQ.

  But since there still exists one for schedule, it is not done
  properly. When adding do_IRQ back, the breakage in the accounting is
  noticed by the ftrace self tests, and it causes a warning and disables
  ftrace"

* tag 'trace-v4.11-rc5-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  ftrace: Fix removing of second function probe
parents d5ff0814 82cc4fc2
...@@ -3755,23 +3755,24 @@ static void __enable_ftrace_function_probe(struct ftrace_ops_hash *old_hash) ...@@ -3755,23 +3755,24 @@ static void __enable_ftrace_function_probe(struct ftrace_ops_hash *old_hash)
ftrace_probe_registered = 1; ftrace_probe_registered = 1;
} }
static void __disable_ftrace_function_probe(void) static bool __disable_ftrace_function_probe(void)
{ {
int i; int i;
if (!ftrace_probe_registered) if (!ftrace_probe_registered)
return; return false;
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
struct hlist_head *hhd = &ftrace_func_hash[i]; struct hlist_head *hhd = &ftrace_func_hash[i];
if (hhd->first) if (hhd->first)
return; return false;
} }
/* no more funcs left */ /* no more funcs left */
ftrace_shutdown(&trace_probe_ops, 0); ftrace_shutdown(&trace_probe_ops, 0);
ftrace_probe_registered = 0; ftrace_probe_registered = 0;
return true;
} }
...@@ -3901,6 +3902,7 @@ static void ...@@ -3901,6 +3902,7 @@ static void
__unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
void *data, int flags) void *data, int flags)
{ {
struct ftrace_ops_hash old_hash_ops;
struct ftrace_func_entry *rec_entry; struct ftrace_func_entry *rec_entry;
struct ftrace_func_probe *entry; struct ftrace_func_probe *entry;
struct ftrace_func_probe *p; struct ftrace_func_probe *p;
...@@ -3912,6 +3914,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, ...@@ -3912,6 +3914,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
struct hlist_node *tmp; struct hlist_node *tmp;
char str[KSYM_SYMBOL_LEN]; char str[KSYM_SYMBOL_LEN];
int i, ret; int i, ret;
bool disabled;
if (glob && (strcmp(glob, "*") == 0 || !strlen(glob))) if (glob && (strcmp(glob, "*") == 0 || !strlen(glob)))
func_g.search = NULL; func_g.search = NULL;
...@@ -3930,6 +3933,10 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, ...@@ -3930,6 +3933,10 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
mutex_lock(&trace_probe_ops.func_hash->regex_lock); mutex_lock(&trace_probe_ops.func_hash->regex_lock);
old_hash_ops.filter_hash = old_hash;
/* Probes only have filters */
old_hash_ops.notrace_hash = NULL;
hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash); hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash);
if (!hash) if (!hash)
/* Hmm, should report this somehow */ /* Hmm, should report this somehow */
...@@ -3967,12 +3974,17 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, ...@@ -3967,12 +3974,17 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
} }
} }
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
__disable_ftrace_function_probe(); disabled = __disable_ftrace_function_probe();
/* /*
* Remove after the disable is called. Otherwise, if the last * Remove after the disable is called. Otherwise, if the last
* probe is removed, a null hash means *all enabled*. * probe is removed, a null hash means *all enabled*.
*/ */
ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash);
/* still need to update the function call sites */
if (ftrace_enabled && !disabled)
ftrace_run_modify_code(&trace_probe_ops, FTRACE_UPDATE_CALLS,
&old_hash_ops);
synchronize_sched(); synchronize_sched();
if (!ret) if (!ret)
free_ftrace_hash_rcu(old_hash); free_ftrace_hash_rcu(old_hash);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment