diff options
author | Oleg Nesterov <oleg@redhat.com> | 2013-07-29 13:50:33 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-29 12:47:34 -0400 |
commit | 8169887b694aa3d9632fdd0308aaa1db7c422e75 (patch) | |
tree | 521d15d1f21cdcbbf25d490a3a0f850e31635fbe /kernel | |
parent | 012dc156d6af49e02a30fcba7d688e251608d97c (diff) |
tracing: trace_remove_event_call() should fail if call/file is in use
commit 2816c551c796ec14620325b2c9ed75b9979d3125 upstream.
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.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 0bff8aaf581b..3d18aadef493 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -1735,16 +1735,47 @@ static void __trace_remove_event_call(struct ftrace_event_call *call) | |||
1735 | destroy_preds(call); | 1735 | destroy_preds(call); |
1736 | } | 1736 | } |
1737 | 1737 | ||
1738 | static int probe_remove_event_call(struct ftrace_event_call *call) | ||
1739 | { | ||
1740 | struct trace_array *tr; | ||
1741 | struct ftrace_event_file *file; | ||
1742 | |||
1743 | #ifdef CONFIG_PERF_EVENTS | ||
1744 | if (call->perf_refcount) | ||
1745 | return -EBUSY; | ||
1746 | #endif | ||
1747 | do_for_each_event_file(tr, file) { | ||
1748 | if (file->event_call != call) | ||
1749 | continue; | ||
1750 | /* | ||
1751 | * We can't rely on ftrace_event_enable_disable(enable => 0) | ||
1752 | * we are going to do, FTRACE_EVENT_FL_SOFT_MODE can suppress | ||
1753 | * TRACE_REG_UNREGISTER. | ||
1754 | */ | ||
1755 | if (file->flags & FTRACE_EVENT_FL_ENABLED) | ||
1756 | return -EBUSY; | ||
1757 | break; | ||
1758 | } while_for_each_event_file(); | ||
1759 | |||
1760 | __trace_remove_event_call(call); | ||
1761 | |||
1762 | return 0; | ||
1763 | } | ||
1764 | |||
1738 | /* Remove an event_call */ | 1765 | /* Remove an event_call */ |
1739 | void trace_remove_event_call(struct ftrace_event_call *call) | 1766 | int trace_remove_event_call(struct ftrace_event_call *call) |
1740 | { | 1767 | { |
1768 | int ret; | ||
1769 | |||
1741 | mutex_lock(&trace_types_lock); | 1770 | mutex_lock(&trace_types_lock); |
1742 | mutex_lock(&event_mutex); | 1771 | mutex_lock(&event_mutex); |
1743 | down_write(&trace_event_sem); | 1772 | down_write(&trace_event_sem); |
1744 | __trace_remove_event_call(call); | 1773 | ret = probe_remove_event_call(call); |
1745 | up_write(&trace_event_sem); | 1774 | up_write(&trace_event_sem); |
1746 | mutex_unlock(&event_mutex); | 1775 | mutex_unlock(&event_mutex); |
1747 | mutex_unlock(&trace_types_lock); | 1776 | mutex_unlock(&trace_types_lock); |
1777 | |||
1778 | return ret; | ||
1748 | } | 1779 | } |
1749 | 1780 | ||
1750 | #define for_each_event(event, start, end) \ | 1781 | #define for_each_event(event, start, end) \ |