ftrace: Added ftrace_func_mapper for function probe triggers
authorSteven Rostedt (VMware) <rostedt@goodmis.org>
Tue, 4 Apr 2017 00:58:35 +0000 (20:58 -0400)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Fri, 21 Apr 2017 02:06:37 +0000 (22:06 -0400)
In order to move the ops to the function probes directly, they need a way to
map function ips to their own data without depending on the infrastructure
of the function probes, as the data field will be going away.

New helper functions are added that are based on the ftrace_hash code.
ftrace_func_mapper functions are there to let the probes map ips to their
data. These can be allocated by the probe ops, and referenced in the
function callbacks.

Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
kernel/trace/ftrace.c
kernel/trace/trace.h
kernel/trace/trace_events.c

index d8873079bed4385b58b888d5dd66debf9a1cba1d..ac47d1845fdb4e42ef0c2fb419bf508a5048f39b 100644 (file)
@@ -3808,6 +3808,147 @@ static void ftrace_free_entry(struct ftrace_func_probe *entry)
        kfree(entry);
 }
 
+struct ftrace_func_map {
+       struct ftrace_func_entry        entry;
+       void                            *data;
+};
+
+struct ftrace_func_mapper {
+       struct ftrace_hash              hash;
+};
+
+/**
+ * allocate_ftrace_func_mapper - allocate a new ftrace_func_mapper
+ *
+ * Returns a ftrace_func_mapper descriptor that can be used to map ips to data.
+ */
+struct ftrace_func_mapper *allocate_ftrace_func_mapper(void)
+{
+       struct ftrace_hash *hash;
+
+       /*
+        * The mapper is simply a ftrace_hash, but since the entries
+        * in the hash are not ftrace_func_entry type, we define it
+        * as a separate structure.
+        */
+       hash = alloc_ftrace_hash(FTRACE_HASH_DEFAULT_BITS);
+       return (struct ftrace_func_mapper *)hash;
+}
+
+/**
+ * ftrace_func_mapper_find_ip - Find some data mapped to an ip
+ * @mapper: The mapper that has the ip maps
+ * @ip: the instruction pointer to find the data for
+ *
+ * Returns the data mapped to @ip if found otherwise NULL. The return
+ * is actually the address of the mapper data pointer. The address is
+ * returned for use cases where the data is no bigger than a long, and
+ * the user can use the data pointer as its data instead of having to
+ * allocate more memory for the reference.
+ */
+void **ftrace_func_mapper_find_ip(struct ftrace_func_mapper *mapper,
+                                 unsigned long ip)
+{
+       struct ftrace_func_entry *entry;
+       struct ftrace_func_map *map;
+
+       entry = ftrace_lookup_ip(&mapper->hash, ip);
+       if (!entry)
+               return NULL;
+
+       map = (struct ftrace_func_map *)entry;
+       return &map->data;
+}
+
+/**
+ * ftrace_func_mapper_add_ip - Map some data to an ip
+ * @mapper: The mapper that has the ip maps
+ * @ip: The instruction pointer address to map @data to
+ * @data: The data to map to @ip
+ *
+ * Returns 0 on succes otherwise an error.
+ */
+int ftrace_func_mapper_add_ip(struct ftrace_func_mapper *mapper,
+                             unsigned long ip, void *data)
+{
+       struct ftrace_func_entry *entry;
+       struct ftrace_func_map *map;
+
+       entry = ftrace_lookup_ip(&mapper->hash, ip);
+       if (entry)
+               return -EBUSY;
+
+       map = kmalloc(sizeof(*map), GFP_KERNEL);
+       if (!map)
+               return -ENOMEM;
+
+       map->entry.ip = ip;
+       map->data = data;
+
+       __add_hash_entry(&mapper->hash, &map->entry);
+
+       return 0;
+}
+
+/**
+ * ftrace_func_mapper_remove_ip - Remove an ip from the mapping
+ * @mapper: The mapper that has the ip maps
+ * @ip: The instruction pointer address to remove the data from
+ *
+ * Returns the data if it is found, otherwise NULL.
+ * Note, if the data pointer is used as the data itself, (see 
+ * ftrace_func_mapper_find_ip(), then the return value may be meaningless,
+ * if the data pointer was set to zero.
+ */
+void *ftrace_func_mapper_remove_ip(struct ftrace_func_mapper *mapper,
+                                  unsigned long ip)
+{
+       struct ftrace_func_entry *entry;
+       struct ftrace_func_map *map;
+       void *data;
+
+       entry = ftrace_lookup_ip(&mapper->hash, ip);
+       if (!entry)
+               return NULL;
+
+       map = (struct ftrace_func_map *)entry;
+       data = map->data;
+
+       remove_hash_entry(&mapper->hash, entry);
+       kfree(entry);
+
+       return data;
+}
+
+/**
+ * free_ftrace_func_mapper - free a mapping of ips and data
+ * @mapper: The mapper that has the ip maps
+ * @free_func: A function to be called on each data item.
+ *
+ * This is used to free the function mapper. The @free_func is optional
+ * and can be used if the data needs to be freed as well.
+ */
+void free_ftrace_func_mapper(struct ftrace_func_mapper *mapper,
+                            ftrace_mapper_func free_func)
+{
+       struct ftrace_func_entry *entry;
+       struct ftrace_func_map *map;
+       struct hlist_head *hhd;
+       int size = 1 << mapper->hash.size_bits;
+       int i;
+
+       if (free_func && mapper->hash.count) {
+               for (i = 0; i < size; i++) {
+                       hhd = &mapper->hash.buckets[i];
+                       hlist_for_each_entry(entry, hhd, hlist) {
+                               map = (struct ftrace_func_map *)entry;
+                               free_func(map);
+                       }
+               }
+       }
+       free_ftrace_hash(&mapper->hash);
+}
+
 int
 register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
                              void *data)
