* [PATCH] tracing: preserve module tracepoint strings
@ 2026-04-06 17:09 Cao Ruichuang
2026-04-08 20:32 ` Steven Rostedt
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Cao Ruichuang @ 2026-04-06 17:09 UTC (permalink / raw)
To: rostedt
Cc: mhiramat, mathieu.desnoyers, mcgrof, petr.pavlu, da.gomez,
samitolvanen, atomlin, linux-kernel, linux-trace-kernel,
linux-modules
tracepoint_string() is documented as exporting constant strings
through printk_formats, including when it is used from modules.
That currently does not work.
A small test module that calls
tracepoint_string("tracepoint_string_test_module_string") loads
successfully and gets a pointer back, but the string never appears
in /sys/kernel/tracing/printk_formats. The loader only collects
__trace_printk_fmt from modules and ignores __tracepoint_str.
Collect module __tracepoint_str entries too, copy them to stable
tracing-managed storage like module trace_printk formats, and let
trace_is_tracepoint_string() recognize those copied strings. This
makes module tracepoint strings visible through printk_formats and
keeps them accepted by the trace string safety checks.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217196
Signed-off-by: Cao Ruichuang <create0818@163.com>
---
include/linux/module.h | 2 ++
kernel/module/main.c | 4 +++
kernel/trace/trace_printk.c | 63 ++++++++++++++++++++++++++++---------
3 files changed, 54 insertions(+), 15 deletions(-)
diff --git a/include/linux/module.h b/include/linux/module.h
index 14f391b18..e475466a7 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -515,6 +515,8 @@ struct module {
#ifdef CONFIG_TRACING
unsigned int num_trace_bprintk_fmt;
const char **trace_bprintk_fmt_start;
+ unsigned int num_tracepoint_strings;
+ const char **tracepoint_strings_start;
#endif
#ifdef CONFIG_EVENT_TRACING
struct trace_event_call **trace_events;
diff --git a/kernel/module/main.c b/kernel/module/main.c
index c3ce106c7..d7d890138 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2672,6 +2672,10 @@ static int find_module_sections(struct module *mod, struct load_info *info)
mod->trace_bprintk_fmt_start = section_objs(info, "__trace_printk_fmt",
sizeof(*mod->trace_bprintk_fmt_start),
&mod->num_trace_bprintk_fmt);
+ mod->tracepoint_strings_start =
+ section_objs(info, "__tracepoint_str",
+ sizeof(*mod->tracepoint_strings_start),
+ &mod->num_tracepoint_strings);
#endif
#ifdef CONFIG_DYNAMIC_FTRACE
/* sechdrs[0].sh_size is always zero */
diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c
index 5ea5e0d76..9f67ce42e 100644
--- a/kernel/trace/trace_printk.c
+++ b/kernel/trace/trace_printk.c
@@ -22,8 +22,9 @@
#ifdef CONFIG_MODULES
/*
- * modules trace_printk()'s formats are autosaved in struct trace_bprintk_fmt
- * which are queued on trace_bprintk_fmt_list.
+ * modules trace_printk() formats and tracepoint_string() strings are
+ * autosaved in struct trace_bprintk_fmt, which are queued on
+ * trace_bprintk_fmt_list.
*/
static LIST_HEAD(trace_bprintk_fmt_list);
@@ -33,8 +34,12 @@ static DEFINE_MUTEX(btrace_mutex);
struct trace_bprintk_fmt {
struct list_head list;
const char *fmt;
+ unsigned int type;
};
+#define TRACE_BPRINTK_TYPE BIT(0)
+#define TRACE_TRACEPOINT_TYPE BIT(1)
+
static inline struct trace_bprintk_fmt *lookup_format(const char *fmt)
{
struct trace_bprintk_fmt *pos;
@@ -49,22 +54,24 @@ static inline struct trace_bprintk_fmt *lookup_format(const char *fmt)
return NULL;
}
-static
-void hold_module_trace_bprintk_format(const char **start, const char **end)
+static void hold_module_trace_format(const char **start, const char **end,
+ unsigned int type)
{
const char **iter;
char *fmt;
/* allocate the trace_printk per cpu buffers */
- if (start != end)
+ if ((type & TRACE_BPRINTK_TYPE) && start != end)
trace_printk_init_buffers();
mutex_lock(&btrace_mutex);
for (iter = start; iter < end; iter++) {
struct trace_bprintk_fmt *tb_fmt = lookup_format(*iter);
if (tb_fmt) {
- if (!IS_ERR(tb_fmt))
+ if (!IS_ERR(tb_fmt)) {
+ tb_fmt->type |= type;
*iter = tb_fmt->fmt;
+ }
continue;
}
@@ -76,6 +83,7 @@ void hold_module_trace_bprintk_format(const char **start, const char **end)
list_add_tail(&tb_fmt->list, &trace_bprintk_fmt_list);
strcpy(fmt, *iter);
tb_fmt->fmt = fmt;
+ tb_fmt->type = type;
} else
kfree(tb_fmt);
}
@@ -85,17 +93,28 @@ void hold_module_trace_bprintk_format(const char **start, const char **end)
mutex_unlock(&btrace_mutex);
}
-static int module_trace_bprintk_format_notify(struct notifier_block *self,
- unsigned long val, void *data)
+static int module_trace_format_notify(struct notifier_block *self,
+ unsigned long val, void *data)
{
struct module *mod = data;
+
+ if (val != MODULE_STATE_COMING)
+ return NOTIFY_OK;
+
if (mod->num_trace_bprintk_fmt) {
const char **start = mod->trace_bprintk_fmt_start;
const char **end = start + mod->num_trace_bprintk_fmt;
- if (val == MODULE_STATE_COMING)
- hold_module_trace_bprintk_format(start, end);
+ hold_module_trace_format(start, end, TRACE_BPRINTK_TYPE);
+ }
+
+ if (mod->num_tracepoint_strings) {
+ const char **start = mod->tracepoint_strings_start;
+ const char **end = start + mod->num_tracepoint_strings;
+
+ hold_module_trace_format(start, end, TRACE_TRACEPOINT_TYPE);
}
+
return NOTIFY_OK;
}
@@ -171,8 +190,8 @@ static void format_mod_stop(void)
#else /* !CONFIG_MODULES */
__init static int
-module_trace_bprintk_format_notify(struct notifier_block *self,
- unsigned long val, void *data)
+module_trace_format_notify(struct notifier_block *self,
+ unsigned long val, void *data)
{
return NOTIFY_OK;
}
@@ -193,8 +212,8 @@ void trace_printk_control(bool enabled)
}
__initdata_or_module static
-struct notifier_block module_trace_bprintk_format_nb = {
- .notifier_call = module_trace_bprintk_format_notify,
+struct notifier_block module_trace_format_nb = {
+ .notifier_call = module_trace_format_notify,
};
int __trace_bprintk(unsigned long ip, const char *fmt, ...)
@@ -254,11 +273,25 @@ EXPORT_SYMBOL_GPL(__ftrace_vprintk);
bool trace_is_tracepoint_string(const char *str)
{
const char **ptr = __start___tracepoint_str;
+#ifdef CONFIG_MODULES
+ struct trace_bprintk_fmt *tb_fmt;
+#endif
for (ptr = __start___tracepoint_str; ptr < __stop___tracepoint_str; ptr++) {
if (str == *ptr)
return true;
}
+
+#ifdef CONFIG_MODULES
+ mutex_lock(&btrace_mutex);
+ list_for_each_entry(tb_fmt, &trace_bprintk_fmt_list, list) {
+ if ((tb_fmt->type & TRACE_TRACEPOINT_TYPE) && str == tb_fmt->fmt) {
+ mutex_unlock(&btrace_mutex);
+ return true;
+ }
+ }
+ mutex_unlock(&btrace_mutex);
+#endif
return false;
}
@@ -824,7 +857,7 @@ fs_initcall(init_trace_printk_function_export);
static __init int init_trace_printk(void)
{
- return register_module_notifier(&module_trace_bprintk_format_nb);
+ return register_module_notifier(&module_trace_format_nb);
}
early_initcall(init_trace_printk);
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] tracing: preserve module tracepoint strings 2026-04-06 17:09 [PATCH] tracing: preserve module tracepoint strings Cao Ruichuang @ 2026-04-08 20:32 ` Steven Rostedt 2026-04-09 12:37 ` Petr Pavlu 2026-04-10 5:18 ` [PATCH v2] " Cao Ruichuang 2 siblings, 0 replies; 8+ messages in thread From: Steven Rostedt @ 2026-04-08 20:32 UTC (permalink / raw) To: Cao Ruichuang Cc: mhiramat, mathieu.desnoyers, mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin, linux-kernel, linux-trace-kernel, linux-modules On Tue, 7 Apr 2026 01:09:44 +0800 Cao Ruichuang <create0818@163.com> wrote: > tracepoint_string() is documented as exporting constant strings > through printk_formats, including when it is used from modules. > That currently does not work. > > A small test module that calls > tracepoint_string("tracepoint_string_test_module_string") loads > successfully and gets a pointer back, but the string never appears > in /sys/kernel/tracing/printk_formats. The loader only collects > __trace_printk_fmt from modules and ignores __tracepoint_str. > > Collect module __tracepoint_str entries too, copy them to stable > tracing-managed storage like module trace_printk formats, and let > trace_is_tracepoint_string() recognize those copied strings. This > makes module tracepoint strings visible through printk_formats and > keeps them accepted by the trace string safety checks. > > Link: https://bugzilla.kernel.org/show_bug.cgi?id=217196 > Signed-off-by: Cao Ruichuang <create0818@163.com> > --- As this is not a trivial change, and affects module code, I'm going to hold off until after v7.1-rc1 to apply it. -- Steve ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] tracing: preserve module tracepoint strings 2026-04-06 17:09 [PATCH] tracing: preserve module tracepoint strings Cao Ruichuang 2026-04-08 20:32 ` Steven Rostedt @ 2026-04-09 12:37 ` Petr Pavlu 2026-04-10 5:18 ` [PATCH v2] " Cao Ruichuang 2 siblings, 0 replies; 8+ messages in thread From: Petr Pavlu @ 2026-04-09 12:37 UTC (permalink / raw) To: Cao Ruichuang Cc: rostedt, mhiramat, mathieu.desnoyers, mcgrof, da.gomez, samitolvanen, atomlin, linux-kernel, linux-trace-kernel, linux-modules On 4/6/26 7:09 PM, Cao Ruichuang wrote: > tracepoint_string() is documented as exporting constant strings > through printk_formats, including when it is used from modules. > That currently does not work. > > A small test module that calls > tracepoint_string("tracepoint_string_test_module_string") loads > successfully and gets a pointer back, but the string never appears > in /sys/kernel/tracing/printk_formats. The loader only collects > __trace_printk_fmt from modules and ignores __tracepoint_str. > > Collect module __tracepoint_str entries too, copy them to stable > tracing-managed storage like module trace_printk formats, and let > trace_is_tracepoint_string() recognize those copied strings. This > makes module tracepoint strings visible through printk_formats and > keeps them accepted by the trace string safety checks. The documentation for tracepoint_string() in include/linux/tracepoint.h contains: * The @str used must be a constant string and persistent as it would not * make sense to show a string that no longer exists. But it is still fine * to be used with modules, because when modules are unloaded, if they * had tracepoints, the ring buffers are cleared too. As long as the string * does not change during the life of the module, it is fine to use * tracepoint_string() within a module. The implemented approach copies all strings referenced by __tracepoint_str and keeps them for the kernel's lifetime. I wonder if the code could directly reference the original data and handle its removal when the module is unloaded, or if the quoted comment should be updated to reflect the new behavior. -- Thanks, Petr ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2] tracing: preserve module tracepoint strings 2026-04-06 17:09 [PATCH] tracing: preserve module tracepoint strings Cao Ruichuang 2026-04-08 20:32 ` Steven Rostedt 2026-04-09 12:37 ` Petr Pavlu @ 2026-04-10 5:18 ` Cao Ruichuang 2026-04-13 9:40 ` Petr Pavlu 2 siblings, 1 reply; 8+ messages in thread From: Cao Ruichuang @ 2026-04-10 5:18 UTC (permalink / raw) To: rostedt Cc: mhiramat, mathieu.desnoyers, petr.pavlu, linux-kernel, linux-trace-kernel tracepoint_string() is documented as exporting constant strings through printk_formats, including when it is used from modules. That currently does not work. A small test module that calls tracepoint_string("tracepoint_string_test_module_string") loads successfully and gets a pointer back, but the string never appears in /sys/kernel/tracing/printk_formats. The loader only collects __trace_printk_fmt from modules and ignores __tracepoint_str. Collect module __tracepoint_str entries too, copy them to stable tracing-managed storage like module trace_printk formats, and let trace_is_tracepoint_string() recognize those copied strings. This makes module tracepoint strings visible through printk_formats and keeps them accepted by the trace string safety checks. Update the tracepoint_string() documentation to describe this module behavior explicitly, so the comment matches the preserved module-string mappings exported by tracing. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217196 Signed-off-by: Cao Ruichuang <create0818@163.com> --- v2: - update tracepoint_string() documentation to describe the preserved module-string mapping explicitly - address Petr Pavlu's review about the comment not matching the implemented module behavior include/linux/module.h | 2 ++ include/linux/tracepoint.h | 14 ++++++--- kernel/module/main.c | 4 +++ kernel/trace/trace_printk.c | 63 ++++++++++++++++++++++++++++--------- 4 files changed, 63 insertions(+), 20 deletions(-) diff --git a/include/linux/module.h b/include/linux/module.h index 14f391b186c..e475466a785 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -515,6 +515,8 @@ struct module { #ifdef CONFIG_TRACING unsigned int num_trace_bprintk_fmt; const char **trace_bprintk_fmt_start; + unsigned int num_tracepoint_strings; + const char **tracepoint_strings_start; #endif #ifdef CONFIG_EVENT_TRACING struct trace_event_call **trace_events; diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 1d7f29f5e90..f14da542402 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -475,11 +475,15 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) * the ASCII strings they represent. * * The @str used must be a constant string and persistent as it would not - * make sense to show a string that no longer exists. But it is still fine - * to be used with modules, because when modules are unloaded, if they - * had tracepoints, the ring buffers are cleared too. As long as the string - * does not change during the life of the module, it is fine to use - * tracepoint_string() within a module. + * make sense to show a string that no longer exists. + * + * For built-in code, the tracing system uses the original string address. + * For modules, the tracing code saves tracepoint strings into + * tracing-managed storage when the module loads, so their mappings remain + * available through printk_formats and trace string checks even after the + * module's own memory goes away. As long as the string does not change + * during the life of the module, it is fine to use tracepoint_string() + * within a module. */ #define tracepoint_string(str) \ ({ \ diff --git a/kernel/module/main.c b/kernel/module/main.c index c3ce106c70a..d7d890138ac 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -2672,6 +2672,10 @@ static int find_module_sections(struct module *mod, struct load_info *info) mod->trace_bprintk_fmt_start = section_objs(info, "__trace_printk_fmt", sizeof(*mod->trace_bprintk_fmt_start), &mod->num_trace_bprintk_fmt); + mod->tracepoint_strings_start = + section_objs(info, "__tracepoint_str", + sizeof(*mod->tracepoint_strings_start), + &mod->num_tracepoint_strings); #endif #ifdef CONFIG_DYNAMIC_FTRACE /* sechdrs[0].sh_size is always zero */ diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 5ea5e0d76f0..9f67ce42ef6 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -22,8 +22,9 @@ #ifdef CONFIG_MODULES /* - * modules trace_printk()'s formats are autosaved in struct trace_bprintk_fmt - * which are queued on trace_bprintk_fmt_list. + * modules trace_printk() formats and tracepoint_string() strings are + * autosaved in struct trace_bprintk_fmt, which are queued on + * trace_bprintk_fmt_list. */ static LIST_HEAD(trace_bprintk_fmt_list); @@ -33,8 +34,12 @@ static DEFINE_MUTEX(btrace_mutex); struct trace_bprintk_fmt { struct list_head list; const char *fmt; + unsigned int type; }; +#define TRACE_BPRINTK_TYPE BIT(0) +#define TRACE_TRACEPOINT_TYPE BIT(1) + static inline struct trace_bprintk_fmt *lookup_format(const char *fmt) { struct trace_bprintk_fmt *pos; @@ -49,22 +54,24 @@ static inline struct trace_bprintk_fmt *lookup_format(const char *fmt) return NULL; } -static -void hold_module_trace_bprintk_format(const char **start, const char **end) +static void hold_module_trace_format(const char **start, const char **end, + unsigned int type) { const char **iter; char *fmt; /* allocate the trace_printk per cpu buffers */ - if (start != end) + if ((type & TRACE_BPRINTK_TYPE) && start != end) trace_printk_init_buffers(); mutex_lock(&btrace_mutex); for (iter = start; iter < end; iter++) { struct trace_bprintk_fmt *tb_fmt = lookup_format(*iter); if (tb_fmt) { - if (!IS_ERR(tb_fmt)) + if (!IS_ERR(tb_fmt)) { + tb_fmt->type |= type; *iter = tb_fmt->fmt; + } continue; } @@ -76,6 +83,7 @@ void hold_module_trace_bprintk_format(const char **start, const char **end) list_add_tail(&tb_fmt->list, &trace_bprintk_fmt_list); strcpy(fmt, *iter); tb_fmt->fmt = fmt; + tb_fmt->type = type; } else kfree(tb_fmt); } @@ -85,17 +93,28 @@ void hold_module_trace_bprintk_format(const char **start, const char **end) mutex_unlock(&btrace_mutex); } -static int module_trace_bprintk_format_notify(struct notifier_block *self, - unsigned long val, void *data) +static int module_trace_format_notify(struct notifier_block *self, + unsigned long val, void *data) { struct module *mod = data; + + if (val != MODULE_STATE_COMING) + return NOTIFY_OK; + if (mod->num_trace_bprintk_fmt) { const char **start = mod->trace_bprintk_fmt_start; const char **end = start + mod->num_trace_bprintk_fmt; - if (val == MODULE_STATE_COMING) - hold_module_trace_bprintk_format(start, end); + hold_module_trace_format(start, end, TRACE_BPRINTK_TYPE); + } + + if (mod->num_tracepoint_strings) { + const char **start = mod->tracepoint_strings_start; + const char **end = start + mod->num_tracepoint_strings; + + hold_module_trace_format(start, end, TRACE_TRACEPOINT_TYPE); } + return NOTIFY_OK; } @@ -171,8 +190,8 @@ static void format_mod_stop(void) #else /* !CONFIG_MODULES */ __init static int -module_trace_bprintk_format_notify(struct notifier_block *self, - unsigned long val, void *data) +module_trace_format_notify(struct notifier_block *self, + unsigned long val, void *data) { return NOTIFY_OK; } @@ -193,8 +212,8 @@ void trace_printk_control(bool enabled) } __initdata_or_module static -struct notifier_block module_trace_bprintk_format_nb = { - .notifier_call = module_trace_bprintk_format_notify, +struct notifier_block module_trace_format_nb = { + .notifier_call = module_trace_format_notify, }; int __trace_bprintk(unsigned long ip, const char *fmt, ...) @@ -254,11 +273,25 @@ EXPORT_SYMBOL_GPL(__ftrace_vprintk); bool trace_is_tracepoint_string(const char *str) { const char **ptr = __start___tracepoint_str; +#ifdef CONFIG_MODULES + struct trace_bprintk_fmt *tb_fmt; +#endif for (ptr = __start___tracepoint_str; ptr < __stop___tracepoint_str; ptr++) { if (str == *ptr) return true; } + +#ifdef CONFIG_MODULES + mutex_lock(&btrace_mutex); + list_for_each_entry(tb_fmt, &trace_bprintk_fmt_list, list) { + if ((tb_fmt->type & TRACE_TRACEPOINT_TYPE) && str == tb_fmt->fmt) { + mutex_unlock(&btrace_mutex); + return true; + } + } + mutex_unlock(&btrace_mutex); +#endif return false; } @@ -824,7 +857,7 @@ fs_initcall(init_trace_printk_function_export); static __init int init_trace_printk(void) { - return register_module_notifier(&module_trace_bprintk_format_nb); + return register_module_notifier(&module_trace_format_nb); } early_initcall(init_trace_printk); -- 2.39.5 (Apple Git-154) ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2] tracing: preserve module tracepoint strings 2026-04-10 5:18 ` [PATCH v2] " Cao Ruichuang @ 2026-04-13 9:40 ` Petr Pavlu 2026-04-13 12:33 ` [PATCH] tracing: separate module tracepoint strings from trace_printk formats Cao Ruichuang 0 siblings, 1 reply; 8+ messages in thread From: Petr Pavlu @ 2026-04-13 9:40 UTC (permalink / raw) To: Cao Ruichuang Cc: rostedt, mhiramat, mathieu.desnoyers, linux-kernel, linux-trace-kernel On 4/10/26 7:18 AM, Cao Ruichuang wrote: > tracepoint_string() is documented as exporting constant strings > through printk_formats, including when it is used from modules. > That currently does not work. > > A small test module that calls > tracepoint_string("tracepoint_string_test_module_string") loads > successfully and gets a pointer back, but the string never appears > in /sys/kernel/tracing/printk_formats. The loader only collects > __trace_printk_fmt from modules and ignores __tracepoint_str. > > Collect module __tracepoint_str entries too, copy them to stable > tracing-managed storage like module trace_printk formats, and let > trace_is_tracepoint_string() recognize those copied strings. This > makes module tracepoint strings visible through printk_formats and > keeps them accepted by the trace string safety checks. > > Update the tracepoint_string() documentation to describe this > module behavior explicitly, so the comment matches the preserved > module-string mappings exported by tracing. > > Link: https://bugzilla.kernel.org/show_bug.cgi?id=217196 > Signed-off-by: Cao Ruichuang <create0818@163.com> > --- > v2: > - update tracepoint_string() documentation to describe the preserved > module-string mapping explicitly > - address Petr Pavlu's review about the comment not matching the > implemented module behavior I questioned in my previous comment whether the data associated with tracepoint_string() could be dropped when the module that created it is unloaded. Typically, modules should not leave any data behind when they are removed. Note how kernel/trace/trace_events.c tracks event fields using add_str_to_module() and releases them in trace_module_remove_events(). In practice, I suppose this isn't a large problem because the usage of tracepoint_string() is limited and one won't typically load/unload different modules that use this facility. Nonetheless, what is the reason for keeping the tracepoint_string() data for unloaded modules? -- Thanks, Petr ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] tracing: separate module tracepoint strings from trace_printk formats 2026-04-13 9:40 ` Petr Pavlu @ 2026-04-13 12:33 ` Cao Ruichuang 2026-04-14 11:37 ` Petr Pavlu 0 siblings, 1 reply; 8+ messages in thread From: Cao Ruichuang @ 2026-04-13 12:33 UTC (permalink / raw) To: petr.pavlu; +Cc: linux-trace-kernel, linux-kernel, mhiramat, Cao Ruichuang The previous module tracepoint_string() fix took the smallest implementation path and reused the existing module trace_printk format storage. That was enough to make module __tracepoint_str entries show up in printk_formats and be accepted by trace_is_tracepoint_string(), but it also made those copied mappings persist after module unload. That does not match the expected module lifetime semantics. Handle module tracepoint_string() mappings separately instead of mixing them into the module trace_printk format list. Keep copying the strings into tracing-managed storage while the module is loaded, but track them on their own list and drop them again on MODULE_STATE_GOING. Keep module trace_printk format handling unchanged. This split is intentional: module trace_printk formats and module tracepoint_string() mappings do not have the same lifetime requirements. Keeping them in one shared structure would either preserve tracepoint_string() mappings too long again, or require mixed ownership/refcount rules in a trace_printk-oriented structure. The separate module tracepoint_string() list intentionally keeps one copied mapping per module entry instead of trying to share copies across modules by string contents. printk_formats is address-based, and sharing those copies would add another layer of shared ownership/refcounting without changing the lifetime rule this fix is trying to restore. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217196 Signed-off-by: Cao Ruichuang <create0818@163.com> --- include/linux/tracepoint.h | 9 +- kernel/trace/trace_printk.c | 250 ++++++++++++++++++++++-------------- 2 files changed, 157 insertions(+), 102 deletions(-) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index f14da542402..aec598a4017 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -479,11 +479,10 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) * * For built-in code, the tracing system uses the original string address. * For modules, the tracing code saves tracepoint strings into - * tracing-managed storage when the module loads, so their mappings remain - * available through printk_formats and trace string checks even after the - * module's own memory goes away. As long as the string does not change - * during the life of the module, it is fine to use tracepoint_string() - * within a module. + * tracing-managed storage while the module is loaded, and drops those + * mappings again when the module unloads. As long as the string does not + * change during the life of the module, it is fine to use + * tracepoint_string() within a module. */ #define tracepoint_string(str) \ ({ \ diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 9f67ce42ef6..0420ffcff93 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -21,24 +21,24 @@ #ifdef CONFIG_MODULES -/* - * modules trace_printk() formats and tracepoint_string() strings are - * autosaved in struct trace_bprintk_fmt, which are queued on - * trace_bprintk_fmt_list. - */ +/* module trace_printk() formats are autosaved on trace_bprintk_fmt_list. */ static LIST_HEAD(trace_bprintk_fmt_list); +/* module tracepoint_string() copies live on tracepoint_string_list. */ +static LIST_HEAD(tracepoint_string_list); -/* serialize accesses to trace_bprintk_fmt_list */ +/* serialize accesses to module format and tracepoint-string lists */ static DEFINE_MUTEX(btrace_mutex); struct trace_bprintk_fmt { struct list_head list; const char *fmt; - unsigned int type; }; -#define TRACE_BPRINTK_TYPE BIT(0) -#define TRACE_TRACEPOINT_TYPE BIT(1) +struct tracepoint_string_entry { + struct list_head list; + struct module *mod; + const char *str; +}; static inline struct trace_bprintk_fmt *lookup_format(const char *fmt) { @@ -54,24 +54,21 @@ static inline struct trace_bprintk_fmt *lookup_format(const char *fmt) return NULL; } -static void hold_module_trace_format(const char **start, const char **end, - unsigned int type) +static void hold_module_trace_bprintk_format(const char **start, const char **end) { const char **iter; char *fmt; /* allocate the trace_printk per cpu buffers */ - if ((type & TRACE_BPRINTK_TYPE) && start != end) + if (start != end) trace_printk_init_buffers(); mutex_lock(&btrace_mutex); for (iter = start; iter < end; iter++) { struct trace_bprintk_fmt *tb_fmt = lookup_format(*iter); if (tb_fmt) { - if (!IS_ERR(tb_fmt)) { - tb_fmt->type |= type; + if (!IS_ERR(tb_fmt)) *iter = tb_fmt->fmt; - } continue; } @@ -83,7 +80,6 @@ static void hold_module_trace_format(const char **start, const char **end, list_add_tail(&tb_fmt->list, &trace_bprintk_fmt_list); strcpy(fmt, *iter); tb_fmt->fmt = fmt; - tb_fmt->type = type; } else kfree(tb_fmt); } @@ -93,89 +89,156 @@ static void hold_module_trace_format(const char **start, const char **end, mutex_unlock(&btrace_mutex); } +static void hold_module_tracepoint_strings(struct module *mod, + const char **start, + const char **end) +{ + const char **iter; + + mutex_lock(&btrace_mutex); + for (iter = start; iter < end; iter++) { + struct tracepoint_string_entry *tp_entry; + char *str; + + tp_entry = kmalloc_obj(*tp_entry); + if (!tp_entry) + continue; + + str = kstrdup(*iter, GFP_KERNEL); + if (!str) { + kfree(tp_entry); + continue; + } + + tp_entry->mod = mod; + tp_entry->str = str; + list_add_tail(&tp_entry->list, &tracepoint_string_list); + *iter = tp_entry->str; + } + mutex_unlock(&btrace_mutex); +} + +static void release_module_tracepoint_strings(struct module *mod) +{ + struct tracepoint_string_entry *tp_entry, *n; + + mutex_lock(&btrace_mutex); + list_for_each_entry_safe(tp_entry, n, &tracepoint_string_list, list) { + if (tp_entry->mod != mod) + continue; + list_del(&tp_entry->list); + kfree(tp_entry->str); + kfree(tp_entry); + } + mutex_unlock(&btrace_mutex); +} + static int module_trace_format_notify(struct notifier_block *self, unsigned long val, void *data) { struct module *mod = data; - if (val != MODULE_STATE_COMING) - return NOTIFY_OK; + switch (val) { + case MODULE_STATE_COMING: + if (mod->num_trace_bprintk_fmt) { + const char **start = mod->trace_bprintk_fmt_start; + const char **end = start + mod->num_trace_bprintk_fmt; - if (mod->num_trace_bprintk_fmt) { - const char **start = mod->trace_bprintk_fmt_start; - const char **end = start + mod->num_trace_bprintk_fmt; + hold_module_trace_bprintk_format(start, end); + } + + if (mod->num_tracepoint_strings) { + const char **start = mod->tracepoint_strings_start; + const char **end = start + mod->num_tracepoint_strings; - hold_module_trace_format(start, end, TRACE_BPRINTK_TYPE); + hold_module_tracepoint_strings(mod, start, end); + } + break; + case MODULE_STATE_GOING: + release_module_tracepoint_strings(mod); + break; } - if (mod->num_tracepoint_strings) { - const char **start = mod->tracepoint_strings_start; - const char **end = start + mod->num_tracepoint_strings; + return NOTIFY_OK; +} - hold_module_trace_format(start, end, TRACE_TRACEPOINT_TYPE); +static const char **find_first_mod_entry(void) +{ + struct trace_bprintk_fmt *tb_fmt; + struct tracepoint_string_entry *tp_entry; + + if (!list_empty(&trace_bprintk_fmt_list)) { + tb_fmt = list_first_entry(&trace_bprintk_fmt_list, + typeof(*tb_fmt), list); + return &tb_fmt->fmt; } - return NOTIFY_OK; + if (!list_empty(&tracepoint_string_list)) { + tp_entry = list_first_entry(&tracepoint_string_list, + typeof(*tp_entry), list); + return &tp_entry->str; + } + + return NULL; } -/* - * The debugfs/tracing/printk_formats file maps the addresses with - * the ASCII formats that are used in the bprintk events in the - * buffer. For userspace tools to be able to decode the events from - * the buffer, they need to be able to map the address with the format. - * - * The addresses of the bprintk formats are in their own section - * __trace_printk_fmt. But for modules we copy them into a link list. - * The code to print the formats and their addresses passes around the - * address of the fmt string. If the fmt address passed into the seq - * functions is within the kernel core __trace_printk_fmt section, then - * it simply uses the next pointer in the list. - * - * When the fmt pointer is outside the kernel core __trace_printk_fmt - * section, then we need to read the link list pointers. The trick is - * we pass the address of the string to the seq function just like - * we do for the kernel core formats. To get back the structure that - * holds the format, we simply use container_of() and then go to the - * next format in the list. - */ -static const char ** -find_next_mod_format(int start_index, void *v, const char **fmt, loff_t *pos) +static struct trace_bprintk_fmt *lookup_mod_format_ptr(const char **fmt_ptr) { - struct trace_bprintk_fmt *mod_fmt; + struct trace_bprintk_fmt *tb_fmt; - if (list_empty(&trace_bprintk_fmt_list)) - return NULL; + list_for_each_entry(tb_fmt, &trace_bprintk_fmt_list, list) { + if (fmt_ptr == &tb_fmt->fmt) + return tb_fmt; + } - /* - * v will point to the address of the fmt record from t_next - * v will be NULL from t_start. - * If this is the first pointer or called from start - * then we need to walk the list. - */ - if (!v || start_index == *pos) { - struct trace_bprintk_fmt *p; - - /* search the module list */ - list_for_each_entry(p, &trace_bprintk_fmt_list, list) { - if (start_index == *pos) - return &p->fmt; - start_index++; + return NULL; +} + +static struct tracepoint_string_entry *lookup_mod_tracepoint_ptr(const char **str_ptr) +{ + struct tracepoint_string_entry *tp_entry; + + list_for_each_entry(tp_entry, &tracepoint_string_list, list) { + if (str_ptr == &tp_entry->str) + return tp_entry; + } + + return NULL; +} + +static const char **find_next_mod_entry(int start_index, void *v, loff_t *pos) +{ + struct trace_bprintk_fmt *tb_fmt; + struct tracepoint_string_entry *tp_entry; + + if (!v || start_index == *pos) + return find_first_mod_entry(); + + tb_fmt = lookup_mod_format_ptr(v); + if (tb_fmt) { + if (tb_fmt->list.next != &trace_bprintk_fmt_list) { + tb_fmt = list_next_entry(tb_fmt, list); + return &tb_fmt->fmt; } - /* pos > index */ + + if (!list_empty(&tracepoint_string_list)) { + tp_entry = list_first_entry(&tracepoint_string_list, + typeof(*tp_entry), list); + return &tp_entry->str; + } + return NULL; } - /* - * v points to the address of the fmt field in the mod list - * structure that holds the module print format. - */ - mod_fmt = container_of(v, typeof(*mod_fmt), fmt); - if (mod_fmt->list.next == &trace_bprintk_fmt_list) + tp_entry = lookup_mod_tracepoint_ptr(v); + if (!tp_entry) return NULL; - mod_fmt = container_of(mod_fmt->list.next, typeof(*mod_fmt), list); + if (tp_entry->list.next == &tracepoint_string_list) + return NULL; - return &mod_fmt->fmt; + tp_entry = list_next_entry(tp_entry, list); + return &tp_entry->str; } static void format_mod_start(void) @@ -195,8 +258,8 @@ module_trace_format_notify(struct notifier_block *self, { return NOTIFY_OK; } -static inline const char ** -find_next_mod_format(int start_index, void *v, const char **fmt, loff_t *pos) +static inline const char **find_next_mod_entry(int start_index, void *v, + loff_t *pos) { return NULL; } @@ -274,7 +337,7 @@ bool trace_is_tracepoint_string(const char *str) { const char **ptr = __start___tracepoint_str; #ifdef CONFIG_MODULES - struct trace_bprintk_fmt *tb_fmt; + struct tracepoint_string_entry *tp_entry; #endif for (ptr = __start___tracepoint_str; ptr < __stop___tracepoint_str; ptr++) { @@ -284,8 +347,8 @@ bool trace_is_tracepoint_string(const char *str) #ifdef CONFIG_MODULES mutex_lock(&btrace_mutex); - list_for_each_entry(tb_fmt, &trace_bprintk_fmt_list, list) { - if ((tb_fmt->type & TRACE_TRACEPOINT_TYPE) && str == tb_fmt->fmt) { + list_for_each_entry(tp_entry, &tracepoint_string_list, list) { + if (str == tp_entry->str) { mutex_unlock(&btrace_mutex); return true; } @@ -297,9 +360,8 @@ bool trace_is_tracepoint_string(const char *str) static const char **find_next(void *v, loff_t *pos) { - const char **fmt = v; int start_index; - int last_index; + int next_index; start_index = __stop___trace_bprintk_fmt - __start___trace_bprintk_fmt; @@ -307,25 +369,19 @@ static const char **find_next(void *v, loff_t *pos) return __start___trace_bprintk_fmt + *pos; /* - * The __tracepoint_str section is treated the same as the - * __trace_printk_fmt section. The difference is that the - * __trace_printk_fmt section should only be used by trace_printk() - * in a debugging environment, as if anything exists in that section - * the trace_prink() helper buffers are allocated, which would just - * waste space in a production environment. - * - * The __tracepoint_str sections on the other hand are used by - * tracepoints which need to map pointers to their strings to - * the ASCII text for userspace. + * Built-in __tracepoint_str entries are exported directly from the + * core section. Module tracepoint_string() mappings are kept on a + * separate tracing-managed list below, because their lifetime is tied + * to module load/unload and differs from module trace_printk() formats. */ - last_index = start_index; + next_index = start_index; start_index = __stop___tracepoint_str - __start___tracepoint_str; - if (*pos < last_index + start_index) - return __start___tracepoint_str + (*pos - last_index); + if (*pos < next_index + start_index) + return __start___tracepoint_str + (*pos - next_index); - start_index += last_index; - return find_next_mod_format(start_index, v, fmt, pos); + start_index += next_index; + return find_next_mod_entry(start_index, v, pos); } static void * -- 2.39.5 (Apple Git-154) ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] tracing: separate module tracepoint strings from trace_printk formats 2026-04-13 12:33 ` [PATCH] tracing: separate module tracepoint strings from trace_printk formats Cao Ruichuang @ 2026-04-14 11:37 ` Petr Pavlu 2026-04-16 8:03 ` Cao Ruichuang 0 siblings, 1 reply; 8+ messages in thread From: Petr Pavlu @ 2026-04-14 11:37 UTC (permalink / raw) To: Cao Ruichuang; +Cc: linux-trace-kernel, linux-kernel, mhiramat, rostedt On 4/13/26 2:33 PM, Cao Ruichuang wrote: > The previous module tracepoint_string() fix took the smallest > implementation path and reused the existing module trace_printk format > storage. > > That was enough to make module __tracepoint_str entries show up in > printk_formats and be accepted by trace_is_tracepoint_string(), but it > also made those copied mappings persist after module unload. That does > not match the expected module lifetime semantics. > > Handle module tracepoint_string() mappings separately instead of mixing > them into the module trace_printk format list. Keep copying the strings > into tracing-managed storage while the module is loaded, but track them > on their own list and drop them again on MODULE_STATE_GOING. > > Keep module trace_printk format handling unchanged. > > This split is intentional: module trace_printk formats and module > tracepoint_string() mappings do not have the same lifetime requirements. > Keeping them in one shared structure would either preserve > tracepoint_string() mappings too long again, or require mixed > ownership/refcount rules in a trace_printk-oriented structure. > > The separate module tracepoint_string() list intentionally keeps one > copied mapping per module entry instead of trying to share copies across > modules by string contents. printk_formats is address-based, and sharing > those copies would add another layer of shared ownership/refcounting > without changing the lifetime rule this fix is trying to restore. I believe it would be more productive to first answer the question in my previous email and give others time to comment on the noted issue and discuss a possible solution. The previous patch hasn't been accepted yet, so sending a new patch on top of it just hours after my comment seems unusual. The patch contains some unrelated changes, such as renaming last_index to next_index in find_next(). I suspect it was generated with the help of AI but it isn't attributed as such. Please check Documentation/process/coding-assistants.rst. The implementation splits the trace_bprintk_fmt_list into two lists. While this looks sensible, it creates nearly duplicate code for handling the two lists, for example, hold_module_trace_format() and hold_module_trace_format(), or lookup_mod_format_ptr() and lookup_mod_tracepoint_ptr(). An alternative would be to keep one list and have a 'struct module *mod' member to differentiate between the strings. 'mod == NULL' for printk fmts and 'mod != NULL' for tracepoint strings. I also considered whether kernel/trace/trace_printk.c could avoid tracking tracepoint strings altogether and instead have find_next_mod_entry() directly traverse the modules list and module::tracepoint_strings_start. However, this would require taking both btrace_mutex and the RCU lock, and I believe it would be more complex. Since the tracepoint strings are now tracked only for the duration of a module's lifetime, kernel/trace/trace_printk.c doesn't need to copy the strings and can reference directly the original module data. Alternatively, the original strings could be placed in an .init section so that once trace_printk.c copies them and the module is initialized, they get dropped. -- Thanks, Petr > > Link: https://bugzilla.kernel.org/show_bug.cgi?id=217196 > Signed-off-by: Cao Ruichuang <create0818@163.com> > --- > include/linux/tracepoint.h | 9 +- > kernel/trace/trace_printk.c | 250 ++++++++++++++++++++++-------------- > 2 files changed, 157 insertions(+), 102 deletions(-) > > diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h > index f14da542402..aec598a4017 100644 > --- a/include/linux/tracepoint.h > +++ b/include/linux/tracepoint.h > @@ -479,11 +479,10 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > * > * For built-in code, the tracing system uses the original string address. > * For modules, the tracing code saves tracepoint strings into > - * tracing-managed storage when the module loads, so their mappings remain > - * available through printk_formats and trace string checks even after the > - * module's own memory goes away. As long as the string does not change > - * during the life of the module, it is fine to use tracepoint_string() > - * within a module. > + * tracing-managed storage while the module is loaded, and drops those > + * mappings again when the module unloads. As long as the string does not > + * change during the life of the module, it is fine to use > + * tracepoint_string() within a module. > */ > #define tracepoint_string(str) \ > ({ \ > diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c > index 9f67ce42ef6..0420ffcff93 100644 > --- a/kernel/trace/trace_printk.c > +++ b/kernel/trace/trace_printk.c > @@ -21,24 +21,24 @@ > > #ifdef CONFIG_MODULES > > -/* > - * modules trace_printk() formats and tracepoint_string() strings are > - * autosaved in struct trace_bprintk_fmt, which are queued on > - * trace_bprintk_fmt_list. > - */ > +/* module trace_printk() formats are autosaved on trace_bprintk_fmt_list. */ > static LIST_HEAD(trace_bprintk_fmt_list); > +/* module tracepoint_string() copies live on tracepoint_string_list. */ > +static LIST_HEAD(tracepoint_string_list); > > -/* serialize accesses to trace_bprintk_fmt_list */ > +/* serialize accesses to module format and tracepoint-string lists */ > static DEFINE_MUTEX(btrace_mutex); > > struct trace_bprintk_fmt { > struct list_head list; > const char *fmt; > - unsigned int type; > }; > > -#define TRACE_BPRINTK_TYPE BIT(0) > -#define TRACE_TRACEPOINT_TYPE BIT(1) > +struct tracepoint_string_entry { > + struct list_head list; > + struct module *mod; > + const char *str; > +}; > > static inline struct trace_bprintk_fmt *lookup_format(const char *fmt) > { > @@ -54,24 +54,21 @@ static inline struct trace_bprintk_fmt *lookup_format(const char *fmt) > return NULL; > } > > -static void hold_module_trace_format(const char **start, const char **end, > - unsigned int type) > +static void hold_module_trace_bprintk_format(const char **start, const char **end) > { > const char **iter; > char *fmt; > > /* allocate the trace_printk per cpu buffers */ > - if ((type & TRACE_BPRINTK_TYPE) && start != end) > + if (start != end) > trace_printk_init_buffers(); > > mutex_lock(&btrace_mutex); > for (iter = start; iter < end; iter++) { > struct trace_bprintk_fmt *tb_fmt = lookup_format(*iter); > if (tb_fmt) { > - if (!IS_ERR(tb_fmt)) { > - tb_fmt->type |= type; > + if (!IS_ERR(tb_fmt)) > *iter = tb_fmt->fmt; > - } > continue; > } > > @@ -83,7 +80,6 @@ static void hold_module_trace_format(const char **start, const char **end, > list_add_tail(&tb_fmt->list, &trace_bprintk_fmt_list); > strcpy(fmt, *iter); > tb_fmt->fmt = fmt; > - tb_fmt->type = type; > } else > kfree(tb_fmt); > } > @@ -93,89 +89,156 @@ static void hold_module_trace_format(const char **start, const char **end, > mutex_unlock(&btrace_mutex); > } > > +static void hold_module_tracepoint_strings(struct module *mod, > + const char **start, > + const char **end) > +{ > + const char **iter; > + > + mutex_lock(&btrace_mutex); > + for (iter = start; iter < end; iter++) { > + struct tracepoint_string_entry *tp_entry; > + char *str; > + > + tp_entry = kmalloc_obj(*tp_entry); > + if (!tp_entry) > + continue; > + > + str = kstrdup(*iter, GFP_KERNEL); > + if (!str) { > + kfree(tp_entry); > + continue; > + } > + > + tp_entry->mod = mod; > + tp_entry->str = str; > + list_add_tail(&tp_entry->list, &tracepoint_string_list); > + *iter = tp_entry->str; > + } > + mutex_unlock(&btrace_mutex); > +} > + > +static void release_module_tracepoint_strings(struct module *mod) > +{ > + struct tracepoint_string_entry *tp_entry, *n; > + > + mutex_lock(&btrace_mutex); > + list_for_each_entry_safe(tp_entry, n, &tracepoint_string_list, list) { > + if (tp_entry->mod != mod) > + continue; > + list_del(&tp_entry->list); > + kfree(tp_entry->str); > + kfree(tp_entry); > + } > + mutex_unlock(&btrace_mutex); > +} > + > static int module_trace_format_notify(struct notifier_block *self, > unsigned long val, void *data) > { > struct module *mod = data; > > - if (val != MODULE_STATE_COMING) > - return NOTIFY_OK; > + switch (val) { > + case MODULE_STATE_COMING: > + if (mod->num_trace_bprintk_fmt) { > + const char **start = mod->trace_bprintk_fmt_start; > + const char **end = start + mod->num_trace_bprintk_fmt; > > - if (mod->num_trace_bprintk_fmt) { > - const char **start = mod->trace_bprintk_fmt_start; > - const char **end = start + mod->num_trace_bprintk_fmt; > + hold_module_trace_bprintk_format(start, end); > + } > + > + if (mod->num_tracepoint_strings) { > + const char **start = mod->tracepoint_strings_start; > + const char **end = start + mod->num_tracepoint_strings; > > - hold_module_trace_format(start, end, TRACE_BPRINTK_TYPE); > + hold_module_tracepoint_strings(mod, start, end); > + } > + break; > + case MODULE_STATE_GOING: > + release_module_tracepoint_strings(mod); > + break; > } > > - if (mod->num_tracepoint_strings) { > - const char **start = mod->tracepoint_strings_start; > - const char **end = start + mod->num_tracepoint_strings; > + return NOTIFY_OK; > +} > > - hold_module_trace_format(start, end, TRACE_TRACEPOINT_TYPE); > +static const char **find_first_mod_entry(void) > +{ > + struct trace_bprintk_fmt *tb_fmt; > + struct tracepoint_string_entry *tp_entry; > + > + if (!list_empty(&trace_bprintk_fmt_list)) { > + tb_fmt = list_first_entry(&trace_bprintk_fmt_list, > + typeof(*tb_fmt), list); > + return &tb_fmt->fmt; > } > > - return NOTIFY_OK; > + if (!list_empty(&tracepoint_string_list)) { > + tp_entry = list_first_entry(&tracepoint_string_list, > + typeof(*tp_entry), list); > + return &tp_entry->str; > + } > + > + return NULL; > } > > -/* > - * The debugfs/tracing/printk_formats file maps the addresses with > - * the ASCII formats that are used in the bprintk events in the > - * buffer. For userspace tools to be able to decode the events from > - * the buffer, they need to be able to map the address with the format. > - * > - * The addresses of the bprintk formats are in their own section > - * __trace_printk_fmt. But for modules we copy them into a link list. > - * The code to print the formats and their addresses passes around the > - * address of the fmt string. If the fmt address passed into the seq > - * functions is within the kernel core __trace_printk_fmt section, then > - * it simply uses the next pointer in the list. > - * > - * When the fmt pointer is outside the kernel core __trace_printk_fmt > - * section, then we need to read the link list pointers. The trick is > - * we pass the address of the string to the seq function just like > - * we do for the kernel core formats. To get back the structure that > - * holds the format, we simply use container_of() and then go to the > - * next format in the list. > - */ > -static const char ** > -find_next_mod_format(int start_index, void *v, const char **fmt, loff_t *pos) > +static struct trace_bprintk_fmt *lookup_mod_format_ptr(const char **fmt_ptr) > { > - struct trace_bprintk_fmt *mod_fmt; > + struct trace_bprintk_fmt *tb_fmt; > > - if (list_empty(&trace_bprintk_fmt_list)) > - return NULL; > + list_for_each_entry(tb_fmt, &trace_bprintk_fmt_list, list) { > + if (fmt_ptr == &tb_fmt->fmt) > + return tb_fmt; > + } > > - /* > - * v will point to the address of the fmt record from t_next > - * v will be NULL from t_start. > - * If this is the first pointer or called from start > - * then we need to walk the list. > - */ > - if (!v || start_index == *pos) { > - struct trace_bprintk_fmt *p; > - > - /* search the module list */ > - list_for_each_entry(p, &trace_bprintk_fmt_list, list) { > - if (start_index == *pos) > - return &p->fmt; > - start_index++; > + return NULL; > +} > + > +static struct tracepoint_string_entry *lookup_mod_tracepoint_ptr(const char **str_ptr) > +{ > + struct tracepoint_string_entry *tp_entry; > + > + list_for_each_entry(tp_entry, &tracepoint_string_list, list) { > + if (str_ptr == &tp_entry->str) > + return tp_entry; > + } > + > + return NULL; > +} > + > +static const char **find_next_mod_entry(int start_index, void *v, loff_t *pos) > +{ > + struct trace_bprintk_fmt *tb_fmt; > + struct tracepoint_string_entry *tp_entry; > + > + if (!v || start_index == *pos) > + return find_first_mod_entry(); > + > + tb_fmt = lookup_mod_format_ptr(v); > + if (tb_fmt) { > + if (tb_fmt->list.next != &trace_bprintk_fmt_list) { > + tb_fmt = list_next_entry(tb_fmt, list); > + return &tb_fmt->fmt; > } > - /* pos > index */ > + > + if (!list_empty(&tracepoint_string_list)) { > + tp_entry = list_first_entry(&tracepoint_string_list, > + typeof(*tp_entry), list); > + return &tp_entry->str; > + } > + > return NULL; > } > > - /* > - * v points to the address of the fmt field in the mod list > - * structure that holds the module print format. > - */ > - mod_fmt = container_of(v, typeof(*mod_fmt), fmt); > - if (mod_fmt->list.next == &trace_bprintk_fmt_list) > + tp_entry = lookup_mod_tracepoint_ptr(v); > + if (!tp_entry) > return NULL; > > - mod_fmt = container_of(mod_fmt->list.next, typeof(*mod_fmt), list); > + if (tp_entry->list.next == &tracepoint_string_list) > + return NULL; > > - return &mod_fmt->fmt; > + tp_entry = list_next_entry(tp_entry, list); > + return &tp_entry->str; > } > > static void format_mod_start(void) > @@ -195,8 +258,8 @@ module_trace_format_notify(struct notifier_block *self, > { > return NOTIFY_OK; > } > -static inline const char ** > -find_next_mod_format(int start_index, void *v, const char **fmt, loff_t *pos) > +static inline const char **find_next_mod_entry(int start_index, void *v, > + loff_t *pos) > { > return NULL; > } > @@ -274,7 +337,7 @@ bool trace_is_tracepoint_string(const char *str) > { > const char **ptr = __start___tracepoint_str; > #ifdef CONFIG_MODULES > - struct trace_bprintk_fmt *tb_fmt; > + struct tracepoint_string_entry *tp_entry; > #endif > > for (ptr = __start___tracepoint_str; ptr < __stop___tracepoint_str; ptr++) { > @@ -284,8 +347,8 @@ bool trace_is_tracepoint_string(const char *str) > > #ifdef CONFIG_MODULES > mutex_lock(&btrace_mutex); > - list_for_each_entry(tb_fmt, &trace_bprintk_fmt_list, list) { > - if ((tb_fmt->type & TRACE_TRACEPOINT_TYPE) && str == tb_fmt->fmt) { > + list_for_each_entry(tp_entry, &tracepoint_string_list, list) { > + if (str == tp_entry->str) { > mutex_unlock(&btrace_mutex); > return true; > } > @@ -297,9 +360,8 @@ bool trace_is_tracepoint_string(const char *str) > > static const char **find_next(void *v, loff_t *pos) > { > - const char **fmt = v; > int start_index; > - int last_index; > + int next_index; > > start_index = __stop___trace_bprintk_fmt - __start___trace_bprintk_fmt; > > @@ -307,25 +369,19 @@ static const char **find_next(void *v, loff_t *pos) > return __start___trace_bprintk_fmt + *pos; > > /* > - * The __tracepoint_str section is treated the same as the > - * __trace_printk_fmt section. The difference is that the > - * __trace_printk_fmt section should only be used by trace_printk() > - * in a debugging environment, as if anything exists in that section > - * the trace_prink() helper buffers are allocated, which would just > - * waste space in a production environment. > - * > - * The __tracepoint_str sections on the other hand are used by > - * tracepoints which need to map pointers to their strings to > - * the ASCII text for userspace. > + * Built-in __tracepoint_str entries are exported directly from the > + * core section. Module tracepoint_string() mappings are kept on a > + * separate tracing-managed list below, because their lifetime is tied > + * to module load/unload and differs from module trace_printk() formats. > */ > - last_index = start_index; > + next_index = start_index; > start_index = __stop___tracepoint_str - __start___tracepoint_str; > > - if (*pos < last_index + start_index) > - return __start___tracepoint_str + (*pos - last_index); > + if (*pos < next_index + start_index) > + return __start___tracepoint_str + (*pos - next_index); > > - start_index += last_index; > - return find_next_mod_format(start_index, v, fmt, pos); > + start_index += next_index; > + return find_next_mod_entry(start_index, v, pos); > } > > static void * ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] tracing: separate module tracepoint strings from trace_printk formats 2026-04-14 11:37 ` Petr Pavlu @ 2026-04-16 8:03 ` Cao Ruichuang 0 siblings, 0 replies; 8+ messages in thread From: Cao Ruichuang @ 2026-04-16 8:03 UTC (permalink / raw) To: Petr Pavlu Cc: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers, linux-kernel, linux-trace-kernel Hi Petr, Sorry for the noise. The previous revisions were not sent automatically by an AI agent, but I did use AI assistance while working on this thread and I should have disclosed that according to Documentation/process/coding-assistants.rst. More importantly, I should have slowed down and answered the design questions first instead of posting another implementation so quickly. After going back through the bug report, the code, and a local reproducer, I think the original bug report is valid: module tracepoint_string() entries do not currently show up in printk_formats. The implementation gap seems to be that the module path handles __trace_printk_fmt, but there is no equivalent handling for a module's __tracepoint_str entries, so printk_formats has no module-side source from which to enumerate them. What I got wrong in the previous versions was the direction of the fix. I treated the missing module __tracepoint_str handling as if it should reuse the module trace_printk storage model, which made the mapping persist beyond module unload. I agree that this is not the right lifetime semantics for tracepoint_string(). So I do not plan to send another patch revision until the intended module lifetime behavior is clear. At this point, the direction that seems right to me is to make module tracepoint_string() mappings visible while the module is alive, without simply reusing the trace_printk persistence model. Thanks, Cao Assisted-by: Codex:GPT-5.4 ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-04-16 8:04 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-04-06 17:09 [PATCH] tracing: preserve module tracepoint strings Cao Ruichuang 2026-04-08 20:32 ` Steven Rostedt 2026-04-09 12:37 ` Petr Pavlu 2026-04-10 5:18 ` [PATCH v2] " Cao Ruichuang 2026-04-13 9:40 ` Petr Pavlu 2026-04-13 12:33 ` [PATCH] tracing: separate module tracepoint strings from trace_printk formats Cao Ruichuang 2026-04-14 11:37 ` Petr Pavlu 2026-04-16 8:03 ` Cao Ruichuang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox