mirror of
https://github.com/torvalds/linux.git
synced 2025-08-15 14:11:42 +02:00

When the ring buffer was first introduced, reading the non-consuming "trace" file required disabling the writing of the ring buffer. To make sure the writing was fully disabled before iterating the buffer with a non-consuming read, it would set the disable flag of the buffer and then call an RCU synchronization to make sure all the buffers were synchronized. The function ring_buffer_read_start() originally would initialize the iterator and call an RCU synchronization, but this was for each individual per CPU buffer where this would get called many times on a machine with many CPUs before the trace file could be read. The commit72c9ddfd4c
("ring-buffer: Make non-consuming read less expensive with lots of cpus.") separated ring_buffer_read_start into ring_buffer_read_prepare(), ring_buffer_read_sync() and then ring_buffer_read_start() to allow each of the per CPU buffers to be prepared, call the read_buffer_read_sync() once, and then the ring_buffer_read_start() for each of the CPUs which made things much faster. The commit1039221cc2
("ring-buffer: Do not disable recording when there is an iterator") removed the requirement of disabling the recording of the ring buffer in order to iterate it, but it did not remove the synchronization that was happening that was required to wait for all the buffers to have no more writers. It's now OK for the buffers to have writers and no synchronization is needed. Remove the synchronization and put back the interface for the ring buffer iterator back before commit72c9ddfd4c
was applied. Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Link: https://lore.kernel.org/20250630180440.3eabb514@batman.local.home Reported-by: David Howells <dhowells@redhat.com> Fixes:1039221cc2
("ring-buffer: Do not disable recording when there is an iterator") Tested-by: David Howells <dhowells@redhat.com> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
154 lines
3.3 KiB
C
154 lines
3.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* kdb helper for dumping the ftrace buffer
|
|
*
|
|
* Copyright (C) 2010 Jason Wessel <jason.wessel@windriver.com>
|
|
*
|
|
* ftrace_dump_buf based on ftrace_dump:
|
|
* Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
|
|
* Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
|
|
*
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/kgdb.h>
|
|
#include <linux/kdb.h>
|
|
#include <linux/ftrace.h>
|
|
|
|
#include "trace.h"
|
|
#include "trace_output.h"
|
|
|
|
static struct trace_iterator iter;
|
|
static struct ring_buffer_iter *buffer_iter[CONFIG_NR_CPUS];
|
|
|
|
static void ftrace_dump_buf(int skip_entries, long cpu_file)
|
|
{
|
|
struct trace_array *tr;
|
|
unsigned int old_userobj;
|
|
int cnt = 0, cpu;
|
|
|
|
tr = iter.tr;
|
|
|
|
old_userobj = tr->trace_flags;
|
|
|
|
/* don't look at user memory in panic mode */
|
|
tr->trace_flags &= ~TRACE_ITER_SYM_USEROBJ;
|
|
|
|
kdb_printf("Dumping ftrace buffer:\n");
|
|
if (skip_entries)
|
|
kdb_printf("(skipping %d entries)\n", skip_entries);
|
|
|
|
trace_iterator_reset(&iter);
|
|
iter.iter_flags |= TRACE_FILE_LAT_FMT;
|
|
|
|
if (cpu_file == RING_BUFFER_ALL_CPUS) {
|
|
for_each_tracing_cpu(cpu) {
|
|
iter.buffer_iter[cpu] =
|
|
ring_buffer_read_start(iter.array_buffer->buffer,
|
|
cpu, GFP_ATOMIC);
|
|
tracing_iter_reset(&iter, cpu);
|
|
}
|
|
} else {
|
|
iter.cpu_file = cpu_file;
|
|
iter.buffer_iter[cpu_file] =
|
|
ring_buffer_read_start(iter.array_buffer->buffer,
|
|
cpu_file, GFP_ATOMIC);
|
|
tracing_iter_reset(&iter, cpu_file);
|
|
}
|
|
|
|
while (trace_find_next_entry_inc(&iter)) {
|
|
if (!cnt)
|
|
kdb_printf("---------------------------------\n");
|
|
cnt++;
|
|
|
|
if (!skip_entries) {
|
|
print_trace_line(&iter);
|
|
trace_printk_seq(&iter.seq);
|
|
} else {
|
|
skip_entries--;
|
|
}
|
|
|
|
if (KDB_FLAG(CMD_INTERRUPT))
|
|
goto out;
|
|
}
|
|
|
|
if (!cnt)
|
|
kdb_printf(" (ftrace buffer empty)\n");
|
|
else
|
|
kdb_printf("---------------------------------\n");
|
|
|
|
out:
|
|
tr->trace_flags = old_userobj;
|
|
|
|
for_each_tracing_cpu(cpu) {
|
|
if (iter.buffer_iter[cpu]) {
|
|
ring_buffer_read_finish(iter.buffer_iter[cpu]);
|
|
iter.buffer_iter[cpu] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* kdb_ftdump - Dump the ftrace log buffer
|
|
*/
|
|
static int kdb_ftdump(int argc, const char **argv)
|
|
{
|
|
int skip_entries = 0;
|
|
long cpu_file;
|
|
int err;
|
|
int cnt;
|
|
|
|
if (argc > 2)
|
|
return KDB_ARGCOUNT;
|
|
|
|
if (argc && kstrtoint(argv[1], 0, &skip_entries))
|
|
return KDB_BADINT;
|
|
|
|
if (argc == 2) {
|
|
err = kstrtol(argv[2], 0, &cpu_file);
|
|
if (err || cpu_file >= NR_CPUS || cpu_file < 0 ||
|
|
!cpu_online(cpu_file))
|
|
return KDB_BADINT;
|
|
} else {
|
|
cpu_file = RING_BUFFER_ALL_CPUS;
|
|
}
|
|
|
|
kdb_trap_printk++;
|
|
|
|
trace_init_global_iter(&iter);
|
|
iter.buffer_iter = buffer_iter;
|
|
|
|
tracer_tracing_disable(iter.tr);
|
|
|
|
/* A negative skip_entries means skip all but the last entries */
|
|
if (skip_entries < 0) {
|
|
if (cpu_file == RING_BUFFER_ALL_CPUS)
|
|
cnt = trace_total_entries(NULL);
|
|
else
|
|
cnt = trace_total_entries_cpu(NULL, cpu_file);
|
|
skip_entries = max(cnt + skip_entries, 0);
|
|
}
|
|
|
|
ftrace_dump_buf(skip_entries, cpu_file);
|
|
|
|
tracer_tracing_enable(iter.tr);
|
|
|
|
kdb_trap_printk--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static kdbtab_t ftdump_cmd = {
|
|
.name = "ftdump",
|
|
.func = kdb_ftdump,
|
|
.usage = "[skip_#entries] [cpu]",
|
|
.help = "Dump ftrace log; -skip dumps last #entries",
|
|
.flags = KDB_ENABLE_ALWAYS_SAFE,
|
|
};
|
|
|
|
static __init int kdb_ftrace_register(void)
|
|
{
|
|
kdb_register(&ftdump_cmd);
|
|
return 0;
|
|
}
|
|
|
|
late_initcall(kdb_ftrace_register);
|