index 0f915c264c19183256ace1d9482a2fb274e8710e..dbbdee21bcc404291af67752ebc27e86d7002477 100644 (file)
@@ -944,8 +944,22 @@ struct ftrace_probe_ops {
                                         unsigned long ip,
                                         struct ftrace_probe_ops *ops,
                                         void *data);
+       void                    *private_data;
 };
 
+struct ftrace_func_mapper;
+typedef int (*ftrace_mapper_func)(void *data);
+
+struct ftrace_func_mapper *allocate_ftrace_func_mapper(void);
+void **ftrace_func_mapper_find_ip(struct ftrace_func_mapper *mapper,
+                                          unsigned long ip);
+int ftrace_func_mapper_add_ip(struct ftrace_func_mapper *mapper,
+                              unsigned long ip, void *data);
+void *ftrace_func_mapper_remove_ip(struct ftrace_func_mapper *mapper,
+                                  unsigned long ip);
+void free_ftrace_func_mapper(struct ftrace_func_mapper *mapper,
+                            ftrace_mapper_func free_func);
+
 extern int
 register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
                              void *data);
index 9dbac1881b0368e1992c0959cab10fefd1aaf497..ee308312fe87ba105088f4dcf42ef8ca3629c3a5 100644 (file)
@@ -2460,32 +2460,44 @@ struct event_probe_data {
        bool                            enable;
 };
 
+static void update_event_probe(struct event_probe_data *data)
+{
+       if (data->enable)
+               clear_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &data->file->flags);
+       else
+               set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &data->file->flags);
+}
+
 static void
 event_enable_probe(unsigned long ip, unsigned long parent_ip,
                   struct ftrace_probe_ops *ops, void **_data)
 {
-       struct event_probe_data **pdata = (struct event_probe_data **)_data;
-       struct event_probe_data *data = *pdata;
+       struct ftrace_func_mapper *mapper = ops->private_data;
+       struct event_probe_data *data;
+       void **pdata;
 
-       if (!data)
+       pdata = ftrace_func_mapper_find_ip(mapper, ip);
+       if (!pdata || !*pdata)
                return;
 
-       if (data->enable)
-               clear_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &data->file->flags);
-       else
-               set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &data->file->flags);
+       data = *pdata;
+       update_event_probe(data);
 }
 
 static void
 event_enable_count_probe(unsigned long ip, unsigned long parent_ip,
                         struct ftrace_probe_ops *ops, void **_data)
 {
-       struct event_probe_data **pdata = (struct event_probe_data **)_data;
-       struct event_probe_data *data = *pdata;
+       struct ftrace_func_mapper *mapper = ops->private_data;
+       struct event_probe_data *data;
+       void **pdata;
 
-       if (!data)
+       pdata = ftrace_func_mapper_find_ip(mapper, ip);
+       if (!pdata || !*pdata)
                return;
 
+       data = *pdata;
+
        if (!data->count)
                return;
 
@@ -2496,14 +2508,23 @@ event_enable_count_probe(unsigned long ip, unsigned long parent_ip,
        if (data->count != -1)
                (data->count)--;
 
-       event_enable_probe(ip, parent_ip, ops, _data);
+       update_event_probe(data);
 }
 
 static int
 event_enable_print(struct seq_file *m, unsigned long ip,
                      struct ftrace_probe_ops *ops, void *_data)
 {
-       struct event_probe_data *data = _data;
+       struct ftrace_func_mapper *mapper = ops->private_data;
+       struct event_probe_data *data;
+       void **pdata;
+
+       pdata = ftrace_func_mapper_find_ip(mapper, ip);
+
+       if (WARN_ON_ONCE(!pdata || !*pdata))
+               return 0;
+
+       data = *pdata;
 
        seq_printf(m, "%ps:", (void *)ip);
 
@@ -2524,10 +2545,17 @@ static int
 event_enable_init(struct ftrace_probe_ops *ops, unsigned long ip,
                  void **_data)
 {
+       struct ftrace_func_mapper *mapper = ops->private_data;
        struct event_probe_data **pdata = (struct event_probe_data **)_data;
        struct event_probe_data *data = *pdata;
+       int ret;
+
+       ret = ftrace_func_mapper_add_ip(mapper, ip, data);
+       if (ret < 0)
+               return ret;
 
        data->ref++;
+
        return 0;
 }
 
@@ -2535,8 +2563,13 @@ static void
 event_enable_free(struct ftrace_probe_ops *ops, unsigned long ip,
                  void **_data)
 {
-       struct event_probe_data **pdata = (struct event_probe_data **)_data;
-       struct event_probe_data *data = *pdata;
+       struct ftrace_func_mapper *mapper = ops->private_data;
+       struct event_probe_data *data;
+
+       data = ftrace_func_mapper_remove_ip(mapper, ip);
+
+       if (WARN_ON_ONCE(!data))
+               return;
 
        if (WARN_ON_ONCE(data->ref <= 0))
                return;
@@ -2548,7 +2581,6 @@ event_enable_free(struct ftrace_probe_ops *ops, unsigned long ip,
                module_put(data->file->event_call->mod);
                kfree(data);
        }
-       *pdata = NULL;
 }
 
 static struct ftrace_probe_ops event_enable_probe_ops = {
@@ -2627,6 +2659,13 @@ event_enable_func(struct ftrace_hash *hash,
        }
 
        ret = -ENOMEM;
+
+       if (!ops->private_data) {
+               ops->private_data = allocate_ftrace_func_mapper();
+               if (!ops->private_data)
+                       goto out;
+       }
+
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                goto out;
@@ -2663,6 +2702,7 @@ event_enable_func(struct ftrace_hash *hash,
        ret = __ftrace_event_enable_disable(file, 1, 1);
        if (ret < 0)
                goto out_put;
+
        ret = register_ftrace_function_probe(glob, ops, data);
        /*
         * The above returns on success the # of functions enabled,