Merge tag 'trace-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
[linux-block.git] / kernel / trace / ftrace.c
index 8319e09e15b945f14f9046edeb885e173ef26652..ccdf3664e4a9a7f6a79423dec807cd9f0c3ecd6f 100644 (file)
@@ -203,30 +203,6 @@ void clear_ftrace_function(void)
        ftrace_trace_function = ftrace_stub;
 }
 
-static void per_cpu_ops_disable_all(struct ftrace_ops *ops)
-{
-       int cpu;
-
-       for_each_possible_cpu(cpu)
-               *per_cpu_ptr(ops->disabled, cpu) = 1;
-}
-
-static int per_cpu_ops_alloc(struct ftrace_ops *ops)
-{
-       int __percpu *disabled;
-
-       if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
-               return -EINVAL;
-
-       disabled = alloc_percpu(int);
-       if (!disabled)
-               return -ENOMEM;
-
-       ops->disabled = disabled;
-       per_cpu_ops_disable_all(ops);
-       return 0;
-}
-
 static void ftrace_sync(struct work_struct *work)
 {
        /*
@@ -262,8 +238,8 @@ static ftrace_func_t ftrace_ops_get_list_func(struct ftrace_ops *ops)
         * If this is a dynamic, RCU, or per CPU ops, or we force list func,
         * then it needs to call the list anyway.
         */
-       if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU |
-                         FTRACE_OPS_FL_RCU) || FTRACE_FORCE_LIST_FUNC)
+       if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_RCU) ||
+           FTRACE_FORCE_LIST_FUNC)
                return ftrace_ops_list_func;
 
        return ftrace_ops_get_func(ops);
@@ -422,11 +398,6 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
        if (!core_kernel_data((unsigned long)ops))
                ops->flags |= FTRACE_OPS_FL_DYNAMIC;
 
-       if (ops->flags & FTRACE_OPS_FL_PER_CPU) {
-               if (per_cpu_ops_alloc(ops))
-                       return -ENOMEM;
-       }
-
        add_ftrace_ops(&ftrace_ops_list, ops);
 
        /* Always save the function, and reset at unregistering */
@@ -2727,11 +2698,6 @@ void __weak arch_ftrace_trampoline_free(struct ftrace_ops *ops)
 {
 }
 
-static void per_cpu_ops_free(struct ftrace_ops *ops)
-{
-       free_percpu(ops->disabled);
-}
-
 static void ftrace_startup_enable(int command)
 {
        if (saved_ftrace_func != ftrace_trace_function) {
@@ -2833,7 +2799,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
                 * not currently active, we can just free them
                 * without synchronizing all CPUs.
                 */
-               if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU))
+               if (ops->flags & FTRACE_OPS_FL_DYNAMIC)
                        goto free_ops;
 
                return 0;
@@ -2880,7 +2846,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
         * The same goes for freeing the per_cpu data of the per_cpu
         * ops.
         */
