tracing: A couple of fixes for 4.15
Subsystems (2)
Steven Rostedt
maintainerLinus,
This includes two fixes
- Bring back context level recursive protection in ring buffer. The simpler counter protection failed, due to a path when tracing with trace_clock_global() as it could not be reentrant and depended on the ring buffer recursive protection to keep that from happening.
- Prevent branch profiling when FORTIFY_SOURCE is enabled. It causes 50 - 60 MB in warning messages. Branch profiling should never be run on production systems, so there's no reason that it needs to be enabled with FORTIFY_SOURCE.
Please pull the latest trace-v4.15-rc4-2 tree, which can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git
trace-v4.15-rc4-2
Tag SHA1: a7fa05e04911a3f5c77818516b137ac742523911 Head SHA1: 68e76e034b6b1c1ce2eece1ab8ae4008e14be470
Randy Dunlap (1):
tracing: Prevent PROFILE_ALL_BRANCHES when FORTIFY_SOURCE=y
Steven Rostedt (VMware) (1):
ring-buffer: Bring back context level recursive checks
----
kernel/trace/Kconfig | 2 +- kernel/trace/ring_buffer.c | 62 +++++++++++++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 18 deletions(-)
Steven Rostedt
maintainerPATCH 1/2 ring-buffer: Bring back context level recursive checks
From: "Steven Rostedt (VMware)" <[email protected]>
Commit 1a149d7d3f45 ("ring-buffer: Rewrite trace_recursive_(un)lock() to be simpler") replaced the context level recursion checks with a simple counter. This would prevent the ring buffer code from recursively calling itself more than the max number of contexts that exist (Normal, softirq, irq, nmi). But this change caused a lockup in a specific case, which was during suspend and resume using a global clock. Adding a stack dump to see where this occurred, the issue was in the trace global clock itself:
trace_buffer_lock_reserve+0x1c/0x50 __trace_graph_entry+0x2d/0x90 trace_graph_entry+0xe8/0x200 prepare_ftrace_return+0x69/0xc0 ftrace_graph_caller+0x78/0xa8 queued_spin_lock_slowpath+0x5/0x1d0 trace_clock_global+0xb0/0xc0 ring_buffer_lock_reserve+0xf9/0x390
The function graph tracer traced queued_spin_lock_slowpath that was called by trace_clock_global. This pointed out that the trace_clock_global() is not reentrant, as it takes a spin lock. It depended on the ring buffer recursive lock from letting that happen.
By removing the context detection and adding just a max number of allowable recursions, it allowed the trace_clock_global() to be entered again and try to retake the spinlock it already held, causing a deadlock.
--- kernel/trace/ring_buffer.c | 62 +++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 17 deletions(-)
kernel/trace/ring_buffer.c +45 -17
@@ -2534,29 +2534,59 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer)* The lock and unlock are done within a preempt disable section. * The current_context per_cpu variable can only be modified * by the current task between lock and unlock. But it can -* be modified more than once via an interrupt. There are four -* different contexts that we need to consider. +* be modified more than once via an interrupt. To pass this +* information from the lock to the unlock without having to +* access the 'in_interrupt()' functions again (which do show +* a bit of overhead in something as critical as function tracing, +* we use a bitmask trick. * -* Normal context. -* SoftIRQ context -* IRQ context -* NMI context +* bit 0 = NMI context +* bit 1 = IRQ context +* bit 2 = SoftIRQ context +* bit 3 = normal context. * -* If for some reason the ring buffer starts to recurse, we -* only allow that to happen at most 4 times (one for each -* context). If it happens 5 times, then we consider this a -* recusive loop and do not let it go further. +* This works because this is the order of contexts that can +* preempt other contexts. A SoftIRQ never preempts an IRQ +* context. +* +* When the context is determined, the corresponding bit is +* checked and set (if it was set, then a recursion of that context +* happened). +* +* On unlock, we need to clear this bit. To do so, just subtract +* 1 from the current_context and AND it to itself. +* +* (binary) +* 101 - 1 = 100 +* 101 & 100 = 100 (clearing bit zero) +* +* 1010 - 1 = 1001 +* 1010 & 1001 = 1000 (clearing bit 1) +* +* The least significant bit can be cleared this way, and it +* just so happens that it is the same bit corresponding to +* the current context. */ static __always_inline inttrace_recursive_lock (struct ring_buffer_per_cpu *cpu_buffer) { -if (cpu_buffer->current_context >=4 ) +unsigned int val = cpu_buffer->current_context; +unsigned long pc =preempt_count (); +int bit; + +if (!(pc & (NMI_MASK |HARDIRQ_MASK |SOFTIRQ_OFFSET ))) + bit =RB_CTX_NORMAL ; +else + bit = pc &NMI_MASK ?RB_CTX_NMI : + pc &HARDIRQ_MASK ?RB_CTX_IRQ : + pc &SOFTIRQ_OFFSET ?2 :RB_CTX_SOFTIRQ ; + +if (unlikely (val & (1 << bit)))return 1 ; - cpu_buffer->current_context++; -/* Interrupts must see this update */ -barrier (); + val |= (1 << bit); + cpu_buffer->current_context = val;return 0 ; }
@@ -2564,9 +2594,7 @@ trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer) static __always_inline voidtrace_recursive_unlock (struct ring_buffer_per_cpu *cpu_buffer) { -/* Don't let the dec leak out */ -barrier (); - cpu_buffer->current_context--; + cpu_buffer->current_context &= cpu_buffer->current_context -1 ; }/**
Signature · 1 line
2.13.2
Steven Rostedt
maintainerPATCH 2/2 tracing: Prevent PROFILE_ALL_BRANCHES when FORTIFY_SOURCE=y
In reply to Steven RostedtFrom: Randy Dunlap <[email protected]>
I regularly get 50 MB - 60 MB files during kernel randconfig builds. These large files mostly contain (many repeats of; e.g., 124,594):
In file included from ../include/linux/string.h:6:0,
from ../include/linux/uuid.h:20,
from ../include/linux/mod_devicetable.h:13,
from ../scripts/mod/devicetable-offsets.c:3:
../include/linux/compiler.h:64:4: warning: '______f' is static but declared in inline function 'strcpy' which is not static [enabled by default]
______f = { \
^
../include/linux/compiler.h:56:23: note: in expansion of macro '__trace_if'
^../include/linux/string.h:425:2: note: in expansion of macro 'if'
if (p_size == (size_t)-1 && q_size == (size_t)-1)
^
This only happens when CONFIG_FORTIFY_SOURCE=y and CONFIG_PROFILE_ALL_BRANCHES=y, so prevent PROFILE_ALL_BRANCHES if FORTIFY_SOURCE=y.
--- kernel/trace/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/Kconfig +1 -1
@@ -355,7 +355,7 @@ config PROFILE_ANNOTATED_BRANCHES on if you need to profile the system's use of these macros. config PROFILE_ALL_BRANCHES - bool "Profile all if conditionals" + bool "Profile all if conditionals" if !FORTIFY_SOURCE select TRACE_BRANCH_PROFILING help This tracer profiles all branch conditions. Every if ()
Signature · 1 line
2.13.2