提交 c29f122c 编写于 作者: S Steven Rostedt 提交者: Steven Rostedt

ftrace: Add context level recursion bit checking

Currently for recursion checking in the function tracer, ftrace
tests a task_struct bit to determine if the function tracer had
recursed or not. If it has, then it will will return without going
further.

But this leads to races. If an interrupt came in after the bit
was set, the functions being traced would see that bit set and
think that the function tracer recursed on itself, and would return.

Instead add a bit for each context (normal, softirq, irq and nmi).

A check of which context the task is in is made before testing the
associated bit. Now if an interrupt preempts the function tracer
after the previous context has been set, the interrupt functions
can still be traced.
Signed-off-by: NSteven Rostedt <rostedt@goodmis.org>
上级 0a016409
...@@ -156,14 +156,27 @@ static void ...@@ -156,14 +156,27 @@ static void
ftrace_global_list_func(unsigned long ip, unsigned long parent_ip, ftrace_global_list_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs) struct ftrace_ops *op, struct pt_regs *regs)
{ {
if (unlikely(trace_recursion_test(TRACE_GLOBAL_BIT))) int bit;
if (in_interrupt()) {
if (in_nmi())
bit = TRACE_GLOBAL_NMI_BIT;
else if (in_irq())
bit = TRACE_GLOBAL_IRQ_BIT;
else
bit = TRACE_GLOBAL_SIRQ_BIT;
} else
bit = TRACE_GLOBAL_BIT;
if (unlikely(trace_recursion_test(bit)))
return; return;
trace_recursion_set(TRACE_GLOBAL_BIT); trace_recursion_set(bit);
do_for_each_ftrace_op(op, ftrace_global_list) { do_for_each_ftrace_op(op, ftrace_global_list) {
op->func(ip, parent_ip, op, regs); op->func(ip, parent_ip, op, regs);
} while_for_each_ftrace_op(op); } while_for_each_ftrace_op(op);
trace_recursion_clear(TRACE_GLOBAL_BIT); trace_recursion_clear(bit);
} }
static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip, static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
...@@ -4132,14 +4145,27 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, ...@@ -4132,14 +4145,27 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ignored, struct pt_regs *regs) struct ftrace_ops *ignored, struct pt_regs *regs)
{ {
struct ftrace_ops *op; struct ftrace_ops *op;
unsigned int bit;
if (function_trace_stop) if (function_trace_stop)
return; return;
if (unlikely(trace_recursion_test(TRACE_INTERNAL_BIT))) if (in_interrupt()) {
return; if (in_nmi())
bit = TRACE_INTERNAL_NMI_BIT;
else if (in_irq())
bit = TRACE_INTERNAL_IRQ_BIT;
else
bit = TRACE_INTERNAL_SIRQ_BIT;
} else
bit = TRACE_INTERNAL_BIT;
if (unlikely(trace_recursion_test(bit)))
return;
trace_recursion_set(bit);
trace_recursion_set(TRACE_INTERNAL_BIT);
/* /*
* Some of the ops may be dynamically allocated, * Some of the ops may be dynamically allocated,
* they must be freed after a synchronize_sched(). * they must be freed after a synchronize_sched().
...@@ -4150,7 +4176,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, ...@@ -4150,7 +4176,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
op->func(ip, parent_ip, op, regs); op->func(ip, parent_ip, op, regs);
} while_for_each_ftrace_op(op); } while_for_each_ftrace_op(op);
preempt_enable_notrace(); preempt_enable_notrace();
trace_recursion_clear(TRACE_INTERNAL_BIT); trace_recursion_clear(bit);
} }
/* /*
......
...@@ -299,8 +299,14 @@ struct tracer { ...@@ -299,8 +299,14 @@ struct tracer {
/* for function tracing recursion */ /* for function tracing recursion */
#define TRACE_INTERNAL_BIT (1<<11) #define TRACE_INTERNAL_BIT (1<<11)
#define TRACE_GLOBAL_BIT (1<<12) #define TRACE_INTERNAL_NMI_BIT (1<<12)
#define TRACE_CONTROL_BIT (1<<13) #define TRACE_INTERNAL_IRQ_BIT (1<<13)
#define TRACE_INTERNAL_SIRQ_BIT (1<<14)
#define TRACE_GLOBAL_BIT (1<<15)
#define TRACE_GLOBAL_NMI_BIT (1<<16)
#define TRACE_GLOBAL_IRQ_BIT (1<<17)
#define TRACE_GLOBAL_SIRQ_BIT (1<<18)
#define TRACE_CONTROL_BIT (1<<19)
/* /*
* Abuse of the trace_recursion. * Abuse of the trace_recursion.
...@@ -309,7 +315,7 @@ struct tracer { ...@@ -309,7 +315,7 @@ struct tracer {
* was called in irq context but we have irq tracing off. Since this * was called in irq context but we have irq tracing off. Since this
* can only be modified by current, we can reuse trace_recursion. * can only be modified by current, we can reuse trace_recursion.
*/ */
#define TRACE_IRQ_BIT (1<<13) #define TRACE_IRQ_BIT (1<<20)
#define trace_recursion_set(bit) do { (current)->trace_recursion |= (bit); } while (0) #define trace_recursion_set(bit) do { (current)->trace_recursion |= (bit); } while (0)
#define trace_recursion_clear(bit) do { (current)->trace_recursion &= ~(bit); } while (0) #define trace_recursion_clear(bit) do { (current)->trace_recursion &= ~(bit); } while (0)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册