tracing/events: don't use wake up for events
authorFrederic Weisbecker <fweisbec@gmail.com>
Sun, 22 Mar 2009 22:10:46 +0000 (23:10 +0100)
committerIngo Molnar <mingo@elte.hu>
Mon, 23 Mar 2009 08:22:14 +0000 (09:22 +0100)
Impact: fix hard-lockup with sched switch events

Some ftrace events, such as sched wakeup, can be traced
while the runqueue lock is hold. Since they are using
trace_current_buffer_unlock_commit(), they call wake_up()
which can try to grab the runqueue lock too, resulting in
a deadlock.

Now for all event, we call a new helper:
trace_nowake_buffer_unlock_commit() which do pretty the same than
trace_current_buffer_unlock_commit() except than it doesn't call
trace_wake_up().

Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <1237759847-21025-4-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_events_stage_3.h

index e6fac0ffe6f059f04b8db758cf5360499a65936a..6bad12819eb6a38cbb479afb0cf7353d6e7759c9 100644 (file)
@@ -860,15 +860,25 @@ static void ftrace_trace_stack(struct trace_array *tr,
 static void ftrace_trace_userstack(struct trace_array *tr,
                                   unsigned long flags, int pc);
 
-void trace_buffer_unlock_commit(struct trace_array *tr,
-                               struct ring_buffer_event *event,
-                               unsigned long flags, int pc)
+static inline void __trace_buffer_unlock_commit(struct trace_array *tr,
+                                       struct ring_buffer_event *event,
+                                       unsigned long flags, int pc,
+                                       int wake)
 {
        ring_buffer_unlock_commit(tr->buffer, event);
 
        ftrace_trace_stack(tr, flags, 6, pc);
        ftrace_trace_userstack(tr, flags, pc);
-       trace_wake_up();
+
+       if (wake)
+               trace_wake_up();
+}
+
+void trace_buffer_unlock_commit(struct trace_array *tr,
+                                       struct ring_buffer_event *event,
+                                       unsigned long flags, int pc)
+{
+       __trace_buffer_unlock_commit(tr, event, flags, pc, 1);
 }
 
 struct ring_buffer_event *
@@ -882,7 +892,13 @@ trace_current_buffer_lock_reserve(unsigned char type, unsigned long len,
 void trace_current_buffer_unlock_commit(struct ring_buffer_event *event,
                                        unsigned long flags, int pc)
 {
-       return trace_buffer_unlock_commit(&global_trace, event, flags, pc);
+       return __trace_buffer_unlock_commit(&global_trace, event, flags, pc, 1);
+}
+
+void trace_nowake_buffer_unlock_commit(struct ring_buffer_event *event,
+                                       unsigned long flags, int pc)
+{
+       return __trace_buffer_unlock_commit(&global_trace, event, flags, pc, 0);
 }
 
 void
index f267723c3c52a967075a794f722b8a7fd007177f..54fd9bcd0a658e726f3328312da4128fe35d23ec 100644 (file)
@@ -483,6 +483,8 @@ trace_current_buffer_lock_reserve(unsigned char type, unsigned long len,
                                  unsigned long flags, int pc);
 void trace_current_buffer_unlock_commit(struct ring_buffer_event *event,
                                        unsigned long flags, int pc);
+void trace_nowake_buffer_unlock_commit(struct ring_buffer_event *event,
+                                       unsigned long flags, int pc);
 
 struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
                                                struct trace_array_cpu *data);
index ebf215e87d5e41d2a17d0875045aa3b2f0c37f70..9a3bd49b52e5b42a4043bc303353e78ac8da5b5a 100644 (file)
@@ -222,7 +222,7 @@ static void ftrace_raw_event_##call(proto)                          \
                                                                        \
        assign;                                                         \
                                                                        \
-       trace_current_buffer_unlock_commit(event, irq_flags, pc);       \
+       trace_nowake_buffer_unlock_commit(event, irq_flags, pc);        \
                                                                        \
        if (call->preds && !filter_match_preds(call, entry))            \
                ring_buffer_event_discard(event);                       \