ftrace: Fix up trampoline accounting with looping on hash ops
[firefly-linux-kernel-4.4.55.git] / kernel / trace / ftrace.c
index 37f9e90d241c64906c5aaf7bf4c494d592e3e518..92376aeac4a71e3a4fb837f952ebd02eda37a0c9 100644 (file)
@@ -1507,25 +1507,38 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
 static void ftrace_remove_tramp(struct ftrace_ops *ops,
                                struct dyn_ftrace *rec)
 {
-       struct ftrace_func_entry *entry;
-
-       entry = ftrace_lookup_ip(ops->tramp_hash, rec->ip);
-       if (!entry)
+       /* If TRAMP is not set, no ops should have a trampoline for this */
+       if (!(rec->flags & FTRACE_FL_TRAMP))
                return;
 
+       rec->flags &= ~FTRACE_FL_TRAMP;
+
+       if ((!ftrace_hash_empty(ops->func_hash->filter_hash) &&
+            !ftrace_lookup_ip(ops->func_hash->filter_hash, rec->ip)) ||
+           ftrace_lookup_ip(ops->func_hash->notrace_hash, rec->ip))
+               return;
        /*
         * The tramp_hash entry will be removed at time
         * of update.
         */
        ops->nr_trampolines--;
-       rec->flags &= ~FTRACE_FL_TRAMP;
 }
 
-static void ftrace_clear_tramps(struct dyn_ftrace *rec)
+static void ftrace_clear_tramps(struct dyn_ftrace *rec, struct ftrace_ops *ops)
 {
        struct ftrace_ops *op;
 
+       /* If TRAMP is not set, no ops should have a trampoline for this */
+       if (!(rec->flags & FTRACE_FL_TRAMP))
+               return;
+
        do_for_each_ftrace_op(op, ftrace_ops_list) {
+               /*
+                * This function is called to clear other tramps
+                * not the one that is being updated.
+                */
+               if (op == ops)
+                       continue;
                if (op->nr_trampolines)
                        ftrace_remove_tramp(op, rec);
        } while_for_each_ftrace_op(op);
@@ -1626,13 +1639,10 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
                                /*
                                 * If we are adding another function callback
                                 * to this function, and the previous had a
-                                * trampoline used, then we need to go back to
-                                * the default trampoline.
+                                * custom trampoline in use, then we need to go
+                                * back to the default trampoline.
                                 */
-                               rec->flags &= ~FTRACE_FL_TRAMP;
-
-                               /* remove trampolines from any ops for this rec */
-                               ftrace_clear_tramps(rec);
+                               ftrace_clear_tramps(rec, ops);
                        }
 
                        /*
@@ -1935,8 +1945,8 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec)
        if (rec->flags & FTRACE_FL_TRAMP) {
                ops = ftrace_find_tramp_ops_new(rec);
                if (FTRACE_WARN_ON(!ops || !ops->trampoline)) {
-                       pr_warning("Bad trampoline accounting at: %p (%pS)\n",
-                                   (void *)rec->ip, (void *)rec->ip);
+                       pr_warn("Bad trampoline accounting at: %p (%pS) (%lx)\n",
+                               (void *)rec->ip, (void *)rec->ip, rec->flags);
                        /* Ftrace is shutting down, return anything */
                        return (unsigned long)FTRACE_ADDR;
                }
@@ -2266,7 +2276,10 @@ static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops)
        } while_for_each_ftrace_rec();
 
        /* The number of recs in the hash must match nr_trampolines */
-       FTRACE_WARN_ON(ops->tramp_hash->count != ops->nr_trampolines);
+       if (FTRACE_WARN_ON(ops->tramp_hash->count != ops->nr_trampolines))
+               pr_warn("count=%ld trampolines=%d\n",
+                       ops->tramp_hash->count,
+                       ops->nr_trampolines);
 
        return 0;
 }