Skip to content

Commit dc1b157

Browse files
committed
tracing: Fix archs that still call tracepoints without RCU watching
Tracepoints require having RCU "watching" as it uses RCU to do updates to the tracepoints. There are some cases that would call a tracepoint when RCU was not "watching". This was usually in the idle path where RCU has "shutdown". For the few locations that had tracepoints without RCU watching, there was an trace_*_rcuidle() variant that could be used. This used SRCU for protection. There are tracepoints that trace when interrupts and preemption are enabled and disabled. In some architectures, these tracepoints are called in a path where RCU is not watching. When x86 and arm64 removed these locations, it was incorrectly assumed that it would be safe to remove the trace_*_rcuidle() variant and also remove the SRCU logic, as it made the code more complex and harder to implement new tracepoint features (like faultable tracepoints and tracepoints in rust). Instead of bringing back the trace_*_rcuidle(), as it will not be trivial to do as new code has already been added depending on its removal, add a workaround to the one file that still requires it (trace_preemptirq.c). If the architecture does not define CONFIG_ARCH_WANTS_NO_INSTR, then check if the code is in the idle path, and if so, call ct_irq_enter/exit() which will enable RCU around the tracepoint. Cc: Masami Hiramatsu <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Mark Rutland <[email protected]> Link: https://lore.kernel.org/[email protected] Reported-by: Geert Uytterhoeven <[email protected]> Fixes: 48bcda6 ("tracing: Remove definition of trace_*_rcuidle()") Closes: https://lore.kernel.org/all/[email protected]/ Acked-by: Paul E. McKenney <[email protected]> Tested-by: Guenter Roeck <[email protected]> Tested-by: Geert Uytterhoeven <[email protected]> Tested-by: Madhavan Srinivasan <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent e63fbd5 commit dc1b157

File tree

1 file changed

+37
-6
lines changed

1 file changed

+37
-6
lines changed

kernel/trace/trace_preemptirq.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,42 @@
1010
#include <linux/module.h>
1111
#include <linux/ftrace.h>
1212
#include <linux/kprobes.h>
13+
#include <linux/hardirq.h>
1314
#include "trace.h"
1415

1516
#define CREATE_TRACE_POINTS
1617
#include <trace/events/preemptirq.h>
1718

19+
/*
20+
* Use regular trace points on architectures that implement noinstr
21+
* tooling: these calls will only happen with RCU enabled, which can
22+
* use a regular tracepoint.
23+
*
24+
* On older architectures, RCU may not be watching in idle. In that
25+
* case, wake up RCU to watch while calling the tracepoint. These
26+
* aren't NMI-safe - so exclude NMI contexts:
27+
*/
28+
#ifdef CONFIG_ARCH_WANTS_NO_INSTR
29+
#define trace(point, args) trace_##point(args)
30+
#else
31+
#define trace(point, args) \
32+
do { \
33+
if (trace_##point##_enabled()) { \
34+
bool exit_rcu = false; \
35+
if (in_nmi()) \
36+
break; \
37+
if (!IS_ENABLED(CONFIG_TINY_RCU) && \
38+
is_idle_task(current)) { \
39+
ct_irq_enter(); \
40+
exit_rcu = true; \
41+
} \
42+
trace_##point(args); \
43+
if (exit_rcu) \
44+
ct_irq_exit(); \
45+
} \
46+
} while (0)
47+
#endif
48+
1849
#ifdef CONFIG_TRACE_IRQFLAGS
1950
/* Per-cpu variable to prevent redundant calls when IRQs already off */
2051
static DEFINE_PER_CPU(int, tracing_irq_cpu);
@@ -28,7 +59,7 @@ static DEFINE_PER_CPU(int, tracing_irq_cpu);
2859
void trace_hardirqs_on_prepare(void)
2960
{
3061
if (this_cpu_read(tracing_irq_cpu)) {
31-
trace_irq_enable(CALLER_ADDR0, CALLER_ADDR1);
62+
trace(irq_enable, TP_ARGS(CALLER_ADDR0, CALLER_ADDR1));
3263
tracer_hardirqs_on(CALLER_ADDR0, CALLER_ADDR1);
3364
this_cpu_write(tracing_irq_cpu, 0);
3465
}
@@ -39,7 +70,7 @@ NOKPROBE_SYMBOL(trace_hardirqs_on_prepare);
3970
void trace_hardirqs_on(void)
4071
{
4172
if (this_cpu_read(tracing_irq_cpu)) {
42-
trace_irq_enable(CALLER_ADDR0, CALLER_ADDR1);
73+
trace(irq_enable, TP_ARGS(CALLER_ADDR0, CALLER_ADDR1));
4374
tracer_hardirqs_on(CALLER_ADDR0, CALLER_ADDR1);
4475
this_cpu_write(tracing_irq_cpu, 0);
4576
}
@@ -61,7 +92,7 @@ void trace_hardirqs_off_finish(void)
6192
if (!this_cpu_read(tracing_irq_cpu)) {
6293
this_cpu_write(tracing_irq_cpu, 1);
6394
tracer_hardirqs_off(CALLER_ADDR0, CALLER_ADDR1);
64-
trace_irq_disable(CALLER_ADDR0, CALLER_ADDR1);
95+
trace(irq_disable, TP_ARGS(CALLER_ADDR0, CALLER_ADDR1));
6596
}
6697

6798
}
@@ -75,7 +106,7 @@ void trace_hardirqs_off(void)
75106
if (!this_cpu_read(tracing_irq_cpu)) {
76107
this_cpu_write(tracing_irq_cpu, 1);
77108
tracer_hardirqs_off(CALLER_ADDR0, CALLER_ADDR1);
78-
trace_irq_disable(CALLER_ADDR0, CALLER_ADDR1);
109+
trace(irq_disable, TP_ARGS(CALLER_ADDR0, CALLER_ADDR1));
79110
}
80111
}
81112
EXPORT_SYMBOL(trace_hardirqs_off);
@@ -86,13 +117,13 @@ NOKPROBE_SYMBOL(trace_hardirqs_off);
86117

87118
void trace_preempt_on(unsigned long a0, unsigned long a1)
88119
{
89-
trace_preempt_enable(a0, a1);
120+
trace(preempt_enable, TP_ARGS(a0, a1));
90121
tracer_preempt_on(a0, a1);
91122
}
92123

93124
void trace_preempt_off(unsigned long a0, unsigned long a1)
94125
{
95-
trace_preempt_disable(a0, a1);
126+
trace(preempt_disable, TP_ARGS(a0, a1));
96127
tracer_preempt_off(a0, a1);
97128
}
98129
#endif

0 commit comments

Comments
 (0)