diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-07-29 13:50:33 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-07-31 13:12:48 -0400 |
commit | 2816c551c796ec14620325b2c9ed75b9979d3125 (patch) | |
tree | c4bc262514de5ef265001411bcdf7ad65916eae9 /kernel | |
parent | 776164c1faac4966ab14418bb0922e1820da1d19 (diff) |
tracing: trace_remove_event_call() should fail if call/file is in use
Change trace_remove_event_call(call) to return the error if this
call is active. This is what the callers assume but can't verify
outside of the tracing locks. Both trace_kprobe.c/trace_uprobe.c
need the additional changes, unregister_trace_probe() should abort
if trace_remove_event_call() fails.
The caller is going to free this call/file so we must ensure that
nobody can use them after trace_remove_event_call() succeeds.
debugfs should be fine after the previous changes and event_remove()
does TRACE_REG_UNREGISTER, but still there are 2 reasons why we need
the additional checks:
- There could be a perf_event(s) attached to this tp_event, so the
patch checks ->perf_refcount.
- TRACE_REG_UNREGISTER can be suppressed by FTRACE_EVENT_FL_SOFT_MODE,
so we simply check FTRACE_EVENT_FL_ENABLED protected by event_mutex.
Link: http://lkml.kernel.org/r/20130729175033.GB26284@redhat.com
Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/trace/trace_events.c | 35 |
1 files changed, 33 insertions, 2 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index a67c913e2f9f..ec04836273c0 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -1713,16 +1713,47 @@ static void __trace_remove_event_call(struct ftrace_event_call *call) | |||
1713 | destroy_preds(call); | 1713 | destroy_preds(call); |
1714 | } | 1714 | } |
1715 | 1715 | ||
1716 | static int probe_remove_event_call(struct ftrace_event_call *call) | ||
1717 | { | ||
1718 | struct trace_array *tr; | ||
1719 | struct ftrace_event_file *file; | ||
1720 | |||
1721 | #ifdef CONFIG_PERF_EVENTS | ||
1722 | if (call->perf_refcount) | ||
1723 | return -EBUSY; | ||
1724 | #endif | ||
1725 | do_for_each_event_file(tr, file) { | ||
1726 | if (file->event_call != call) | ||
1727 | continue; | ||
1728 | /* | ||
1729 | * We can't rely on ftrace_event_enable_disable(enable => 0) | ||
1730 | * we are going to do, FTRACE_EVENT_FL_SOFT_MODE can suppress | ||
1731 | * TRACE_REG_UNREGISTER. | ||
1732 | */ | ||
1733 | if (file->flags & FTRACE_EVENT_FL_ENABLED) | ||
1734 | return -EBUSY; | ||
1735 | break; | ||
1736 | } while_for_each_event_file(); | ||
1737 | |||
1738 | __trace_remove_event_call(call); | ||
1739 | |||
1740 | return 0; | ||
1741 | } | ||
1742 | |||
1716 | /* Remove an event_call */ | 1743 | /* Remove an event_call */ |
1717 | void trace_remove_event_call(struct ftrace_event_call *call) | 1744 | int trace_remove_event_call(struct ftrace_event_call *call) |
1718 | { | 1745 | { |
1746 | int ret; | ||
1747 | |||
1719 | mutex_lock(&trace_types_lock); | 1748 | mutex_lock(&trace_types_lock); |
1720 | mutex_lock(&event_mutex); | 1749 | mutex_lock(&event_mutex); |
1721 | down_write(&trace_event_sem); | 1750 | down_write(&trace_event_sem); |
1722 | __trace_remove_event_call(call); | 1751 | ret = probe_remove_event_call(call); |
1723 | up_write(&trace_event_sem); | 1752 | up_write(&trace_event_sem); |
1724 | mutex_unlock(&event_mutex); | 1753 | mutex_unlock(&event_mutex); |
1725 | mutex_unlock(&trace_types_lock); | 1754 | mutex_unlock(&trace_types_lock); |
1755 | |||
1756 | return ret; | ||
1726 | } | 1757 | } |
1727 | 1758 | ||
1728 | #define for_each_event(event, start, end) \ | 1759 | #define for_each_event(event, start, end) \ |