mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-07-08 18:13:59 +02:00
tracing: Fix syscall events activation by ensuring refcount hits zero
[ Upstream commit0a663b764d] When multiple syscall events are specified in the kernel command line (e.g., trace_event=syscalls:sys_enter_openat,syscalls:sys_enter_close), they are often not captured after boot, even though they appear enabled in the tracing/set_event file. The issue stems from how syscall events are initialized. Syscall tracepoints require the global reference count (sys_tracepoint_refcount) to transition from 0 to 1 to trigger the registration of the syscall work (TIF_SYSCALL_TRACEPOINT) for tasks, including the init process (pid 1). The current implementation of early_enable_events() with disable_first=true used an interleaved sequence of "Disable A -> Enable A -> Disable B -> Enable B". If multiple syscalls are enabled, the refcount never drops to zero, preventing the 0->1 transition that triggers actual registration. Fix this by splitting early_enable_events() into two distinct phases: 1. Disable all events specified in the buffer. 2. Enable all events specified in the buffer. This ensures the refcount hits zero before re-enabling, allowing syscall events to be properly activated during early boot. The code is also refactored to use a helper function to avoid logic duplication between the disable and enable phases. Cc: stable@vger.kernel.org Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Link: https://patch.msgid.link/20260224023544.1250787-1-hehuiwen@kylinos.cn Fixes:ce1039bd3a("tracing: Fix enabling of syscall events on the command line") Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
6f2d498af9
commit
22a97e295e
+36
-15
@@ -3393,27 +3393,23 @@ static __init int event_trace_memsetup(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __init void
|
||||
early_enable_events(struct trace_array *tr, bool disable_first)
|
||||
/*
|
||||
* Helper function to enable or disable a comma-separated list of events
|
||||
* from the bootup buffer.
|
||||
*/
|
||||
static __init void __early_set_events(struct trace_array *tr, bool enable)
|
||||
{
|
||||
char *buf = bootup_event_buf;
|
||||
char *token;
|
||||
int ret;
|
||||
|
||||
while (true) {
|
||||
token = strsep(&buf, ",");
|
||||
|
||||
if (!token)
|
||||
break;
|
||||
|
||||
while ((token = strsep(&buf, ","))) {
|
||||
if (*token) {
|
||||
/* Restarting syscalls requires that we stop them first */
|
||||
if (disable_first)
|
||||
if (enable) {
|
||||
if (ftrace_set_clr_event(tr, token, 1))
|
||||
pr_warn("Failed to enable trace event: %s\n", token);
|
||||
} else {
|
||||
ftrace_set_clr_event(tr, token, 0);
|
||||
|
||||
ret = ftrace_set_clr_event(tr, token, 1);
|
||||
if (ret)
|
||||
pr_warn("Failed to enable trace event: %s\n", token);
|
||||
}
|
||||
}
|
||||
|
||||
/* Put back the comma to allow this to be called again */
|
||||
@@ -3422,6 +3418,31 @@ early_enable_events(struct trace_array *tr, bool disable_first)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* early_enable_events - enable events from the bootup buffer
|
||||
* @tr: The trace array to enable the events in
|
||||
* @disable_first: If true, disable all events before enabling them
|
||||
*
|
||||
* This function enables events from the bootup buffer. If @disable_first
|
||||
* is true, it will first disable all events in the buffer before enabling
|
||||
* them.
|
||||
*
|
||||
* For syscall events, which rely on a global refcount to register the
|
||||
* SYSCALL_WORK_SYSCALL_TRACEPOINT flag (especially for pid 1), we must
|
||||
* ensure the refcount hits zero before re-enabling them. A simple
|
||||
* "disable then enable" per-event is not enough if multiple syscalls are
|
||||
* used, as the refcount will stay above zero. Thus, we need a two-phase
|
||||
* approach: disable all, then enable all.
|
||||
*/
|
||||
static __init void
|
||||
early_enable_events(struct trace_array *tr, bool disable_first)
|
||||
{
|
||||
if (disable_first)
|
||||
__early_set_events(tr, false);
|
||||
|
||||
__early_set_events(tr, true);
|
||||
}
|
||||
|
||||
static __init int event_trace_enable(void)
|
||||
{
|
||||
struct trace_array *tr = top_trace_array();
|
||||
|
||||
Reference in New Issue
Block a user