From ac1d52d0b85854958c7e78c8006e39aadb6ce4b8 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 16 Mar 2009 00:32:41 +0100 Subject: tracing/ftrace: fix double calls to tracing_start() Impact: fix a warning during preemptirqsoff selftests When the preemptirqsoff selftest fails, we see the following warning: [ 6.050000] Testing tracer preemptirqsoff: .. no entries found .. ------------[ cut here ]------------ [ 6.060000] WARNING: at kernel/trace/trace.c:688 tracing_start+0x67/0xd3() [ 6.060000] Modules linked in: [ 6.060000] Pid: 1, comm: swapper Tainted: G [ 6.060000] Call Trace: [ 6.060000] [] warn_slowpath+0xb1/0x100 [ 6.060000] [] ? trace_preempt_on+0x35/0x4b [ 6.060000] [] ? tracing_start+0x31/0xd3 [ 6.060000] [] ? tracing_start+0x31/0xd3 [ 6.060000] [] ? __lock_acquired+0xe6/0x1f2 [ 6.060000] [] ? tracing_start+0x31/0xd3 [ 6.060000] [] tracing_start+0x67/0xd3 [ 6.060000] [] ? irqsoff_tracer_reset+0x2d/0x57 [ 6.060000] [] trace_selftest_startup_preemptirqsoff+0x1c8/0x1f1 [ 6.060000] [] register_tracer+0x12f/0x241 [ 6.060000] [] ? init_irqsoff_tracer+0x0/0x53 [ 6.060000] [] init_irqsoff_tracer+0x3b/0x53 This is because in fail case, the preemptirqsoff tracer selftest calls twice the tracing_start() function: int trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array *tr) { if (!ret && !count) { printk(KERN_CONT ".. no entries found .."); ret = -1; tracing_start(); <----- goto out; } [...] out: trace->reset(tr); tracing_start(); <------ tracing_max_latency = save_max; return ret; } Since it is well handled in the out path, we don't need the conditional one. Reported-by: Ingo Molnar Signed-off-by: Frederic Weisbecker LKML-Reference: <1237159961-7447-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar --- kernel/trace/trace_selftest.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'kernel/trace/trace_selftest.c') diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index f907a2b29028..a2ca6f0fef9b 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -414,7 +414,7 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * ret = tracer_init(trace, tr); if (ret) { warn_failed_init_tracer(trace, ret); - goto out; + goto out_no_start; } /* reset the max latency */ @@ -432,21 +432,16 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * tracing_stop(); /* check both trace buffers */ ret = trace_test_buffer(tr, NULL); - if (ret) { - tracing_start(); + if (ret) goto out; - } ret = trace_test_buffer(&max_tr, &count); - if (ret) { - tracing_start(); + if (ret) goto out; - } if (!ret && !count) { printk(KERN_CONT ".. no entries found .."); ret = -1; - tracing_start(); goto out; } @@ -475,9 +470,10 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * goto out; } - out: - trace->reset(tr); +out: tracing_start(); +out_no_start: + trace->reset(tr); tracing_max_latency = save_max; return ret; -- cgit v1.2.3 From 490362003457f8d387f6f6e73e3a7efbf56c3314 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 17 Mar 2009 22:38:58 +0100 Subject: tracing/ftrace: stop {irqs, preempt}soff tracers when tracing is stopped Impact: fix a selftest warning In some cases, it's possible to see the following warning on irqsoff tracer selftest: [ 4.640003] Testing tracer irqsoff: <4>------------[ cut here ]------------ [ 4.653562] WARNING: at kernel/trace/trace.c:458 update_max_tr_single+0x9a/0xc4() [ 4.660000] Hardware name: System Product Name [ 4.660000] Modules linked in: [ 4.660000] Pid: 301, comm: kstop/1 Not tainted 2.6.29-rc8-tip #35837 [ 4.660000] Call Trace: [ 4.660000] [<4014b588>] warn_slowpath+0x79/0x8f [ 4.660000] [<402d6949>] ? put_dec+0x64/0x6b [ 4.660000] [<40162b56>] ? getnstimeofday+0x58/0xdd [ 4.660000] [<40162210>] ? clocksource_read+0x3/0xf [ 4.660000] [<4015eb44>] ? ktime_set+0x8/0x34 [ 4.660000] [<4014101a>] ? balance_runtime+0x8/0x56 [ 4.660000] [<405f6f11>] ? _spin_lock+0x3/0x10 [ 4.660000] [<4011f643>] ? ftrace_call+0x5/0x8 [ 4.660000] [<4015d0f1>] ? task_cputime_zero+0x3/0x27 [ 4.660000] [<40190ee7>] ? cpupri_set+0x90/0xcb [ 4.660000] [<405f7208>] ? _spin_lock_irqsave+0x22/0x34 [ 4.660000] [<40190f12>] ? cpupri_set+0xbb/0xcb [ 4.660000] [<405f7151>] ? _spin_unlock_irqrestore+0x23/0x35 [ 4.660000] [<4018493f>] ? ring_buffer_reset_cpu+0x27/0x51 [ 4.660000] [<405f7208>] ? _spin_lock_irqsave+0x22/0x34 [ 4.660000] [<40184962>] ? ring_buffer_reset_cpu+0x4a/0x51 [ 4.660000] [<405f7151>] ? _spin_unlock_irqrestore+0x23/0x35 [ 4.660000] [<4018cc29>] ? trace_hardirqs_off+0x1a/0x1c [ 4.660000] [<405f7151>] ? _spin_unlock_irqrestore+0x23/0x35 [ 4.660000] [<40184962>] ? ring_buffer_reset_cpu+0x4a/0x51 [ 4.660000] [<401850f3>] ? cpumask_next+0x15/0x18 [ 4.660000] [<4018a41f>] update_max_tr_single+0x9a/0xc4 [ 4.660000] [<4014e5fe>] ? exit_notify+0x16/0xf2 [ 4.660000] [<4018cd13>] check_critical_timing+0xcc/0x11e [ 4.660000] [<4014e5fe>] ? exit_notify+0x16/0xf2 [ 4.660000] [<4014e5fe>] ? exit_notify+0x16/0xf2 [ 4.660000] [<4018cdf1>] stop_critical_timing+0x8c/0x9f [ 4.660000] [<4014e5c4>] ? forget_original_parent+0xac/0xd0 [ 4.660000] [<4018ce3a>] trace_hardirqs_on+0x1a/0x1c [ 4.660000] [<4014e5c4>] forget_original_parent+0xac/0xd0 [ 4.660000] [<4014e5fe>] exit_notify+0x16/0xf2 [ 4.660000] [<4014e8a5>] do_exit+0x1cb/0x225 [ 4.660000] [<4015c72b>] ? kthread+0x0/0x69 [ 4.660000] [<4011f61d>] kernel_thread_helper+0xd/0x10 [ 4.660000] ---[ end trace a7919e7f17c0a725 ]--- [ 4.660164] .. no entries found ..FAILED! During the selftest of irqsoff tracer, we do that: /* disable interrupts for a bit */ local_irq_disable(); udelay(100); local_irq_enable(); /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ ret = trace_test_buffer(tr, NULL); If a callsite performs a new max delay with irqs off just after tracing_stop, update_max_tr_single() -> ring_buffer_swap_cpu() will be called with the buffers disabled by tracing_stop(), hence the warning, then ring_buffer_swap_cpu() return -EAGAIN and update_max_tr_single() complains. Fix it by also stopping the tracer before stopping the tracing globally. A similar situation can happen with preemptoff and preemptirqsoff tracers where we apply the same fix. Reported-by: Ingo Molnar Signed-off-by: Frederic Weisbecker Acked-by: Steven Rostedt LKML-Reference: <1237325938-5240-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar --- kernel/trace/trace_selftest.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'kernel/trace/trace_selftest.c') diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index a2ca6f0fef9b..38856ba78a92 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -315,6 +315,14 @@ trace_selftest_startup_irqsoff(struct tracer *trace, struct trace_array *tr) local_irq_disable(); udelay(100); local_irq_enable(); + + /* + * Stop the tracer to avoid a warning subsequent + * to buffer flipping failure because tracing_stop() + * disables the tr and max buffers, making flipping impossible + * in case of parallels max irqs off latencies. + */ + trace->stop(tr); /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ @@ -369,6 +377,14 @@ trace_selftest_startup_preemptoff(struct tracer *trace, struct trace_array *tr) preempt_disable(); udelay(100); preempt_enable(); + + /* + * Stop the tracer to avoid a warning subsequent + * to buffer flipping failure because tracing_stop() + * disables the tr and max buffers, making flipping impossible + * in case of parallels max preempt off latencies. + */ + trace->stop(tr); /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ @@ -428,6 +444,13 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * /* reverse the order of preempt vs irqs */ local_irq_enable(); + /* + * Stop the tracer to avoid a warning subsequent + * to buffer flipping failure because tracing_stop() + * disables the tr and max buffers, making flipping impossible + * in case of parallels max irqs/preempt off latencies. + */ + trace->stop(tr); /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ @@ -448,6 +471,8 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * /* do the test by disabling interrupts first this time */ tracing_max_latency = 0; tracing_start(); + trace->start(tr); + preempt_disable(); local_irq_disable(); udelay(100); @@ -455,6 +480,7 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * /* reverse the order of preempt vs irqs */ local_irq_enable(); + trace->stop(tr); /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ -- cgit v1.2.3 From cf586b61f80229491127d3c57c06ed93c9f530d3 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 22 Mar 2009 05:04:35 +0100 Subject: tracing/function-graph-tracer: prevent hangs during self-tests Impact: detect tracing related hangs Sometimes, with some configs, the function graph tracer can make the timer interrupt too much slow, hanging the kernel in an endless loop of timer interrupts servicing. As suggested by Ingo, this patch brings a watchdog which stops the selftest after a defined number of functions traced, definitely disabling this tracer. For those who want to debug the cause of the function graph trace hang, you can pass the ftrace_dump_on_oops kernel parameter to dump the traces after this hang detection. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt LKML-Reference: <1237694675-23509-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar --- kernel/trace/trace_selftest.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'kernel/trace/trace_selftest.c') diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 38856ba78a92..b56dcf7d3566 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -248,6 +248,28 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) #ifdef CONFIG_FUNCTION_GRAPH_TRACER + +/* Maximum number of functions to trace before diagnosing a hang */ +#define GRAPH_MAX_FUNC_TEST 100000000 + +static void __ftrace_dump(bool disable_tracing); +static unsigned int graph_hang_thresh; + +/* Wrap the real function entry probe to avoid possible hanging */ +static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace) +{ + /* This is harmlessly racy, we want to approximately detect a hang */ + if (unlikely(++graph_hang_thresh > GRAPH_MAX_FUNC_TEST)) { + ftrace_graph_stop(); + printk(KERN_WARNING "BUG: Function graph tracer hang!\n"); + if (ftrace_dump_on_oops) + __ftrace_dump(false); + return 0; + } + + return trace_graph_entry(trace); +} + /* * Pretty much the same than for the function tracer from which the selftest * has been borrowed. @@ -259,15 +281,29 @@ trace_selftest_startup_function_graph(struct tracer *trace, int ret; unsigned long count; - ret = tracer_init(trace, tr); + /* + * Simulate the init() callback but we attach a watchdog callback + * to detect and recover from possible hangs + */ + tracing_reset_online_cpus(tr); + ret = register_ftrace_graph(&trace_graph_return, + &trace_graph_entry_watchdog); if (ret) { warn_failed_init_tracer(trace, ret); goto out; } + tracing_start_cmdline_record(); /* Sleep for a 1/10 of a second */ msleep(100); + /* Have we just recovered from a hang? */ + if (graph_hang_thresh > GRAPH_MAX_FUNC_TEST) { + trace->reset(tr); + ret = -1; + goto out; + } + tracing_stop(); /* check the trace buffer */ -- cgit v1.2.3 From 0cf53ff62b3e9e491ff5e5f05b193fb6ce643047 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 22 Mar 2009 15:13:07 +0100 Subject: tracing: keep the tracing buffer after self-test failure Instead of using ftrace_dump_on_oops, it's far more convenient to have the trace leading up to a self-test failure available in /debug/tracing/trace. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt LKML-Reference: <1237694675-23509-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar --- kernel/trace/trace_selftest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel/trace/trace_selftest.c') diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index b56dcf7d3566..08f4eb2763d1 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -299,7 +299,7 @@ trace_selftest_startup_function_graph(struct tracer *trace, /* Have we just recovered from a hang? */ if (graph_hang_thresh > GRAPH_MAX_FUNC_TEST) { - trace->reset(tr); + tracing_selftest_disabled = true; ret = -1; goto out; } -- cgit v1.2.3