Commit 02ca1521 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Steven Rostedt

ftrace/kprobes: Fix not to delete probes if in use

Fix kprobe-tracer not to delete a probe if the probe is in use.
In that case, delete operation will return -EBUSY.

This bug can cause a kernel panic if enabled probes are deleted
during perf record.

(Add some probes on functions)
sh-4.2# perf probe --del probe:\*
sh-4.2# exit
(kernel panic)

This is originally reported on the fedora bugzilla:

 https://bugzilla.redhat.com/show_bug.cgi?id=742383

I've also checked that this problem doesn't happen on
tracepoints when module removing because perf event
locks target module.

$ sudo ./perf record -e xfs:\* -aR sh
sh-4.2# rmmod xfs
ERROR: Module xfs is in use
sh-4.2# exit
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.203 MB perf.data (~8862 samples) ]
Signed-off-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: stable@kernel.org
Link: http://lkml.kernel.org/r/20111004104438.14591.6553.stgit@fedora15Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent 9d3ec7a0
...@@ -836,11 +836,17 @@ static void __unregister_trace_probe(struct trace_probe *tp) ...@@ -836,11 +836,17 @@ static void __unregister_trace_probe(struct trace_probe *tp)
} }
/* Unregister a trace_probe and probe_event: call with locking probe_lock */ /* Unregister a trace_probe and probe_event: call with locking probe_lock */
static void unregister_trace_probe(struct trace_probe *tp) static int unregister_trace_probe(struct trace_probe *tp)
{ {
/* Enabled event can not be unregistered */
if (trace_probe_is_enabled(tp))
return -EBUSY;
__unregister_trace_probe(tp); __unregister_trace_probe(tp);
list_del(&tp->list); list_del(&tp->list);
unregister_probe_event(tp); unregister_probe_event(tp);
return 0;
} }
/* Register a trace_probe and probe_event */ /* Register a trace_probe and probe_event */
...@@ -854,7 +860,9 @@ static int register_trace_probe(struct trace_probe *tp) ...@@ -854,7 +860,9 @@ static int register_trace_probe(struct trace_probe *tp)
/* Delete old (same name) event if exist */ /* Delete old (same name) event if exist */
old_tp = find_trace_probe(tp->call.name, tp->call.class->system); old_tp = find_trace_probe(tp->call.name, tp->call.class->system);
if (old_tp) { if (old_tp) {
unregister_trace_probe(old_tp); ret = unregister_trace_probe(old_tp);
if (ret < 0)
goto end;
free_trace_probe(old_tp); free_trace_probe(old_tp);
} }
...@@ -892,6 +900,7 @@ static int trace_probe_module_callback(struct notifier_block *nb, ...@@ -892,6 +900,7 @@ static int trace_probe_module_callback(struct notifier_block *nb,
mutex_lock(&probe_lock); mutex_lock(&probe_lock);
list_for_each_entry(tp, &probe_list, list) { list_for_each_entry(tp, &probe_list, list) {
if (trace_probe_within_module(tp, mod)) { if (trace_probe_within_module(tp, mod)) {
/* Don't need to check busy - this should have gone. */
__unregister_trace_probe(tp); __unregister_trace_probe(tp);
ret = __register_trace_probe(tp); ret = __register_trace_probe(tp);
if (ret) if (ret)
...@@ -1205,10 +1214,11 @@ static int create_trace_probe(int argc, char **argv) ...@@ -1205,10 +1214,11 @@ static int create_trace_probe(int argc, char **argv)
return -ENOENT; return -ENOENT;
} }
/* delete an event */ /* delete an event */
unregister_trace_probe(tp); ret = unregister_trace_probe(tp);
free_trace_probe(tp); if (ret == 0)
free_trace_probe(tp);
mutex_unlock(&probe_lock); mutex_unlock(&probe_lock);
return 0; return ret;
} }
if (argc < 2) { if (argc < 2) {
...@@ -1317,18 +1327,29 @@ static int create_trace_probe(int argc, char **argv) ...@@ -1317,18 +1327,29 @@ static int create_trace_probe(int argc, char **argv)
return ret; return ret;
} }
static void release_all_trace_probes(void) static int release_all_trace_probes(void)
{ {
struct trace_probe *tp; struct trace_probe *tp;
int ret = 0;
mutex_lock(&probe_lock); mutex_lock(&probe_lock);
/* Ensure no probe is in use. */
list_for_each_entry(tp, &probe_list, list)
if (trace_probe_is_enabled(tp)) {
ret = -EBUSY;
goto end;
}
/* TODO: Use batch unregistration */ /* TODO: Use batch unregistration */
while (!list_empty(&probe_list)) { while (!list_empty(&probe_list)) {
tp = list_entry(probe_list.next, struct trace_probe, list); tp = list_entry(probe_list.next, struct trace_probe, list);
unregister_trace_probe(tp); unregister_trace_probe(tp);
free_trace_probe(tp); free_trace_probe(tp);
} }
end:
mutex_unlock(&probe_lock); mutex_unlock(&probe_lock);
return ret;
} }
/* Probes listing interfaces */ /* Probes listing interfaces */
...@@ -1380,9 +1401,13 @@ static const struct seq_operations probes_seq_op = { ...@@ -1380,9 +1401,13 @@ static const struct seq_operations probes_seq_op = {
static int probes_open(struct inode *inode, struct file *file) static int probes_open(struct inode *inode, struct file *file)
{ {
if ((file->f_mode & FMODE_WRITE) && int ret;
(file->f_flags & O_TRUNC))
release_all_trace_probes(); if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
ret = release_all_trace_probes();
if (ret < 0)
return ret;
}
return seq_open(file, &probes_seq_op); return seq_open(file, &probes_seq_op);
} }
...@@ -2055,6 +2080,21 @@ static __init int kprobe_trace_self_tests_init(void) ...@@ -2055,6 +2080,21 @@ static __init int kprobe_trace_self_tests_init(void)
ret = target(1, 2, 3, 4, 5, 6); ret = target(1, 2, 3, 4, 5, 6);
/* Disable trace points before removing it */
tp = find_trace_probe("testprobe", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tp == NULL)) {
pr_warning("error on getting test probe.\n");
warn++;
} else
disable_trace_probe(tp, TP_FLAG_TRACE);
tp = find_trace_probe("testprobe2", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tp == NULL)) {
pr_warning("error on getting 2nd test probe.\n");
warn++;
} else
disable_trace_probe(tp, TP_FLAG_TRACE);
ret = command_trace_probe("-:testprobe"); ret = command_trace_probe("-:testprobe");
if (WARN_ON_ONCE(ret)) { if (WARN_ON_ONCE(ret)) {
pr_warning("error on deleting a probe.\n"); pr_warning("error on deleting a probe.\n");
......
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