* [PATCH v4 0/2] tracing: Preserve repeated boot-time parameters and drain deferred trigger frees
@ 2026-03-24 22:13 Wesley Atwell
2026-03-24 22:13 ` [PATCH v4 1/2] tracing: Preserve repeated boot-time tracing parameters Wesley Atwell
2026-03-24 22:13 ` [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails Wesley Atwell
0 siblings, 2 replies; 10+ messages in thread
From: Wesley Atwell @ 2026-03-24 22:13 UTC (permalink / raw)
To: linux-trace-kernel
Cc: linux-kernel, rostedt, mhiramat, mark.rutland, mathieu.desnoyers,
tom.zanussi, Wesley Atwell
Patch 1 preserves repeated boot-time tracing parameters by appending to
the existing parser buffers instead of overwriting earlier values, so
repeated command-line entries and Bootconfig array expansions work with
the delimited formats the parsers already consume.
Patch 2 fixes the deferred event-trigger free path so queued frees are
still drained if the cleanup kthread never comes up after boot.
v4:
- drop the kernel-parameters.txt update from patch 1
- move trace_append_boot_param() into trace.c and keep only the
prototype in trace.h
- capitalize the tracing patch subjects
- rename trigger_start_kthread_locked() to trigger_create_kthread_locked()
- change the failure comment to say "creation failed"
Wesley Atwell (2):
tracing: Preserve repeated boot-time tracing parameters
tracing: Drain deferred trigger frees if kthread creation fails
kernel/trace/ftrace.c | 12 +++--
kernel/trace/trace.c | 31 ++++++++++-
kernel/trace/trace.h | 2 +
kernel/trace/trace_events.c | 26 ++++++++--
kernel/trace/trace_events_trigger.c | 79 ++++++++++++++++++++++++-----
kernel/trace/trace_kprobe.c | 3 +-
6 files changed, 131 insertions(+), 22 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4 1/2] tracing: Preserve repeated boot-time tracing parameters
2026-03-24 22:13 [PATCH v4 0/2] tracing: Preserve repeated boot-time parameters and drain deferred trigger frees Wesley Atwell
@ 2026-03-24 22:13 ` Wesley Atwell
2026-03-28 17:53 ` Steven Rostedt
2026-03-28 20:18 ` [PATCH v5] " Wesley Atwell
2026-03-24 22:13 ` [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails Wesley Atwell
1 sibling, 2 replies; 10+ messages in thread
From: Wesley Atwell @ 2026-03-24 22:13 UTC (permalink / raw)
To: linux-trace-kernel
Cc: linux-kernel, rostedt, mhiramat, mark.rutland, mathieu.desnoyers,
tom.zanussi, Wesley Atwell
Some tracing boot parameters already accept delimited value lists, but
their __setup() handlers keep only the last instance seen at boot.
Make repeated instances append to the same boot-time buffer in the
format each parser already consumes.
Use a shared trace_append_boot_param() helper for the ftrace filters,
trace_options, and kprobe_event boot parameters. trace_trigger=
tokenizes its backing storage in place, so keep a running offset and
only parse the newly appended chunk into bootup_triggers[].
This also lets Bootconfig array values work naturally when they expand
to repeated param=value entries.
Signed-off-by: Wesley Atwell <atwellwea@gmail.com>
---
kernel/trace/ftrace.c | 12 ++++++++----
kernel/trace/trace.c | 31 ++++++++++++++++++++++++++++++-
kernel/trace/trace.h | 2 ++
kernel/trace/trace_events.c | 26 +++++++++++++++++++++++---
kernel/trace/trace_kprobe.c | 3 ++-
5 files changed, 65 insertions(+), 9 deletions(-)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 413310912609..8bd3dd1d549c 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -6841,7 +6841,8 @@ bool ftrace_filter_param __initdata;
static int __init set_ftrace_notrace(char *str)
{
ftrace_filter_param = true;
- strscpy(ftrace_notrace_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_notrace_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_notrace=", set_ftrace_notrace);
@@ -6849,7 +6850,8 @@ __setup("ftrace_notrace=", set_ftrace_notrace);
static int __init set_ftrace_filter(char *str)
{
ftrace_filter_param = true;
- strscpy(ftrace_filter_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_filter_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_filter=", set_ftrace_filter);
@@ -6861,14 +6863,16 @@ static int ftrace_graph_set_hash(struct ftrace_hash *hash, char *buffer);
static int __init set_graph_function(char *str)
{
- strscpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_graph_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_graph_filter=", set_graph_function);
static int __init set_graph_notrace_function(char *str)
{
- strscpy(ftrace_graph_notrace_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_graph_notrace_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_graph_notrace=", set_graph_notrace_function);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index a626211ceb9a..1b1f6f9035c6 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -228,6 +228,34 @@ static int boot_instance_index;
static char boot_snapshot_info[COMMAND_LINE_SIZE] __initdata;
static int boot_snapshot_index;
+/*
+ * Repeated boot parameters, including Bootconfig array expansions, need
+ * to stay in the delimiter form that the existing parser consumes.
+ */
+void __init trace_append_boot_param(char *buf, const char *str, char sep,
+ size_t size)
+{
+ size_t len, str_len;
+
+ if (!buf[0]) {
+ strscpy(buf, str, size);
+ return;
+ }
+
+ str_len = strlen(str);
+ if (!str_len)
+ return;
+
+ len = strlen(buf);
+ if (len >= size - 1)
+ return;
+ if (str_len >= size - len - 1)
+ return;
+
+ buf[len] = sep;
+ strscpy(buf + len + 1, str, size - len - 1);
+}
+
static int __init set_cmdline_ftrace(char *str)
{
strscpy(bootup_tracer_buf, str, MAX_TRACER_SIZE);
@@ -329,7 +357,8 @@ static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata;
static int __init set_trace_boot_options(char *str)
{
- strscpy(trace_boot_options_buf, str, MAX_TRACER_SIZE);
+ trace_append_boot_param(trace_boot_options_buf, str, ',',
+ MAX_TRACER_SIZE);
return 1;
}
__setup("trace_options=", set_trace_boot_options);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index b8f3804586a0..64ce179d12dd 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -863,6 +863,8 @@ extern int DYN_FTRACE_TEST_NAME(void);
extern int DYN_FTRACE_TEST_NAME2(void);
extern void trace_set_ring_buffer_expanded(struct trace_array *tr);
+void __init trace_append_boot_param(char *buf, const char *str,
+ char sep, size_t size);
extern bool tracing_selftest_disabled;
#ifdef CONFIG_FTRACE_STARTUP_TEST
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 249d1cba72c0..5f72be33f2d1 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -3679,20 +3679,40 @@ static struct boot_triggers {
} bootup_triggers[MAX_BOOT_TRIGGERS];
static char bootup_trigger_buf[COMMAND_LINE_SIZE];
+static size_t bootup_trigger_buf_len;
static int nr_boot_triggers;
static __init int setup_trace_triggers(char *str)
{
char *trigger;
char *buf;
+ size_t start, str_len;
int i;
- strscpy(bootup_trigger_buf, str, COMMAND_LINE_SIZE);
+ if (bootup_trigger_buf_len >= COMMAND_LINE_SIZE)
+ return 1;
+
+ start = bootup_trigger_buf_len;
+ if (start && !*str)
+ return 1;
+
+ str_len = strlen(str);
+ if (start && str_len >= COMMAND_LINE_SIZE - start)
+ return 1;
+
+ /*
+ * trace_trigger= parsing tokenizes the backing storage in place.
+ * Copy each repeated parameter into fresh space and only parse that
+ * newly copied chunk here.
+ */
+ trace_append_boot_param(bootup_trigger_buf + start, str, '\0',
+ COMMAND_LINE_SIZE - start);
+ bootup_trigger_buf_len += strlen(bootup_trigger_buf + start) + 1;
trace_set_ring_buffer_expanded(NULL);
disable_tracing_selftest("running event triggers");
- buf = bootup_trigger_buf;
- for (i = 0; i < MAX_BOOT_TRIGGERS; i++) {
+ buf = bootup_trigger_buf + start;
+ for (i = nr_boot_triggers; i < MAX_BOOT_TRIGGERS; i++) {
trigger = strsep(&buf, ",");
if (!trigger)
break;
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index a5dbb72528e0..e9f1c55aea64 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -31,7 +31,8 @@ static char kprobe_boot_events_buf[COMMAND_LINE_SIZE] __initdata;
static int __init set_kprobe_boot_events(char *str)
{
- strscpy(kprobe_boot_events_buf, str, COMMAND_LINE_SIZE);
+ trace_append_boot_param(kprobe_boot_events_buf, str, ';',
+ COMMAND_LINE_SIZE);
disable_tracing_selftest("running kprobe events");
return 1;
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails
2026-03-24 22:13 [PATCH v4 0/2] tracing: Preserve repeated boot-time parameters and drain deferred trigger frees Wesley Atwell
2026-03-24 22:13 ` [PATCH v4 1/2] tracing: Preserve repeated boot-time tracing parameters Wesley Atwell
@ 2026-03-24 22:13 ` Wesley Atwell
2026-03-27 19:06 ` Steven Rostedt
1 sibling, 1 reply; 10+ messages in thread
From: Wesley Atwell @ 2026-03-24 22:13 UTC (permalink / raw)
To: linux-trace-kernel
Cc: linux-kernel, rostedt, mhiramat, mark.rutland, mathieu.desnoyers,
tom.zanussi, Wesley Atwell
Boot-time trigger registration can fail before the trigger-data cleanup
kthread exists. Deferring those frees until late init is fine, but the
post-boot fallback must still drain the deferred list if kthread
creation never succeeds.
Otherwise, boot-deferred nodes can accumulate on
trigger_data_free_list, later frees fall back to synchronously freeing
only the current object, and the older queued entries are leaked
forever.
Keep the deferred boot-time behavior, but when kthread creation fails,
drain the whole queued list synchronously. Do the same in the late-init
drain path so queued entries are not stranded there either.
Fixes: 61d445af0a7c ("tracing: Add bulk garbage collection of freeing event_trigger_data")
Signed-off-by: Wesley Atwell <atwellwea@gmail.com>
---
kernel/trace/trace_events_trigger.c | 79 ++++++++++++++++++++++++-----
1 file changed, 66 insertions(+), 13 deletions(-)
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index d5230b759a2d..655db2e82513 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -22,6 +22,39 @@ static struct task_struct *trigger_kthread;
static struct llist_head trigger_data_free_list;
static DEFINE_MUTEX(trigger_data_kthread_mutex);
+static int trigger_kthread_fn(void *ignore);
+
+static void trigger_create_kthread_locked(void)
+{
+ lockdep_assert_held(&trigger_data_kthread_mutex);
+
+ if (!trigger_kthread) {
+ struct task_struct *kthread;
+
+ kthread = kthread_create(trigger_kthread_fn, NULL,
+ "trigger_data_free");
+ if (!IS_ERR(kthread))
+ WRITE_ONCE(trigger_kthread, kthread);
+ }
+}
+
+static void trigger_data_free_queued_locked(void)
+{
+ struct event_trigger_data *data, *tmp;
+ struct llist_node *llnodes;
+
+ lockdep_assert_held(&trigger_data_kthread_mutex);
+
+ llnodes = llist_del_all(&trigger_data_free_list);
+ if (!llnodes)
+ return;
+
+ tracepoint_synchronize_unregister();
+
+ llist_for_each_entry_safe(data, tmp, llnodes, llist)
+ kfree(data);
+}
+
/* Bulk garbage collection of event_trigger_data elements */
static int trigger_kthread_fn(void *ignore)
{
@@ -56,30 +89,50 @@ void trigger_data_free(struct event_trigger_data *data)
if (data->cmd_ops->set_filter)
data->cmd_ops->set_filter(NULL, data, NULL);
+ /*
+ * Boot-time trigger registration can fail before kthread creation
+ * works. Keep the deferred-free semantics during boot and let late
+ * init start the kthread to drain the list.
+ */
+ if (system_state == SYSTEM_BOOTING && !trigger_kthread) {
+ llist_add(&data->llist, &trigger_data_free_list);
+ return;
+ }
+
if (unlikely(!trigger_kthread)) {
guard(mutex)(&trigger_data_kthread_mutex);
+
+ trigger_create_kthread_locked();
/* Check again after taking mutex */
if (!trigger_kthread) {
- struct task_struct *kthread;
-
- kthread = kthread_create(trigger_kthread_fn, NULL,
- "trigger_data_free");
- if (!IS_ERR(kthread))
- WRITE_ONCE(trigger_kthread, kthread);
+ llist_add(&data->llist, &trigger_data_free_list);
+ /* Drain the queued frees synchronously if creation failed. */
+ trigger_data_free_queued_locked();
+ return;
}
}
- if (!trigger_kthread) {
- /* Do it the slow way */
- tracepoint_synchronize_unregister();
- kfree(data);
- return;
- }
-
llist_add(&data->llist, &trigger_data_free_list);
wake_up_process(trigger_kthread);
}
+static int __init trigger_data_free_init(void)
+{
+ guard(mutex)(&trigger_data_kthread_mutex);
+
+ if (llist_empty(&trigger_data_free_list))
+ return 0;
+
+ trigger_create_kthread_locked();
+ if (trigger_kthread)
+ wake_up_process(trigger_kthread);
+ else
+ trigger_data_free_queued_locked();
+
+ return 0;
+}
+late_initcall(trigger_data_free_init);
+
static inline void data_ops_trigger(struct event_trigger_data *data,
struct trace_buffer *buffer, void *rec,
struct ring_buffer_event *event)
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails
2026-03-24 22:13 ` [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails Wesley Atwell
@ 2026-03-27 19:06 ` Steven Rostedt
2026-03-27 22:41 ` Wesley Atwell
0 siblings, 1 reply; 10+ messages in thread
From: Steven Rostedt @ 2026-03-27 19:06 UTC (permalink / raw)
To: Wesley Atwell
Cc: linux-trace-kernel, linux-kernel, mhiramat, mark.rutland,
mathieu.desnoyers, tom.zanussi
On Tue, 24 Mar 2026 16:13:26 -0600
Wesley Atwell <atwellwea@gmail.com> wrote:
> Boot-time trigger registration can fail before the trigger-data cleanup
> kthread exists. Deferring those frees until late init is fine, but the
> post-boot fallback must still drain the deferred list if kthread
> creation never succeeds.
>
> Otherwise, boot-deferred nodes can accumulate on
> trigger_data_free_list, later frees fall back to synchronously freeing
> only the current object, and the older queued entries are leaked
> forever.
>
> Keep the deferred boot-time behavior, but when kthread creation fails,
> drain the whole queued list synchronously. Do the same in the late-init
> drain path so queued entries are not stranded there either.
>
> Fixes: 61d445af0a7c ("tracing: Add bulk garbage collection of freeing event_trigger_data")
> Signed-off-by: Wesley Atwell <atwellwea@gmail.com>
> ---
Do you have a test case (kernel command line) that will make
trigger_data_free() get called at boot up?
-- Steve
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails
2026-03-27 19:06 ` Steven Rostedt
@ 2026-03-27 22:41 ` Wesley Atwell
2026-03-28 2:30 ` Steven Rostedt
0 siblings, 1 reply; 10+ messages in thread
From: Wesley Atwell @ 2026-03-27 22:41 UTC (permalink / raw)
To: Steven Rostedt
Cc: linux-trace-kernel, linux-kernel, mhiramat, mark.rutland,
mathieu.desnoyers, tom.zanussi
Yes,
This kernel command line reliably reaches trigger_data_free() during boot:
trace_event=sched:sched_switch
trace_trigger=sched_switch.traceon,sched_switch.traceon
On an unpatched tree, that crashes during early boot before userspace.
The call trace goes through:
trigger_data_free()
__kthread_create_on_node()
try_to_wake_up()
The stack also shows the boot-time trigger registration path:
event_trigger_parse()
trigger_process_regex()
__trace_early_add_events()
With v4 applied, the same command line boots successfully. The guest log shows:
Failed to register trigger 'traceon' on event sched_switch
And /sys/kernel/tracing/events/sched/sched_switch/trigger contains:
traceon:unlimited
I also verified patch 1 with repeated trace_trigger= parameters:
before the patch, only the last parameter was preserved; after the
patch, both triggers were installed.
Thanks,
Wesley Atwell
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails
2026-03-27 22:41 ` Wesley Atwell
@ 2026-03-28 2:30 ` Steven Rostedt
2026-03-28 4:56 ` Wesley Atwell
0 siblings, 1 reply; 10+ messages in thread
From: Steven Rostedt @ 2026-03-28 2:30 UTC (permalink / raw)
To: Wesley Atwell
Cc: linux-trace-kernel, linux-kernel, mhiramat, mark.rutland,
mathieu.desnoyers, tom.zanussi
On Fri, 27 Mar 2026 16:41:52 -0600
Wesley Atwell <atwellwea@gmail.com> wrote:
> Yes,
>
> This kernel command line reliably reaches trigger_data_free() during boot:
>
> trace_event=sched:sched_switch
> trace_trigger=sched_switch.traceon,sched_switch.traceon
>
> On an unpatched tree, that crashes during early boot before userspace.
> The call trace goes through:
>
> trigger_data_free()
> __kthread_create_on_node()
> try_to_wake_up()
>
> The stack also shows the boot-time trigger registration path:
>
> event_trigger_parse()
> trigger_process_regex()
> __trace_early_add_events()
Thanks for this. I can reproduce the crash. I'm also going to add this
to the change log as it is useful (I'll even add it to one of my
regression tests). I'll take this patch separately (this didn't need to
be a patch series, as the two patches do not depend on each other).
-- Steve
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails
2026-03-28 2:30 ` Steven Rostedt
@ 2026-03-28 4:56 ` Wesley Atwell
0 siblings, 0 replies; 10+ messages in thread
From: Wesley Atwell @ 2026-03-28 4:56 UTC (permalink / raw)
To: Steven Rostedt
Cc: linux-trace-kernel, linux-kernel, mhiramat, mark.rutland,
mathieu.desnoyers, tom.zanussi
Hi Steve,
I'm glad the test case was helpful. I'll include similar testing
details in future commit messages and avoid grouping unrelated
patches.
Thanks,
Wesley Atwell
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 1/2] tracing: Preserve repeated boot-time tracing parameters
2026-03-24 22:13 ` [PATCH v4 1/2] tracing: Preserve repeated boot-time tracing parameters Wesley Atwell
@ 2026-03-28 17:53 ` Steven Rostedt
2026-03-28 20:18 ` [PATCH v5] " Wesley Atwell
1 sibling, 0 replies; 10+ messages in thread
From: Steven Rostedt @ 2026-03-28 17:53 UTC (permalink / raw)
To: Wesley Atwell
Cc: linux-trace-kernel, linux-kernel, mhiramat, mark.rutland,
mathieu.desnoyers, tom.zanussi
On Tue, 24 Mar 2026 16:13:25 -0600
Wesley Atwell <atwellwea@gmail.com> wrote:
> +++ b/kernel/trace/trace.c
> @@ -228,6 +228,34 @@ static int boot_instance_index;
> static char boot_snapshot_info[COMMAND_LINE_SIZE] __initdata;
> static int boot_snapshot_index;
>
> +/*
> + * Repeated boot parameters, including Bootconfig array expansions, need
> + * to stay in the delimiter form that the existing parser consumes.
> + */
> +void __init trace_append_boot_param(char *buf, const char *str, char sep,
> + size_t size)
> +{
> + size_t len, str_len;
Why use the "size_t" type? Just use int. Then you don't need to play games
about unsigned types in the if statements below. The boot cmdline will
never come close to being 2GB in size.
> +
> + if (!buf[0]) {
Should we check for size here? Perhaps just remove this part (see below)
> + strscpy(buf, str, size);
> + return;
> + }
> +
> + str_len = strlen(str);
> + if (!str_len)
> + return;
> +
> + len = strlen(buf);
> + if (len >= size - 1)
> + return;
> + if (str_len >= size - len - 1)
> + return;
Instead of the above, have:
/* Plus 2 for ",\0" */
if (str_len + len + 2 > size)
return;
> +
> + buf[len] = sep;
If we remove the first check, here we can have:
if (len)
buf[len++] = sep;
By adding one to length, it makes the strscpy() a bit more readable.
strscpy(buf + len, str, size - len);
> + strscpy(buf + len + 1, str, size - len - 1);
> +}
> +
-- Steve
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v5] tracing: Preserve repeated boot-time tracing parameters
2026-03-24 22:13 ` [PATCH v4 1/2] tracing: Preserve repeated boot-time tracing parameters Wesley Atwell
2026-03-28 17:53 ` Steven Rostedt
@ 2026-03-28 20:18 ` Wesley Atwell
2026-03-29 15:49 ` Steven Rostedt
1 sibling, 1 reply; 10+ messages in thread
From: Wesley Atwell @ 2026-03-28 20:18 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mark Rutland, Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
Wesley Atwell
Some tracing boot parameters already accept delimited value lists, but
their __setup() handlers keep only the last instance seen at boot.
Make repeated instances append to the same boot-time buffer in the
format each parser already consumes.
Use a shared trace_append_boot_param() helper for the ftrace filters,
trace_options, and kprobe_event boot parameters. trace_trigger=
tokenizes its backing storage in place, so keep a running offset and
only parse the newly appended chunk into bootup_triggers[].
This also lets Bootconfig array values work naturally when they expand
to repeated param=value entries.
Validated by booting with repeated ftrace_filter=, ftrace_notrace=,
ftrace_graph_filter=, ftrace_graph_notrace=, trace_options=,
kprobe_event=, and trace_trigger= parameters and confirming that the
resulting tracefs state preserved every requested entry. Before this
change, only the last instance from each repeated parameter survived
boot.
Signed-off-by: Wesley Atwell <atwellwea@gmail.com>
---
v5:
- use int sizes in the shared append helper and trace_trigger bookkeeping
- keep a single bounded append path that only inserts the separator after
the first entry
- only advance the trace_trigger buffer offset after a successful append
kernel/trace/ftrace.c | 12 ++++++++----
kernel/trace/trace.c | 29 ++++++++++++++++++++++++++++-
kernel/trace/trace.h | 2 ++
kernel/trace/trace_events.c | 23 ++++++++++++++++++++---
kernel/trace/trace_kprobe.c | 3 ++-
5 files changed, 60 insertions(+), 9 deletions(-)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 413310912609..8bd3dd1d549c 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -6841,7 +6841,8 @@ bool ftrace_filter_param __initdata;
static int __init set_ftrace_notrace(char *str)
{
ftrace_filter_param = true;
- strscpy(ftrace_notrace_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_notrace_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_notrace=", set_ftrace_notrace);
@@ -6849,7 +6850,8 @@ __setup("ftrace_notrace=", set_ftrace_notrace);
static int __init set_ftrace_filter(char *str)
{
ftrace_filter_param = true;
- strscpy(ftrace_filter_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_filter_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_filter=", set_ftrace_filter);
@@ -6861,14 +6863,16 @@ static int ftrace_graph_set_hash(struct ftrace_hash *hash, char *buffer);
static int __init set_graph_function(char *str)
{
- strscpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_graph_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_graph_filter=", set_graph_function);
static int __init set_graph_notrace_function(char *str)
{
- strscpy(ftrace_graph_notrace_buf, str, FTRACE_FILTER_SIZE);
+ trace_append_boot_param(ftrace_graph_notrace_buf, str, ',',
+ FTRACE_FILTER_SIZE);
return 1;
}
__setup("ftrace_graph_notrace=", set_graph_notrace_function);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index a626211ceb9a..c8cf45dc4152 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -228,6 +228,32 @@ static int boot_instance_index;
static char boot_snapshot_info[COMMAND_LINE_SIZE] __initdata;
static int boot_snapshot_index;
+/*
+ * Repeated boot parameters, including Bootconfig array expansions, need
+ * to stay in the delimiter form that the existing parser consumes.
+ */
+void __init trace_append_boot_param(char *buf, const char *str, char sep,
+ int size)
+{
+ int len, needed, str_len;
+
+ if (!*str)
+ return;
+
+ len = strlen(buf);
+ str_len = strlen(str);
+ needed = len + str_len + 1;
+ if (len)
+ needed++;
+ if (needed > size)
+ return;
+
+ if (len)
+ buf[len++] = sep;
+
+ strscpy(buf + len, str, size - len);
+}
+
static int __init set_cmdline_ftrace(char *str)
{
strscpy(bootup_tracer_buf, str, MAX_TRACER_SIZE);
@@ -329,7 +355,8 @@ static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata;
static int __init set_trace_boot_options(char *str)
{
- strscpy(trace_boot_options_buf, str, MAX_TRACER_SIZE);
+ trace_append_boot_param(trace_boot_options_buf, str, ',',
+ MAX_TRACER_SIZE);
return 1;
}
__setup("trace_options=", set_trace_boot_options);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index b8f3804586a0..237a0417de1c 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -863,6 +863,8 @@ extern int DYN_FTRACE_TEST_NAME(void);
extern int DYN_FTRACE_TEST_NAME2(void);
extern void trace_set_ring_buffer_expanded(struct trace_array *tr);
+void __init trace_append_boot_param(char *buf, const char *str,
+ char sep, int size);
extern bool tracing_selftest_disabled;
#ifdef CONFIG_FTRACE_STARTUP_TEST
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 249d1cba72c0..7f0bec4622f6 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -3679,20 +3679,37 @@ static struct boot_triggers {
} bootup_triggers[MAX_BOOT_TRIGGERS];
static char bootup_trigger_buf[COMMAND_LINE_SIZE];
+static int bootup_trigger_buf_len;
static int nr_boot_triggers;
static __init int setup_trace_triggers(char *str)
{
+ char *slot;
char *trigger;
char *buf;
int i;
- strscpy(bootup_trigger_buf, str, COMMAND_LINE_SIZE);
+ if (bootup_trigger_buf_len >= COMMAND_LINE_SIZE)
+ return 1;
+
+ slot = bootup_trigger_buf + bootup_trigger_buf_len;
+
+ /*
+ * trace_trigger= parsing tokenizes the backing storage in place.
+ * Copy each repeated parameter into fresh space and only parse that
+ * newly copied chunk here.
+ */
+ trace_append_boot_param(slot, str, '\0',
+ COMMAND_LINE_SIZE - bootup_trigger_buf_len);
+ if (!*slot)
+ return 1;
+
+ bootup_trigger_buf_len += strlen(slot) + 1;
trace_set_ring_buffer_expanded(NULL);
disable_tracing_selftest("running event triggers");
- buf = bootup_trigger_buf;
- for (i = 0; i < MAX_BOOT_TRIGGERS; i++) {
+ buf = slot;
+ for (i = nr_boot_triggers; i < MAX_BOOT_TRIGGERS; i++) {
trigger = strsep(&buf, ",");
if (!trigger)
break;
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index a5dbb72528e0..e9f1c55aea64 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -31,7 +31,8 @@ static char kprobe_boot_events_buf[COMMAND_LINE_SIZE] __initdata;
static int __init set_kprobe_boot_events(char *str)
{
- strscpy(kprobe_boot_events_buf, str, COMMAND_LINE_SIZE);
+ trace_append_boot_param(kprobe_boot_events_buf, str, ';',
+ COMMAND_LINE_SIZE);
disable_tracing_selftest("running kprobe events");
return 1;
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v5] tracing: Preserve repeated boot-time tracing parameters
2026-03-28 20:18 ` [PATCH v5] " Wesley Atwell
@ 2026-03-29 15:49 ` Steven Rostedt
0 siblings, 0 replies; 10+ messages in thread
From: Steven Rostedt @ 2026-03-29 15:49 UTC (permalink / raw)
To: Wesley Atwell
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel
On Sat, 28 Mar 2026 14:18:42 -0600
Wesley Atwell <atwellwea@gmail.com> wrote:
> Some tracing boot parameters already accept delimited value lists, but
> their __setup() handlers keep only the last instance seen at boot.
> Make repeated instances append to the same boot-time buffer in the
> format each parser already consumes.
>
> Use a shared trace_append_boot_param() helper for the ftrace filters,
> trace_options, and kprobe_event boot parameters. trace_trigger=
> tokenizes its backing storage in place, so keep a running offset and
> only parse the newly appended chunk into bootup_triggers[].
>
> This also lets Bootconfig array values work naturally when they expand
> to repeated param=value entries.
>
> Validated by booting with repeated ftrace_filter=, ftrace_notrace=,
> ftrace_graph_filter=, ftrace_graph_notrace=, trace_options=,
> kprobe_event=, and trace_trigger= parameters and confirming that the
> resulting tracefs state preserved every requested entry. Before this
> change, only the last instance from each repeated parameter survived
> boot.
>
> Signed-off-by: Wesley Atwell <atwellwea@gmail.com>
> ---
> v5:
FYI, it's nice to have a daisy chain connection of previous versions. I
suggest instead of just saying "v5:" use:
Changes since v4: https://lore.kernel.org/all/20260324221326.1395799-2-atwellwea@gmail.com/
> - use int sizes in the shared append helper and trace_trigger bookkeeping
> - keep a single bounded append path that only inserts the separator after
> the first entry
> - only advance the trace_trigger buffer offset after a successful append
>
> kernel/trace/ftrace.c | 12 ++++++++----
> kernel/trace/trace.c | 29 ++++++++++++++++++++++++++++-
> kernel/trace/trace.h | 2 ++
> kernel/trace/trace_events.c | 23 ++++++++++++++++++++---
> kernel/trace/trace_kprobe.c | 3 ++-
> 5 files changed, 60 insertions(+), 9 deletions(-)
>
> +/*
> + * Repeated boot parameters, including Bootconfig array expansions, need
> + * to stay in the delimiter form that the existing parser consumes.
> + */
> +void __init trace_append_boot_param(char *buf, const char *str, char sep,
> + int size)
> +{
This is much better.
> + int len, needed, str_len;
> +
> + if (!*str)
> + return;
> +
> + len = strlen(buf);
> + str_len = strlen(str);
> + needed = len + str_len + 1;
Perhaps add a comment:
/* For continuation, account for separator */
> + if (len)
> + needed++;
> + if (needed > size)
> + return;
> +
> + if (len)
> + buf[len++] = sep;
> +
> + strscpy(buf + len, str, size - len);
> +}
> +
> static int __init set_cmdline_ftrace(char *str)
> {
> strscpy(bootup_tracer_buf, str, MAX_TRACER_SIZE);
> --- a/kernel/trace/trace_events.c
> +++ b/kernel/trace/trace_events.c
> @@ -3679,20 +3679,37 @@ static struct boot_triggers {
> } bootup_triggers[MAX_BOOT_TRIGGERS];
>
> static char bootup_trigger_buf[COMMAND_LINE_SIZE];
> +static int bootup_trigger_buf_len;
> static int nr_boot_triggers;
>
> static __init int setup_trace_triggers(char *str)
> {
> + char *slot;
> char *trigger;
> char *buf;
> int i;
>
> - strscpy(bootup_trigger_buf, str, COMMAND_LINE_SIZE);
> + if (bootup_trigger_buf_len >= COMMAND_LINE_SIZE)
> + return 1;
> +
> + slot = bootup_trigger_buf + bootup_trigger_buf_len;
The bootup_trigger_buf is a temporary buffer for this function only. It
works fine as is. There's no reason to modify this function.
-- Steve
> +
> + /*
> + * trace_trigger= parsing tokenizes the backing storage in place.
> + * Copy each repeated parameter into fresh space and only parse that
> + * newly copied chunk here.
> + */
> + trace_append_boot_param(slot, str, '\0',
> + COMMAND_LINE_SIZE - bootup_trigger_buf_len);
> + if (!*slot)
> + return 1;
> +
> + bootup_trigger_buf_len += strlen(slot) + 1;
> trace_set_ring_buffer_expanded(NULL);
> disable_tracing_selftest("running event triggers");
>
> - buf = bootup_trigger_buf;
> - for (i = 0; i < MAX_BOOT_TRIGGERS; i++) {
> + buf = slot;
> + for (i = nr_boot_triggers; i < MAX_BOOT_TRIGGERS; i++) {
> trigger = strsep(&buf, ",");
> if (!trigger)
> break;
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-03-29 15:49 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-24 22:13 [PATCH v4 0/2] tracing: Preserve repeated boot-time parameters and drain deferred trigger frees Wesley Atwell
2026-03-24 22:13 ` [PATCH v4 1/2] tracing: Preserve repeated boot-time tracing parameters Wesley Atwell
2026-03-28 17:53 ` Steven Rostedt
2026-03-28 20:18 ` [PATCH v5] " Wesley Atwell
2026-03-29 15:49 ` Steven Rostedt
2026-03-24 22:13 ` [PATCH v4 2/2] tracing: Drain deferred trigger frees if kthread creation fails Wesley Atwell
2026-03-27 19:06 ` Steven Rostedt
2026-03-27 22:41 ` Wesley Atwell
2026-03-28 2:30 ` Steven Rostedt
2026-03-28 4:56 ` Wesley Atwell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox