]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - kernel/trace/trace_events.c
tracing: Have type enum modifications copy the strings
[mirror_ubuntu-jammy-kernel.git] / kernel / trace / trace_events.c
index fa2b632055b3026720adfa8446013f08342854a7..c072e8b9849c1c2590d0735e1f8683a881f09932 100644 (file)
@@ -40,6 +40,14 @@ static LIST_HEAD(ftrace_generic_fields);
 static LIST_HEAD(ftrace_common_fields);
 static bool eventdir_initialized;
 
+static LIST_HEAD(module_strings);
+
+struct module_string {
+       struct list_head        next;
+       struct module           *module;
+       char                    *str;
+};
+
 #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO)
 
 static struct kmem_cache *field_cachep;
@@ -2633,14 +2641,40 @@ static void update_event_printk(struct trace_event_call *call,
        }
 }
 
+static void add_str_to_module(struct module *module, char *str)
+{
+       struct module_string *modstr;
+
+       modstr = kmalloc(sizeof(*modstr), GFP_KERNEL);
+
+       /*
+        * If we failed to allocate memory here, then we'll just
+        * let the str memory leak when the module is removed.
+        * If this fails to allocate, there's worse problems than
+        * a leaked string on module removal.
+        */
+       if (WARN_ON_ONCE(!modstr))
+               return;
+
+       modstr->module = module;
+       modstr->str = str;
+
+       list_add(&modstr->next, &module_strings);
+}
+
 static void update_event_fields(struct trace_event_call *call,
                                struct trace_eval_map *map)
 {
        struct ftrace_event_field *field;
        struct list_head *head;
        char *ptr;
+       char *str;
        int len = strlen(map->eval_string);
 
+       /* Dynamic events should never have field maps */
+       if (WARN_ON_ONCE(call->flags & TRACE_EVENT_FL_DYNAMIC))
+               return;
+
        head = trace_get_fields(call);
        list_for_each_entry(field, head, link) {
                ptr = strchr(field->type, '[');
@@ -2654,9 +2688,26 @@ static void update_event_fields(struct trace_event_call *call,
                if (strncmp(map->eval_string, ptr, len) != 0)
                        continue;
 
+               str = kstrdup(field->type, GFP_KERNEL);
+               if (WARN_ON_ONCE(!str))
+                       return;
+               ptr = str + (ptr - field->type);
                ptr = eval_replace(ptr, map, len);
                /* enum/sizeof string smaller than value */
-               WARN_ON_ONCE(!ptr);
+               if (WARN_ON_ONCE(!ptr)) {
+                       kfree(str);
+                       continue;
+               }
+
+               /*
+                * If the event is part of a module, then we need to free the string
+                * when the module is removed. Otherwise, it will stay allocated
+                * until a reboot.
+                */
+               if (call->module)
+                       add_str_to_module(call->module, str);
+
+               field->type = str;
        }
 }
 
@@ -2879,6 +2930,7 @@ static void trace_module_add_events(struct module *mod)
 static void trace_module_remove_events(struct module *mod)
 {
        struct trace_event_call *call, *p;
+       struct module_string *modstr, *m;
 
        down_write(&trace_event_sem);
        list_for_each_entry_safe(call, p, &ftrace_events, list) {
@@ -2887,6 +2939,14 @@ static void trace_module_remove_events(struct module *mod)
                if (call->module == mod)
                        __trace_remove_event_call(call);
        }
+       /* Check for any strings allocade for this module */
+       list_for_each_entry_safe(modstr, m, &module_strings, next) {
+               if (modstr->module != mod)
+                       continue;
+               list_del(&modstr->next);
+               kfree(modstr->str);
+               kfree(modstr);
+       }
        up_write(&trace_event_sem);
 
        /*