Skip to content

Commit c29f122

Browse files
Steven Rostedtrostedt
authored andcommitted
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: Steven Rostedt <rostedt@goodmis.org>
1 parent 0a01640 commit c29f122

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

kernel/trace/ftrace.c

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,27 @@ static void
156156
ftrace_global_list_func(unsigned long ip, unsigned long parent_ip,
157157
struct ftrace_ops *op, struct pt_regs *regs)
158158
{
159-
if (unlikely(trace_recursion_test(TRACE_GLOBAL_BIT)))
159+
int bit;
160+
161+
if (in_interrupt()) {
162+
if (in_nmi())
163+
bit = TRACE_GLOBAL_NMI_BIT;
164+
165+
else if (in_irq())
166+
bit = TRACE_GLOBAL_IRQ_BIT;
167+
else
168+
bit = TRACE_GLOBAL_SIRQ_BIT;
169+
} else
170+
bit = TRACE_GLOBAL_BIT;
171+
172+
if (unlikely(trace_recursion_test(bit)))
160173
return;
161174

162-
trace_recursion_set(TRACE_GLOBAL_BIT);
175+
trace_recursion_set(bit);
163176
do_for_each_ftrace_op(op, ftrace_global_list) {
164177
op->func(ip, parent_ip, op, regs);
165178
} while_for_each_ftrace_op(op);
166-
trace_recursion_clear(TRACE_GLOBAL_BIT);
179+
trace_recursion_clear(bit);
167180
}
168181

169182
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,
41324145
struct ftrace_ops *ignored, struct pt_regs *regs)
41334146
{
41344147
struct ftrace_ops *op;
4148+
unsigned int bit;
41354149

41364150
if (function_trace_stop)
41374151
return;
41384152

4139-
if (unlikely(trace_recursion_test(TRACE_INTERNAL_BIT)))
4140-
return;
4153+
if (in_interrupt()) {
4154+
if (in_nmi())
4155+
bit = TRACE_INTERNAL_NMI_BIT;
4156+
4157+
else if (in_irq())
4158+
bit = TRACE_INTERNAL_IRQ_BIT;
4159+
else
4160+
bit = TRACE_INTERNAL_SIRQ_BIT;
4161+
} else
4162+
bit = TRACE_INTERNAL_BIT;
4163+
4164+
if (unlikely(trace_recursion_test(bit)))
4165+
return;
4166+
4167+
trace_recursion_set(bit);
41414168

4142-
trace_recursion_set(TRACE_INTERNAL_BIT);
41434169
/*
41444170
* Some of the ops may be dynamically allocated,
41454171
* they must be freed after a synchronize_sched().
@@ -4150,7 +4176,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
41504176
op->func(ip, parent_ip, op, regs);
41514177
} while_for_each_ftrace_op(op);
41524178
preempt_enable_notrace();
4153-
trace_recursion_clear(TRACE_INTERNAL_BIT);
4179+
trace_recursion_clear(bit);
41544180
}
41554181

41564182
/*

kernel/trace/trace.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,14 @@ struct tracer {
299299

300300
/* for function tracing recursion */
301301
#define TRACE_INTERNAL_BIT (1<<11)
302-
#define TRACE_GLOBAL_BIT (1<<12)
303-
#define TRACE_CONTROL_BIT (1<<13)
302+
#define TRACE_INTERNAL_NMI_BIT (1<<12)
303+
#define TRACE_INTERNAL_IRQ_BIT (1<<13)
304+
#define TRACE_INTERNAL_SIRQ_BIT (1<<14)
305+
#define TRACE_GLOBAL_BIT (1<<15)
306+
#define TRACE_GLOBAL_NMI_BIT (1<<16)
307+
#define TRACE_GLOBAL_IRQ_BIT (1<<17)
308+
#define TRACE_GLOBAL_SIRQ_BIT (1<<18)
309+
#define TRACE_CONTROL_BIT (1<<19)
304310

305311
/*
306312
* Abuse of the trace_recursion.
@@ -309,7 +315,7 @@ struct tracer {
309315
* was called in irq context but we have irq tracing off. Since this
310316
* can only be modified by current, we can reuse trace_recursion.
311317
*/
312-
#define TRACE_IRQ_BIT (1<<13)
318+
#define TRACE_IRQ_BIT (1<<20)
313319

314320
#define trace_recursion_set(bit) do { (current)->trace_recursion |= (bit); } while (0)
315321
#define trace_recursion_clear(bit) do { (current)->trace_recursion &= ~(bit); } while (0)

0 commit comments

Comments
 (0)