-       if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) {
+       if (ops->flags & FTRACE_OPS_FL_DYNAMIC) {
                /*
                 * We need to do a hard force of sched synchronization.
                 * This is because we use preempt_disable() to do RCU, but
@@ -2903,9 +2869,6 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
 
  free_ops:
                arch_ftrace_trampoline_free(ops);
-
-               if (ops->flags & FTRACE_OPS_FL_PER_CPU)
-                       per_cpu_ops_free(ops);
        }
 
        return 0;
@@ -5672,10 +5635,29 @@ static int ftrace_process_locs(struct module *mod,
        return ret;
 }
 
+struct ftrace_mod_func {
+       struct list_head        list;
+       char                    *name;
+       unsigned long           ip;
+       unsigned int            size;
+};
+
+struct ftrace_mod_map {
+       struct rcu_head         rcu;
+       struct list_head        list;
+       struct module           *mod;
+       unsigned long           start_addr;
+       unsigned long           end_addr;
+       struct list_head        funcs;
+       unsigned int            num_funcs;
+};
+
 #ifdef CONFIG_MODULES
 
 #define next_to_ftrace_page(p) container_of(p, struct ftrace_page, next)
 
+static LIST_HEAD(ftrace_mod_maps);
+
 static int referenced_filters(struct dyn_ftrace *rec)
 {
        struct ftrace_ops *ops;
@@ -5729,8 +5711,26 @@ static void clear_mod_from_hashes(struct ftrace_page *pg)
        mutex_unlock(&trace_types_lock);
 }
 
+static void ftrace_free_mod_map(struct rcu_head *rcu)
+{
+       struct ftrace_mod_map *mod_map = container_of(rcu, struct ftrace_mod_map, rcu);
+       struct ftrace_mod_func *mod_func;
+       struct ftrace_mod_func *n;
+
+       /* All the contents of mod_map are now not visible to readers */
+       list_for_each_entry_safe(mod_func, n, &mod_map->funcs, list) {
+               kfree(mod_func->name);
+               list_del(&mod_func->list);
+               kfree(mod_func);
+       }
+
+       kfree(mod_map);
+}
+
 void ftrace_release_mod(struct module *mod)
 {
+       struct ftrace_mod_map *mod_map;
+       struct ftrace_mod_map *n;
        struct dyn_ftrace *rec;
        struct ftrace_page **last_pg;
        struct ftrace_page *tmp_page = NULL;
@@ -5742,6 +5742,14 @@ void ftrace_release_mod(struct module *mod)
        if (ftrace_disabled)
                goto out_unlock;
 
+       list_for_each_entry_safe(mod_map, n, &ftrace_mod_maps, list) {
+               if (mod_map->mod == mod) {
+                       list_del_rcu(&mod_map->list);
+                       call_rcu_sched(&mod_map->rcu, ftrace_free_mod_map);
+                       break;
+               }
+       }
+
        /*
         * Each module has its own ftrace_pages, remove
         * them from the list.
@@ -5749,7 +5757,8 @@ void ftrace_release_mod(struct module *mod)
        last_pg = &ftrace_pages_start;
        for (pg = ftrace_pages_start; pg; pg = *last_pg) {
                rec = &pg->records[0];
-               if (within_module_core(rec->ip, mod)) {
+               if (within_module_core(rec->ip, mod) ||
+                   within_module_init(rec->ip, mod)) {
                        /*
                         * As core pages are first, the first
                         * page should never be a module page.
@@ -5818,7 +5827,8 @@ void ftrace_module_enable(struct module *mod)
                 * not part of this module, then skip this pg,
                 * which the "break" will do.
                 */
-               if (!within_module_core(rec->ip, mod))
+               if (!within_module_core(rec->ip, mod) &&
+                   !within_module_init(rec->ip, mod))
                        break;
 
                cnt = 0;
@@ -5863,23 +5873,245 @@ void ftrace_module_init(struct module *mod)
        ftrace_process_locs(mod, mod->ftrace_callsites,
                            mod->ftrace_callsites + mod->num_ftrace_callsites);
 }
+
+static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map,
+                               struct dyn_ftrace *rec)
+{
+       struct ftrace_mod_func *mod_func;
+       unsigned long symsize;
+       unsigned long offset;
+       char str[KSYM_SYMBOL_LEN];
+       char *modname;
+       const char *ret;
+
+       ret = kallsyms_lookup(rec->ip, &symsize, &offset, &modname, str);
+       if (!ret)
+               return;
+
+       mod_func = kmalloc(sizeof(*mod_func), GFP_KERNEL);
+       if (!mod_func)
+               return;
+
+       mod_func->name = kstrdup(str, GFP_KERNEL);
+       if (!mod_func->name) {
+               kfree(mod_func);
+               return;
+       }
+
+       mod_func->ip = rec->ip - offset;
+       mod_func->size = symsize;
+
+       mod_map->num_funcs++;
+
+       list_add_rcu(&mod_func->list, &mod_map->funcs);
+}
+
+static struct ftrace_mod_map *
+allocate_ftrace_mod_map(struct module *mod,
+                       unsigned long start, unsigned long end)
+{
+       struct ftrace_mod_map *mod_map;
+
+       mod_map = kmalloc(sizeof(*mod_map), GFP_KERNEL);
+       if (!mod_map)
+               return NULL;
+
+       mod_map->mod = mod;
+       mod_map->start_addr = start;
+       mod_map->end_addr = end;
+       mod_map->num_funcs = 0;
+
+       INIT_LIST_HEAD_RCU(&mod_map->funcs);
+
+       list_add_rcu(&mod_map->list, &ftrace_mod_maps);
+
+       return mod_map;
+}
+
+static const char *
+ftrace_func_address_lookup(struct ftrace_mod_map *mod_map,
+                          unsigned long addr, unsigned long *size,
+                          unsigned long *off, char *sym)
+{
+       struct ftrace_mod_func *found_func =  NULL;
+       struct ftrace_mod_func *mod_func;
+
+       list_for_each_entry_rcu(mod_func, &mod_map->funcs, list) {
+               if (addr >= mod_func->ip &&
+                   addr < mod_func->ip + mod_func->size) {
+                       found_func = mod_func;
+                       break;
+               }
+       }
+
+       if (found_func) {
+               if (size)
+                       *size = found_func->size;
+               if (off)
+                       *off = addr - found_func->ip;
+               if (sym)
+                       strlcpy(sym, found_func->name, KSYM_NAME_LEN);
+
+               return found_func->name;
+       }
+
+       return NULL;
+}
+
+const char *
+ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,
+                  unsigned long *off, char **modname, char *sym)
+{
+       struct ftrace_mod_map *mod_map;
+       const char *ret = NULL;
+
+       /* mod_map is freed via call_rcu_sched() */
+       preempt_disable();
+       list_for_each_entry_rcu(mod_map, &ftrace_mod_maps, list) {
+               ret = ftrace_func_address_lookup(mod_map, addr, size, off, sym);
+               if (ret) {
+                       if (modname)
+                               *modname = mod_map->mod->name;
+                       break;
+               }
+       }
+       preempt_enable();
+
+       return ret;
+}
+
+int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
+                          char *type, char *name,
+                          char *module_name, int *exported)
+{
+       struct ftrace_mod_map *mod_map;
+       struct ftrace_mod_func *mod_func;
+
+       preempt_disable();
+       list_for_each_entry_rcu(mod_map, &ftrace_mod_maps, list) {
+
+               if (symnum >= mod_map->num_funcs) {
+                       symnum -= mod_map->num_funcs;
+                       continue;
+               }
+
+               list_for_each_entry_rcu(mod_func, &mod_map->funcs, list) {
+                       if (symnum > 1) {
+                               symnum--;
+                               continue;
+                       }
+
+                       *value = mod_func->ip;
+                       *type = 'T';
+                       strlcpy(name, mod_func->name, KSYM_NAME_LEN);
+                       strlcpy(module_name, mod_map->mod->name, MODULE_NAME_LEN);
+                       *exported = 1;
+                       preempt_enable();
+                       return 0;
+               }
+               WARN_ON(1);
+               break;
+       }
+       preempt_enable();
+       return -ERANGE;
+}
+
+#else
+static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map,
+                               struct dyn_ftrace *rec) { }
+static inline struct ftrace_mod_map *
+allocate_ftrace_mod_map(struct module *mod,
+                       unsigned long start, unsigned long end)
+{
+       return NULL;
+}
 #endif /* CONFIG_MODULES */
 
