aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-11-05 22:13:05 -0500
committerIngo Molnar <mingo@elte.hu>2009-11-08 04:31:42 -0500
commit444a2a3bcd6d5bed5c823136f68fcc93c0fe283f (patch)
tree6a57308586b4e723238646074e79298845803520 /include
parent09879b99d44d701c603935ef2549004405d7f8f9 (diff)
tracing, perf_events: Protect the buffer from recursion in perf
While tracing using events with perf, if one enables the lockdep:lock_acquire event, it will infect every other perf trace events. Basically, you can enable whatever set of trace events through perf but if this event is part of the set, the only result we can get is a long list of lock_acquire events of rcu read lock, and only that. This is because of a recursion inside perf. 1) When a trace event is triggered, it will fill a per cpu buffer and submit it to perf. 2) Perf will commit this event but will also protect some data using rcu_read_lock 3) A recursion appears: rcu_read_lock triggers a lock_acquire event that will fill the per cpu event and then submit the buffer to perf. 4) Perf detects a recursion and ignores it 5) Perf continues its work on the previous event, but its buffer has been overwritten by the lock_acquire event, it has then been turned into a lock_acquire event of rcu read lock Such scenario also happens with lock_release with rcu_read_unlock(). We could turn the rcu_read_lock() into __rcu_read_lock() to drop the lock debugging from perf fast path, but that would make us lose the rcu debugging and that doesn't prevent from other possible kind of recursion from perf in the future. This patch adds a recursion protection based on a counter on the perf trace per cpu buffers to solve the problem. -v2: Fixed lost whitespace, added reviewed-by tag Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Reviewed-by: Masami Hiramatsu <mhiramat@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Li Zefan <lizf@cn.fujitsu.com> Cc: Jason Baron <jbaron@redhat.com> LKML-Reference: <1257477185-7838-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'include')
-rw-r--r--include/linux/ftrace_event.h9
-rw-r--r--include/trace/ftrace.h39
2 files changed, 37 insertions, 11 deletions
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index f7b47c336703..43360c1d8f70 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -137,8 +137,13 @@ struct ftrace_event_call {
137 137
138#define FTRACE_MAX_PROFILE_SIZE 2048 138#define FTRACE_MAX_PROFILE_SIZE 2048
139 139
140extern char *trace_profile_buf; 140struct perf_trace_buf {
141extern char *trace_profile_buf_nmi; 141 char buf[FTRACE_MAX_PROFILE_SIZE];
142 int recursion;
143};
144
145extern struct perf_trace_buf *perf_trace_buf;
146extern struct perf_trace_buf *perf_trace_buf_nmi;
142 147
143#define MAX_FILTER_PRED 32 148#define MAX_FILTER_PRED 32
144#define MAX_FILTER_STR_VAL 256 /* Should handle KSYM_SYMBOL_LEN */ 149#define MAX_FILTER_STR_VAL 256 /* Should handle KSYM_SYMBOL_LEN */
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index a7f946094128..4945d1c99864 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -649,6 +649,7 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
649 * struct ftrace_event_call *event_call = &event_<call>; 649 * struct ftrace_event_call *event_call = &event_<call>;
650 * extern void perf_tp_event(int, u64, u64, void *, int); 650 * extern void perf_tp_event(int, u64, u64, void *, int);
651 * struct ftrace_raw_##call *entry; 651 * struct ftrace_raw_##call *entry;
652 * struct perf_trace_buf *trace_buf;
652 * u64 __addr = 0, __count = 1; 653 * u64 __addr = 0, __count = 1;
653 * unsigned long irq_flags; 654 * unsigned long irq_flags;
654 * struct trace_entry *ent; 655 * struct trace_entry *ent;
@@ -673,14 +674,25 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
673 * __cpu = smp_processor_id(); 674 * __cpu = smp_processor_id();
674 * 675 *
675 * if (in_nmi()) 676 * if (in_nmi())
676 * raw_data = rcu_dereference(trace_profile_buf_nmi); 677 * trace_buf = rcu_dereference(perf_trace_buf_nmi);
677 * else 678 * else
678 * raw_data = rcu_dereference(trace_profile_buf); 679 * trace_buf = rcu_dereference(perf_trace_buf);
679 * 680 *
680 * if (!raw_data) 681 * if (!trace_buf)
681 * goto end; 682 * goto end;
682 * 683 *
683 * raw_data = per_cpu_ptr(raw_data, __cpu); 684 * trace_buf = per_cpu_ptr(trace_buf, __cpu);
685 *
686 * // Avoid recursion from perf that could mess up the buffer
687 * if (trace_buf->recursion++)
688 * goto end_recursion;
689 *
690 * raw_data = trace_buf->buf;
691 *
692 * // Make recursion update visible before entering perf_tp_event
693 * // so that we protect from perf recursions.
694 *
695 * barrier();
684 * 696 *
685 * //zero dead bytes from alignment to avoid stack leak to userspace: 697 * //zero dead bytes from alignment to avoid stack leak to userspace:
686 * *(u64 *)(&raw_data[__entry_size - sizeof(u64)]) = 0ULL; 698 * *(u64 *)(&raw_data[__entry_size - sizeof(u64)]) = 0ULL;
@@ -713,8 +725,9 @@ static void ftrace_profile_##call(proto) \
713{ \ 725{ \
714 struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ 726 struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
715 struct ftrace_event_call *event_call = &event_##call; \ 727 struct ftrace_event_call *event_call = &event_##call; \
716 extern void perf_tp_event(int, u64, u64, void *, int); \ 728 extern void perf_tp_event(int, u64, u64, void *, int); \
717 struct ftrace_raw_##call *entry; \ 729 struct ftrace_raw_##call *entry; \
730 struct perf_trace_buf *trace_buf; \
718 u64 __addr = 0, __count = 1; \ 731 u64 __addr = 0, __count = 1; \
719 unsigned long irq_flags; \ 732 unsigned long irq_flags; \
720 struct trace_entry *ent; \ 733 struct trace_entry *ent; \
@@ -739,14 +752,20 @@ static void ftrace_profile_##call(proto) \
739 __cpu = smp_processor_id(); \ 752 __cpu = smp_processor_id(); \
740 \ 753 \
741 if (in_nmi()) \ 754 if (in_nmi()) \
742 raw_data = rcu_dereference(trace_profile_buf_nmi); \ 755 trace_buf = rcu_dereference(perf_trace_buf_nmi); \
743 else \ 756 else \
744 raw_data = rcu_dereference(trace_profile_buf); \ 757 trace_buf = rcu_dereference(perf_trace_buf); \
745 \ 758 \
746 if (!raw_data) \ 759 if (!trace_buf) \
747 goto end; \ 760 goto end; \
748 \ 761 \
749 raw_data = per_cpu_ptr(raw_data, __cpu); \ 762 trace_buf = per_cpu_ptr(trace_buf, __cpu); \
763 if (trace_buf->recursion++) \
764 goto end_recursion; \
765 \
766 barrier(); \
767 \
768 raw_data = trace_buf->buf; \
750 \ 769 \
751 *(u64 *)(&raw_data[__entry_size - sizeof(u64)]) = 0ULL; \ 770 *(u64 *)(&raw_data[__entry_size - sizeof(u64)]) = 0ULL; \
752 entry = (struct ftrace_raw_##call *)raw_data; \ 771 entry = (struct ftrace_raw_##call *)raw_data; \
@@ -761,6 +780,8 @@ static void ftrace_profile_##call(proto) \
761 perf_tp_event(event_call->id, __addr, __count, entry, \ 780 perf_tp_event(event_call->id, __addr, __count, entry, \
762 __entry_size); \ 781 __entry_size); \
763 \ 782 \
783end_recursion: \
784 trace_buf->recursion--; \
764end: \ 785end: \
765 local_irq_restore(irq_flags); \ 786 local_irq_restore(irq_flags); \
766 \ 787 \