diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-07-03 21:37:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-07-03 21:37:25 -0400 |
commit | ef34c6ce49cce8b4d4913fa9166729071f48bab1 (patch) | |
tree | 24dc5a2b05b854dd505d13af80534e0e2f018561 /kernel | |
parent | af6f157a4b4f511d06d564086cb3516bb164b7c6 (diff) | |
parent | 099ed151675cd1d2dbeae1dac697975f6a68716d (diff) |
Merge tag 'trace-fixes-v3.16-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace
Pull tracing fixes from Steven Rostedt:
"Oleg Nesterov found and fixed a bug in the perf/ftrace/uprobes code
where running:
# perf probe -x /lib/libc.so.6 syscall
# echo 1 >> /sys/kernel/debug/tracing/events/probe_libc/enable
# perf record -e probe_libc:syscall whatever
kills the uprobe. Along the way he found some other minor bugs and
clean ups that he fixed up making it a total of 4 patches.
Doing unrelated work, I found that the reading of the ftrace trace
file disables all function tracer callbacks. This was fine when
ftrace was the only user, but now that it's used by perf and kprobes,
this is a bug where reading trace can disable kprobes and perf. A
very unexpected side effect and should be fixed"
* tag 'trace-fixes-v3.16-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
tracing: Remove ftrace_stop/start() from reading the trace file
tracing/uprobes: Fix the usage of uprobe_buffer_enable() in probe_event_enable()
tracing/uprobes: Kill the bogus UPROBE_HANDLER_REMOVE code in uprobe_dispatcher()
uprobes: Change unregister/apply to WARN() if uprobe/consumer is gone
tracing/uprobes: Revert "Support mix of ftrace and perf"
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/events/uprobes.c | 6 | ||||
-rw-r--r-- | kernel/trace/trace.c | 2 | ||||
-rw-r--r-- | kernel/trace/trace_uprobe.c | 46 |
3 files changed, 30 insertions, 24 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index c445e392e93f..6f3254e8c137 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -846,7 +846,7 @@ static void __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *u | |||
846 | { | 846 | { |
847 | int err; | 847 | int err; |
848 | 848 | ||
849 | if (!consumer_del(uprobe, uc)) /* WARN? */ | 849 | if (WARN_ON(!consumer_del(uprobe, uc))) |
850 | return; | 850 | return; |
851 | 851 | ||
852 | err = register_for_each_vma(uprobe, NULL); | 852 | err = register_for_each_vma(uprobe, NULL); |
@@ -927,7 +927,7 @@ int uprobe_apply(struct inode *inode, loff_t offset, | |||
927 | int ret = -ENOENT; | 927 | int ret = -ENOENT; |
928 | 928 | ||
929 | uprobe = find_uprobe(inode, offset); | 929 | uprobe = find_uprobe(inode, offset); |
930 | if (!uprobe) | 930 | if (WARN_ON(!uprobe)) |
931 | return ret; | 931 | return ret; |
932 | 932 | ||
933 | down_write(&uprobe->register_rwsem); | 933 | down_write(&uprobe->register_rwsem); |
@@ -952,7 +952,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume | |||
952 | struct uprobe *uprobe; | 952 | struct uprobe *uprobe; |
953 | 953 | ||
954 | uprobe = find_uprobe(inode, offset); | 954 | uprobe = find_uprobe(inode, offset); |
955 | if (!uprobe) | 955 | if (WARN_ON(!uprobe)) |
956 | return; | 956 | return; |
957 | 957 | ||
958 | down_write(&uprobe->register_rwsem); | 958 | down_write(&uprobe->register_rwsem); |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 384ede311717..f243444a3772 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -1396,7 +1396,6 @@ void tracing_start(void) | |||
1396 | 1396 | ||
1397 | arch_spin_unlock(&global_trace.max_lock); | 1397 | arch_spin_unlock(&global_trace.max_lock); |
1398 | 1398 | ||
1399 | ftrace_start(); | ||
1400 | out: | 1399 | out: |
1401 | raw_spin_unlock_irqrestore(&global_trace.start_lock, flags); | 1400 | raw_spin_unlock_irqrestore(&global_trace.start_lock, flags); |
1402 | } | 1401 | } |
@@ -1443,7 +1442,6 @@ void tracing_stop(void) | |||
1443 | struct ring_buffer *buffer; | 1442 | struct ring_buffer *buffer; |
1444 | unsigned long flags; | 1443 | unsigned long flags; |
1445 | 1444 | ||
1446 | ftrace_stop(); | ||
1447 | raw_spin_lock_irqsave(&global_trace.start_lock, flags); | 1445 | raw_spin_lock_irqsave(&global_trace.start_lock, flags); |
1448 | if (global_trace.stop_count++) | 1446 | if (global_trace.stop_count++) |
1449 | goto out; | 1447 | goto out; |
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 04fdb5de823c..3c9b97e6b1f4 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c | |||
@@ -893,6 +893,9 @@ probe_event_enable(struct trace_uprobe *tu, struct ftrace_event_file *file, | |||
893 | int ret; | 893 | int ret; |
894 | 894 | ||
895 | if (file) { | 895 | if (file) { |
896 | if (tu->tp.flags & TP_FLAG_PROFILE) | ||
897 | return -EINTR; | ||
898 | |||
896 | link = kmalloc(sizeof(*link), GFP_KERNEL); | 899 | link = kmalloc(sizeof(*link), GFP_KERNEL); |
897 | if (!link) | 900 | if (!link) |
898 | return -ENOMEM; | 901 | return -ENOMEM; |
@@ -901,29 +904,40 @@ probe_event_enable(struct trace_uprobe *tu, struct ftrace_event_file *file, | |||
901 | list_add_tail_rcu(&link->list, &tu->tp.files); | 904 | list_add_tail_rcu(&link->list, &tu->tp.files); |
902 | 905 | ||
903 | tu->tp.flags |= TP_FLAG_TRACE; | 906 | tu->tp.flags |= TP_FLAG_TRACE; |
904 | } else | 907 | } else { |
905 | tu->tp.flags |= TP_FLAG_PROFILE; | 908 | if (tu->tp.flags & TP_FLAG_TRACE) |
909 | return -EINTR; | ||
906 | 910 | ||
907 | ret = uprobe_buffer_enable(); | 911 | tu->tp.flags |= TP_FLAG_PROFILE; |
908 | if (ret < 0) | 912 | } |
909 | return ret; | ||
910 | 913 | ||
911 | WARN_ON(!uprobe_filter_is_empty(&tu->filter)); | 914 | WARN_ON(!uprobe_filter_is_empty(&tu->filter)); |
912 | 915 | ||
913 | if (enabled) | 916 | if (enabled) |
914 | return 0; | 917 | return 0; |
915 | 918 | ||
919 | ret = uprobe_buffer_enable(); | ||
920 | if (ret) | ||
921 | goto err_flags; | ||
922 | |||
916 | tu->consumer.filter = filter; | 923 | tu->consumer.filter = filter; |
917 | ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); | 924 | ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); |
918 | if (ret) { | 925 | if (ret) |
919 | if (file) { | 926 | goto err_buffer; |
920 | list_del(&link->list); | ||
921 | kfree(link); | ||
922 | tu->tp.flags &= ~TP_FLAG_TRACE; | ||
923 | } else | ||
924 | tu->tp.flags &= ~TP_FLAG_PROFILE; | ||
925 | } | ||
926 | 927 | ||
928 | return 0; | ||
929 | |||
930 | err_buffer: | ||
931 | uprobe_buffer_disable(); | ||
932 | |||
933 | err_flags: | ||
934 | if (file) { | ||
935 | list_del(&link->list); | ||
936 | kfree(link); | ||
937 | tu->tp.flags &= ~TP_FLAG_TRACE; | ||
938 | } else { | ||
939 | tu->tp.flags &= ~TP_FLAG_PROFILE; | ||
940 | } | ||
927 | return ret; | 941 | return ret; |
928 | } | 942 | } |
929 | 943 | ||
@@ -1201,12 +1215,6 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) | |||
1201 | 1215 | ||
1202 | current->utask->vaddr = (unsigned long) &udd; | 1216 | current->utask->vaddr = (unsigned long) &udd; |
1203 | 1217 | ||
1204 | #ifdef CONFIG_PERF_EVENTS | ||
1205 | if ((tu->tp.flags & TP_FLAG_TRACE) == 0 && | ||
1206 | !uprobe_perf_filter(&tu->consumer, 0, current->mm)) | ||
1207 | return UPROBE_HANDLER_REMOVE; | ||
1208 | #endif | ||
1209 | |||
1210 | if (WARN_ON_ONCE(!uprobe_cpu_buffer)) | 1218 | if (WARN_ON_ONCE(!uprobe_cpu_buffer)) |
1211 | return 0; | 1219 | return 0; |
1212 | 1220 | ||