]> www.infradead.org Git - nvme.git/commitdiff
ftrace: Implement :mod: cache filtering on kernel command line
authorSteven Rostedt <rostedt@goodmis.org>
Thu, 16 Jan 2025 22:58:32 +0000 (17:58 -0500)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Fri, 17 Jan 2025 02:27:10 +0000 (21:27 -0500)
Module functions can be set to set_ftrace_filter before the module is
loaded.

  # echo :mod:snd_hda_intel > set_ftrace_filter

This will enable all the functions for the module snd_hda_intel. If that
module is not loaded, it is "cached" in the trace array for when the
module is loaded, its functions will be traced.

But this is not implemented in the kernel command line. That's because the
kernel command line filtering is added very early in boot up as it is
needed to be done before boot time function tracing can start, which is
also available very early in boot up. The code used by the
"set_ftrace_filter" file can not be used that early as it depends on some
other initialization to occur first. But some of the functions can.

Implement the ":mod:" feature of "set_ftrace_filter" in the kernel command
line parsing. Now function tracing on just a single module that is loaded
at boot up can be done.

Adding:

 ftrace=function ftrace_filter=:mod:sna_hda_intel

To the kernel command line will only enable the sna_hda_intel module
functions when the module is loaded, and it will start tracing.

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://lore.kernel.org/20250116175832.34e39779@gandalf.local.home
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
kernel/trace/ftrace.c
kernel/trace/trace.c
kernel/trace/trace.h

index f054343be0263c5ab570b6471cabfc639ad7dccf..9387e2865ca82a4d107bcc9bdcb7735bd7a1375f 100644 (file)
@@ -4968,10 +4968,6 @@ static int cache_mod(struct trace_array *tr,
        return ftrace_add_mod(tr, func, module, enable);
 }
 
-static int
-ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
-                int reset, int enable);
-
 #ifdef CONFIG_MODULES
 static void process_mod_list(struct list_head *head, struct ftrace_ops *ops,
                             char *mod, bool enable)
@@ -5761,7 +5757,7 @@ ftrace_match_addr(struct ftrace_hash *hash, unsigned long *ips,
 static int
 ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len,
                unsigned long *ips, unsigned int cnt,
-               int remove, int reset, int enable)
+               int remove, int reset, int enable, char *mod)
 {
        struct ftrace_hash **orig_hash;
        struct ftrace_hash *hash;
@@ -5787,7 +5783,15 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len,
                goto out_regex_unlock;
        }
 
