tracing: Fixes for v6.12 and earlier
Subsystems (6)
Steven Rostedt
maintainercover tracing: Fixes for v6.12 and earlier
Various fixes for tracing:
- Fix tp_printk crashing the kernel
With the code that can handle a buffer from a previous boot, the trace_check_vprintf() needed access to the delta of the address space used by the old buffer and the current buffer. To do so, the trace_array (tr) parameter was used. But when tp_printk is enabled on the kernel command line, no trace buffer is used and the trace event is sent directly to printk(). That meant the tr field of the iterator descriptor was NULL, and since tp_printk still uses trace_check_vprintf() it caused a NULL dereference.
- Add ptrace.h include to x86 ftrace file for completeness
- Fix rtla installation when done with out-of-tree build
- Fix the help messages in rtla that were incorrect
- Several fixes to fix races with the timerlat and hwlat code
Several locking issues were discovered with the coordination between timerlat kthread creation and hotplug. As timerlat has callbacks from hotplug code to start kthreads when CPUs come online. There are also locking issues with grabbing the cpu_read_lock() and the locks within timerlat.
Ben Hutchings (1):
tools/rtla: Fix installation from out-of-tree build
Eder Zulian (1):
rtla: Fix the help text in osnoise and timerlat top tools
Sami Tolvanen (1):
x86/ftrace: Include <asm/ptrace.h>
Steven Rostedt (1):
tracing: Fix trace_check_vprintf() when tp_printk is used
Wei Li (4):
tracing/timerlat: Fix duplicated kthread creation due to CPU online/offline tracing/timerlat: Drop interface_lock in stop_kthread() tracing/timerlat: Fix a race during cpuhp processing tracing/hwlat: Fix a race during cpuhp processing
----
arch/x86/include/asm/ftrace.h | 2 ++ kernel/trace/trace.c | 15 +++++++++++++-- kernel/trace/trace_hwlat.c | 2 ++ kernel/trace/trace_osnoise.c | 22 +++++++++++++--------- tools/tracing/rtla/Makefile.rtla | 2 +- tools/tracing/rtla/src/osnoise_top.c | 2 +- tools/tracing/rtla/src/timerlat_top.c | 4 ++-- 7 files changed, 34 insertions(+), 15 deletions(-)
Steven Rostedt
maintainerPATCH 1/8 tracing: Fix trace_check_vprintf() when tp_printk is used
From: Steven Rostedt <[email protected]>
When the tp_printk kernel command line is used, the trace events go directly to printk(). It is still checked via the trace_check_vprintf() function to make sure the pointers of the trace event are legit.
The addition of reading buffers from previous boots required adding a delta between the addresses of the previous boot and the current boot so that the pointers in the old buffer can still be used. But this required adding a trace_array pointer to acquire the delta offsets.
The tp_printk code does not provide a trace_array (tr) pointer, so when the offsets were examined, a NULL pointer dereference happened and the kernel crashed.
If the trace_array does not exist, just default the delta offsets to zero, as that also means the trace event is not being read from a previous boot.
--- kernel/trace/trace.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
kernel/trace/trace.c +13 -2
@@ -3697,8 +3697,8 @@ static void test_can_verify(void)void trace_check_vprintf (struct trace_iterator *iter,const char *fmt,va_list ap) { -long text_delta = iter->tr->text_delta; -long data_delta = iter->tr->data_delta; +long text_delta =0 ; +long data_delta =0 ;const char *p = fmt;const char *str;bool good;
@@ -3710,6 +3710,17 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,if (static_branch_unlikely (&trace_no_verify)) goto print; +/* +* When the kernel is booted with the tp_printk command line +* parameter, trace events go directly through to printk(). +* It also is checked by this function, but it does not +* have an associated trace_array (tr) for it. +*/ +if (iter->tr) { + text_delta = iter->tr->text_delta; + data_delta = iter->tr->data_delta; + } +/* Don't bother checking when doing a ftrace_dump() */ if (iter->fmt == static_fmt_buf) goto print;
Signature · 1 line
2.45.2
Steven Rostedt
maintainerPATCH 2/8 tools/rtla: Fix installation from out-of-tree build
In reply to Steven RostedtFrom: Ben Hutchings <[email protected]>
rtla now supports out-of-tree builds, but installation fails as it still tries to install the rtla binary from the source tree. Use the existing macro $(RTLA) to refer to the binary.
--- tools/tracing/rtla/Makefile.rtla | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/tracing/rtla/Makefile.rtla +1 -1
@@ -38,7 +38,7 @@ BINDIR := /usr/bin .PHONY: install install: doc_install @$(MKDIR) -p $(DESTDIR)$(BINDIR) - $(call QUIET_INSTALL,rtla)$(INSTALL) rtla -m 755 $(DESTDIR)$(BINDIR) + $(call QUIET_INSTALL,rtla)$(INSTALL) $(RTLA) -m 755 $(DESTDIR)$(BINDIR) @$(STRIP) $(DESTDIR)$(BINDIR)/rtla @test ! -f $(DESTDIR)$(BINDIR)/osnoise || $(RM) $(DESTDIR)$(BINDIR)/osnoise @$(LN) rtla $(DESTDIR)$(BINDIR)/osnoise
Signature · 1 line
2.45.2
Steven Rostedt
maintainerPATCH 3/8 rtla: Fix the help text in osnoise and timerlat top tools
In reply to Steven RostedtFrom: Eder Zulian <[email protected]>
The help text in osnoise top and timerlat top had some minor errors and omissions. The -d option was missing the 's' (second) abbreviation and the error message for '-d' used '-D'.
--- tools/tracing/rtla/src/osnoise_top.c | 2 +- tools/tracing/rtla/src/timerlat_top.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
tools/tracing/rtla/src/osnoise_top.c +1 -1
@@ -442,7 +442,7 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)case 'd' : params->duration =parse_seconds_duration (optarg);if (!params->duration) -osnoise_top_usage (params,"Invalid -D duration\n" ); +osnoise_top_usage (params,"Invalid -d duration\n" );break ;case 'e' : tevent =trace_event_alloc (optarg);
tools/tracing/rtla/src/timerlat_top.c +2 -2
@@ -459,7 +459,7 @@ static void timerlat_top_usage(char *usage)" -c/--cpus cpus: run the tracer only on the given cpus" ," -H/--house-keeping cpus: run rtla control threads only on the given cpus" ," -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited" , -" -d/--duration time[m|h|d]: duration of the session in seconds" , +" -d/--duration time[s|m|h|d]: duration of the session" ," -D/--debug: print debug info" ," --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)" , " -t/--trace[file]: save the stopped trace to [file|timerlat_trace.txt]",
@@ -613,7 +613,7 @@ static struct timerlat_top_paramscase 'd' : params->duration =parse_seconds_duration (optarg);if (!params->duration) -timerlat_top_usage ("Invalid -D duration\n" ); +timerlat_top_usage ("Invalid -d duration\n" );break ;case 'e' : tevent =trace_event_alloc (optarg);
Signature · 1 line
2.45.2
Steven Rostedt
maintainerPATCH 4/8 x86/ftrace: Include <asm/ptrace.h>
In reply to Steven RostedtFrom: Sami Tolvanen <[email protected]>
<asm/ftrace.h> uses struct pt_regs in several places. Include <asm/ptrace.h> to ensure it's visible. This is needed to make sure object files that only include <asm/asm-prototypes.h> compile.
--- arch/x86/include/asm/ftrace.h | 2 ++ 1 file changed, 2 insertions(+)
arch/x86/include/asm/ftrace.h +2 -0
@@ -2,6 +2,8 @@#ifndef _ASM_X86_FTRACE_H#define _ASM_X86_FTRACE_H +#include <asm/ptrace.h> +#ifdef CONFIG_FUNCTION_TRACER #ifndef CC_USING_FENTRY # error Compiler does not support fentry?
Signature · 1 line
2.45.2
Steven Rostedt
maintainerPATCH 5/8 tracing/timerlat: Fix duplicated kthread creation due to CPU online/offline
In reply to Steven RostedtFrom: Wei Li <[email protected]>
osnoise_hotplug_workfn() is the asynchronous online callback for "trace/osnoise:online". It may be congested when a CPU goes online and offline repeatedly and is invoked for multiple times after a certain online.
This will lead to kthread leak and timer corruption. Add a check in start_kthread() to prevent this situation.
--- kernel/trace/trace_osnoise.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
kernel/trace/trace_osnoise.c +5 -2
@@ -2007,6 +2007,10 @@ static int start_kthread(unsigned int cpu)void *main = osnoise_main;char comm[24 ]; +/* Do not start a new thread if it is already running */ +if (per_cpu (per_cpu_osnoise_var, cpu).kthread) +return 0 ; +if (timerlat_enabled ()) {snprintf (comm,24 ,"timerlat/%d" , cpu); main = timerlat_main;
@@ -2061,11 +2065,10 @@ static int start_per_cpu_kthreads(void)if (cpumask_test_and_clear_cpu (cpu, &kthread_cpumask)) {struct task_struct *kthread; - kthread =per_cpu (per_cpu_osnoise_var, cpu).kthread; + kthread =xchg_relaxed (&(per_cpu (per_cpu_osnoise_var, cpu).kthread),NULL );if (!WARN_ON (!kthread))kthread_stop (kthread); } -per_cpu (per_cpu_osnoise_var, cpu).kthread =NULL ; }for_each_cpu (cpu, current_mask) {
Signature · 1 line
2.45.2
Steven Rostedt
maintainerPATCH 6/8 tracing/timerlat: Drop interface_lock in stop_kthread()
In reply to Steven RostedtFrom: Wei Li <[email protected]>
stop_kthread() is the offline callback for "trace/osnoise:online", since commit 5bfbcd1ee57b ("tracing/timerlat: Add interface_lock around clearing of kthread in stop_kthread()"), the following ABBA deadlock scenario is introduced:
T1 | T2 [BP] | T3 [AP]
osnoise_hotplug_workfn() | work_for_cpu_fn() | cpuhp_thread_fun()
| _cpu_down() | osnoise_cpu_die()
mutex_lock(&interface_lock) | | stop_kthread()
| cpus_write_lock() | mutex_lock(&interface_lock)
cpus_read_lock() | cpuhp_kick_ap() |As the interface_lock here in just for protecting the "kthread" field of the osn_var, use xchg() instead to fix this issue. Also use for_each_online_cpu() back in stop_per_cpu_kthreads() as it can take cpu_read_lock() again.
--- kernel/trace/trace_osnoise.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
kernel/trace/trace_osnoise.c +6 -7
@@ -1953,12 +1953,8 @@ static void stop_kthread(unsigned int cpu) {struct task_struct *kthread; -mutex_lock (&interface_lock); - kthread =per_cpu (per_cpu_osnoise_var, cpu).kthread; + kthread =xchg_relaxed (&(per_cpu (per_cpu_osnoise_var, cpu).kthread),NULL );if (kthread) { -per_cpu (per_cpu_osnoise_var, cpu).kthread =NULL ; -mutex_unlock (&interface_lock); -if (cpumask_test_and_clear_cpu (cpu, &kthread_cpumask) && !WARN_ON (!test_bit (OSN_WORKLOAD , &osnoise_options))) {kthread_stop (kthread);
@@ -1972,7 +1968,6 @@ static void stop_kthread(unsigned int cpu)put_task_struct (kthread); } }else { -mutex_unlock (&interface_lock);/* if no workload, just return */ if (!test_bit (OSN_WORKLOAD , &osnoise_options)) {/*
@@ -1994,8 +1989,12 @@ static void stop_per_cpu_kthreads(void) {int cpu; -for_each_possible_cpu (cpu) +cpus_read_lock (); + +for_each_online_cpu (cpu)stop_kthread (cpu); + +cpus_read_unlock (); }/*
Signature · 1 line
2.45.2
Steven Rostedt
maintainerPATCH 7/8 tracing/timerlat: Fix a race during cpuhp processing
In reply to Steven RostedtFrom: Wei Li <[email protected]>
There is another found exception that the "timerlat/1" thread was scheduled on CPU0, and lead to timer corruption finally:
``` ODEBUG: init active (active state 0) object: ffff888237c2e108 object type: hrtimer hint: timerlat_irq+0x0/0x220 WARNING: CPU: 0 PID: 426 at lib/debugobjects.c:518 debug_print_object+0x7d/0xb0 Modules linked in: CPU: 0 UID: 0 PID: 426 Comm: timerlat/1 Not tainted 6.11.0-rc7+ #45 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014 RIP: 0010:debug_print_object+0x7d/0xb0 ... Call Trace: <TASK> ? __warn+0x7c/0x110 ? debug_print_object+0x7d/0xb0 ? report_bug+0xf1/0x1d0 ? prb_read_valid+0x17/0x20 ? handle_bug+0x3f/0x70 ? exc_invalid_op+0x13/0x60 ? asm_exc_invalid_op+0x16/0x20 ? debug_print_object+0x7d/0xb0 ? debug_print_object+0x7d/0xb0 ? __pfx_timerlat_irq+0x10/0x10 __debug_object_init+0x110/0x150 hrtimer_init+0x1d/0x60 timerlat_main+0xab/0x2d0 ? __pfx_timerlat_main+0x10/0x10 kthread+0xb7/0xe0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x2d/0x40 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> ```
After tracing the scheduling event, it was discovered that the migration of the "timerlat/1" thread was performed during thread creation. Further analysis confirmed that it is because the CPU online processing for osnoise is implemented through workers, which is asynchronous with the offline processing. When the worker was scheduled to create a thread, the CPU may has already been removed from the cpu_online_mask during the offline process, resulting in the inability to select the right CPU:
T1 | T2
[CPUHP_ONLINE] | cpu_device_down()
osnoise_hotplug_workfn() |
| cpus_write_lock()
| takedown_cpu(1)
| cpus_write_unlock()
[CPUHP_OFFLINE] |
cpus_read_lock() |
start_kthread(1) |
cpus_read_unlock() |To fix this, skip online processing if the CPU is already offline.
--- kernel/trace/trace_osnoise.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/trace/trace_osnoise.c +2 -0
@@ -2097,6 +2097,8 @@ static void osnoise_hotplug_workfn(struct work_struct *dummy)mutex_lock (&interface_lock);cpus_read_lock (); +if (!cpu_online (cpu)) + goto out_unlock;if (!cpumask_test_cpu (cpu, &osnoise_cpumask)) goto out_unlock;
Signature · 1 line
2.45.2
Steven Rostedt
maintainerPATCH 8/8 tracing/hwlat: Fix a race during cpuhp processing
In reply to Steven RostedtFrom: Wei Li <[email protected]>
The cpuhp online/offline processing race also exists in percpu-mode hwlat tracer in theory, apply the fix too. That is:
T1 | T2
[CPUHP_ONLINE] | cpu_device_down()
hwlat_hotplug_workfn() |
| cpus_write_lock()
| takedown_cpu(1)
| cpus_write_unlock()
[CPUHP_OFFLINE] |
cpus_read_lock() |
start_kthread(1) |
cpus_read_unlock() |--- kernel/trace/trace_hwlat.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/trace/trace_hwlat.c +2 -0
@@ -520,6 +520,8 @@ static void hwlat_hotplug_workfn(struct work_struct *dummy)if (!hwlat_busy || hwlat_data.thread_mode !=MODE_PER_CPU ) goto out_unlock; +if (!cpu_online (cpu)) + goto out_unlock;if (!cpumask_test_cpu (cpu, tr->tracing_cpumask)) goto out_unlock;
Signature · 1 line
2.45.2