-void __init ftrace_free_init_mem(void)
+struct ftrace_init_func {
+       struct list_head list;
+       unsigned long ip;
+};
+
+/* Clear any init ips from hashes */
+static void
+clear_func_from_hash(struct ftrace_init_func *func, struct ftrace_hash *hash)
+{
+       struct ftrace_func_entry *entry;
+
+       if (ftrace_hash_empty(hash))
+               return;
+
+       entry = __ftrace_lookup_ip(hash, func->ip);
+
+       /*
+        * Do not allow this rec to match again.
+        * Yeah, it may waste some memory, but will be removed
+        * if/when the hash is modified again.
+        */
+       if (entry)
+               entry->ip = 0;
+}
+
+static void
+clear_func_from_hashes(struct ftrace_init_func *func)
+{
+       struct trace_array *tr;
+
+       mutex_lock(&trace_types_lock);
+       list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+               if (!tr->ops || !tr->ops->func_hash)
+                       continue;
+               mutex_lock(&tr->ops->func_hash->regex_lock);
+               clear_func_from_hash(func, tr->ops->func_hash->filter_hash);
+               clear_func_from_hash(func, tr->ops->func_hash->notrace_hash);
+               mutex_unlock(&tr->ops->func_hash->regex_lock);
+       }
+       mutex_unlock(&trace_types_lock);
+}
+
+static void add_to_clear_hash_list(struct list_head *clear_list,
+                                  struct dyn_ftrace *rec)
+{
+       struct ftrace_init_func *func;
+
+       func = kmalloc(sizeof(*func), GFP_KERNEL);
+       if (!func) {
+               WARN_ONCE(1, "alloc failure, ftrace filter could be stale\n");
+               return;
+       }
+
+       func->ip = rec->ip;
+       list_add(&func->list, clear_list);
+}
+
+void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr)
 {
-       unsigned long start = (unsigned long)(&__init_begin);
-       unsigned long end = (unsigned long)(&__init_end);
+       unsigned long start = (unsigned long)(start_ptr);
+       unsigned long end = (unsigned long)(end_ptr);
        struct ftrace_page **last_pg = &ftrace_pages_start;
        struct ftrace_page *pg;
        struct dyn_ftrace *rec;
        struct dyn_ftrace key;
+       struct ftrace_mod_map *mod_map = NULL;
+       struct ftrace_init_func *func, *func_next;
+       struct list_head clear_hash;
        int order;
 
+       INIT_LIST_HEAD(&clear_hash);
+
        key.ip = start;
        key.flags = end;        /* overload flags, as it is unsigned long */
 
        mutex_lock(&ftrace_lock);
 
+       /*
+        * If we are freeing module init memory, then check if
+        * any tracer is active. If so, we need to save a mapping of
+        * the module functions being freed with the address.
+        */
+       if (mod && ftrace_ops_list != &ftrace_list_end)
+               mod_map = allocate_ftrace_mod_map(mod, start, end);
+
        for (pg = ftrace_pages_start; pg; last_pg = &pg->next, pg = *last_pg) {
                if (end < pg->records[0].ip ||
                    start >= (pg->records[pg->index - 1].ip + MCOUNT_INSN_SIZE))
@@ -5890,6 +6122,13 @@ void __init ftrace_free_init_mem(void)
                              ftrace_cmp_recs);
                if (!rec)
                        continue;
+
+               /* rec will be cleared from hashes after ftrace_lock unlock */
+               add_to_clear_hash_list(&clear_hash, rec);
+
+               if (mod_map)
+                       save_ftrace_mod_rec(mod_map, rec);
+
                pg->index--;
                ftrace_update_tot_cnt--;
                if (!pg->index) {
@@ -5908,6 +6147,19 @@ void __init ftrace_free_init_mem(void)
                goto again;
        }
        mutex_unlock(&ftrace_lock);
+
+       list_for_each_entry_safe(func, func_next, &clear_hash, list) {
+               clear_func_from_hashes(func);
+               kfree(func);
+       }
+}
+
+void __init ftrace_free_init_mem(void)
+{
+       void *start = (void *)(&__init_begin);
+       void *end = (void *)(&__init_end);
+
+       ftrace_free_mem(NULL, start, end);
 }
 
 void __init ftrace_init(void)
@@ -6063,10 +6315,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
                 * If any of the above fails then the op->func() is not executed.
                 */
                if ((!(op->flags & FTRACE_OPS_FL_RCU) || rcu_is_watching()) &&
-                   (!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
-                    !ftrace_function_local_disabled(op)) &&
                    ftrace_ops_test(op, ip, regs)) {
-                   
                        if (FTRACE_WARN_ON(!op->func)) {
                                pr_warn("op=%p %pS\n", op, op);
                                goto out;
@@ -6124,10 +6373,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
 
        preempt_disable_notrace();
 
-       if (!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
-           !ftrace_function_local_disabled(op)) {
-               op->func(ip, parent_ip, op, regs);
-       }
+       op->func(ip, parent_ip, op, regs);
 
        preempt_enable_notrace();
        trace_clear_recursion(bit);
@@ -6151,7 +6397,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops)
         * or does per cpu logic, then we need to call the assist handler.
         */
        if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE) ||
-           ops->flags & (FTRACE_OPS_FL_RCU | FTRACE_OPS_FL_PER_CPU))
+           ops->flags & FTRACE_OPS_FL_RCU)
                return ftrace_ops_assist_func;
 
        return ops->func;