-       if (buf && !ftrace_match_records(hash, buf, len)) {
+       if (buf && !match_records(hash, buf, len, mod)) {
+               /* If this was for a module and nothing was enabled, flag it */
+               if (mod)
+                       (*orig_hash)->flags |= FTRACE_HASH_FL_MOD;
+
+               /*
+                * Even if it is a mod, return error to let caller know
+                * nothing was added
+                */
                ret = -EINVAL;
                goto out_regex_unlock;
        }
@@ -5812,7 +5816,7 @@ static int
 ftrace_set_addr(struct ftrace_ops *ops, unsigned long *ips, unsigned int cnt,
                int remove, int reset, int enable)
 {
-       return ftrace_set_hash(ops, NULL, 0, ips, cnt, remove, reset, enable);
+       return ftrace_set_hash(ops, NULL, 0, ips, cnt, remove, reset, enable, NULL);
 }
 
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
@@ -6190,7 +6194,38 @@ static int
 ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
                 int reset, int enable)
 {
-       return ftrace_set_hash(ops, buf, len, NULL, 0, 0, reset, enable);
+       char *mod = NULL, *func, *command, *next = buf;
+       char *tmp __free(kfree) = NULL;
+       struct trace_array *tr = ops->private;
+       int ret;
+
+       func = strsep(&next, ":");
+
+       /* This can also handle :mod: parsing */
+       if (next) {
+               if (!tr)
+                       return -EINVAL;
+
+               command = strsep(&next, ":");
+               if (strcmp(command, "mod") != 0)
+                       return -EINVAL;
+
+               mod = next;
+               len = command - func;
+               /* Save the original func as ftrace_set_hash() can modify it */
+               tmp = kstrdup(func, GFP_KERNEL);
+       }
+
+       ret = ftrace_set_hash(ops, func, len, NULL, 0, 0, reset, enable, mod);
+
+       if (tr && mod && ret < 0) {
+               /* Did tmp fail to allocate? */
+               if (!tmp)
+                       return -ENOMEM;
+               ret = cache_mod(tr, tmp, mod, enable);
+       }
+
+       return ret;
 }
 
 /**
@@ -6354,6 +6389,14 @@ ftrace_set_early_filter(struct ftrace_ops *ops, char *buf, int enable)
 
        ftrace_ops_init(ops);
 
+       /* The trace_array is needed for caching module function filters */
+       if (!ops->private) {
+               struct trace_array *tr = trace_get_global_array();
+
+               ops->private = tr;
+               ftrace_init_trace_array(tr);
+       }
+
        while (buf) {
                func = strsep(&buf, ",");
                ftrace_set_regex(ops, func, strlen(func), 0, enable);
@@ -7787,9 +7830,14 @@ static void ftrace_update_trampoline(struct ftrace_ops *ops)
 
 void ftrace_init_trace_array(struct trace_array *tr)
 {
+       if (tr->flags & TRACE_ARRAY_FL_MOD_INIT)
+               return;
+
        INIT_LIST_HEAD(&tr->func_probes);
        INIT_LIST_HEAD(&tr->mod_trace);
        INIT_LIST_HEAD(&tr->mod_notrace);
+
+       tr->flags |= TRACE_ARRAY_FL_MOD_INIT;
 }
 #else
 
@@ -7818,7 +7866,8 @@ static void ftrace_update_trampoline(struct ftrace_ops *ops)
 __init void ftrace_init_global_array_ops(struct trace_array *tr)
 {
        tr->ops = &global_ops;
-       tr->ops->private = tr;
+       if (!global_ops.private)
+               global_ops.private = tr;
        ftrace_init_trace_array(tr);
        init_array_fgraph_ops(tr, tr->ops);
 }
index 957f941a08e743cc3bb8bc89597b950a6308b42e..a553f1e7a057a268b5216f9aec6f18d76bc93ad6 100644 (file)
@@ -10657,6 +10657,14 @@ out:
        return ret;
 }
 
+#ifdef CONFIG_FUNCTION_TRACER
+/* Used to set module cached ftrace filtering at boot up */
+__init struct trace_array *trace_get_global_array(void)
+{
+       return &global_trace;
+}
+#endif
+
 void __init ftrace_boot_snapshot(void)
 {
 #ifdef CONFIG_TRACER_MAX_TRACE
index 5f3e68a8d8a0064490235a99f539e836808a1c30..ba68a5a540929140f03b58df681fe1b397868b7a 100644 (file)
@@ -432,6 +432,7 @@ struct trace_array {
 enum {
        TRACE_ARRAY_FL_GLOBAL   = BIT(0),
        TRACE_ARRAY_FL_BOOT     = BIT(1),
+       TRACE_ARRAY_FL_MOD_INIT = BIT(2),
 };
 
 extern struct list_head ftrace_trace_arrays;
@@ -1116,6 +1117,7 @@ void ftrace_destroy_function_files(struct trace_array *tr);
 int ftrace_allocate_ftrace_ops(struct trace_array *tr);
 void ftrace_free_ftrace_ops(struct trace_array *tr);
 void ftrace_init_global_array_ops(struct trace_array *tr);
+struct trace_array *trace_get_global_array(void);
 void ftrace_init_array_ops(struct trace_array *tr, ftrace_func_t func);
 void ftrace_reset_array_ops(struct trace_array *tr);
 void ftrace_init_tracefs(struct trace_array *tr, struct dentry *d_tracer);