ftrace: Fix checking of trampoline ftrace_ops in finding trampoline
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>
Fri, 24 Oct 2014 18:48:35 +0000 (14:48 -0400)
committerSteven Rostedt <rostedt@goodmis.org>
Fri, 24 Oct 2014 20:53:11 +0000 (16:53 -0400)
When modifying code, ftrace has several checks to make sure things
are being done correctly. One of them is to make sure any code it
modifies is exactly what it expects it to be before it modifies it.
In order to do so with the new trampoline logic, it must be able
to find out what trampoline a function is hooked to in order to
see if the code that hooks to it is what's expected.

The logic to find the trampoline from a record (accounting descriptor
for a function that is hooked) needs to only look at the "old_hash"
of an ops that is being modified. The old_hash is the list of function
an ops is hooked to before its update. Since a record would only be
pointing to an ops that is being modified if it was already hooked
before.

Currently, it can pick a modified ops based on its new functions it
will be hooked to, and this picks the wrong trampoline and causes
the check to fail, disabling ftrace.

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
ftrace: squash into ordering of ops for modification

kernel/trace/ftrace.c

index 483b8c1b1de0589b0ac2ea2646b1782a4e0ddf00..31c90fec415899bfb40927bbbcc8ad29657317ab 100644 (file)
@@ -1925,8 +1925,16 @@ ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec)
         * when we are adding another op to the rec or removing the
         * current one. Thus, if the op is being added, we can
         * ignore it because it hasn't attached itself to the rec
-        * yet. That means we just need to find the op that has a
-        * trampoline and is not beeing added.
+        * yet.
+        *
+        * If an ops is being modified (hooking to different functions)
+        * then we don't care about the new functions that are being
+        * added, just the old ones (that are probably being removed).
+        *
+        * If we are adding an ops to a function that already is using
+        * a trampoline, it needs to be removed (trampolines are only
+        * for single ops connected), then an ops that is not being
+        * modified also needs to be checked.
         */
        do_for_each_ftrace_op(op, ftrace_ops_list) {
 
@@ -1940,17 +1948,23 @@ ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec)
                if (op->flags & FTRACE_OPS_FL_ADDING)
                        continue;
 
+
                /*
-                * If the ops is not being added and has a trampoline,
-                * then it must be the one that we want!
+                * If the ops is being modified and is in the old
+                * hash, then it is probably being removed from this
+                * function.
                 */
-               if (hash_contains_ip(ip, op->func_hash))
-                       return op;
-
-               /* If the ops is being modified, it may be in the old hash. */
                if ((op->flags & FTRACE_OPS_FL_MODIFYING) &&
                    hash_contains_ip(ip, &op->old_hash))
                        return op;
+               /*
+                * If the ops is not being added or modified, and it's
+                * in its normal filter hash, then this must be the one
+                * we want!
+                */
+               if (!(op->flags & FTRACE_OPS_FL_MODIFYING) &&
+                   hash_contains_ip(ip, op->func_hash))
+                       return op;
 
        } while_for_each_ftrace_op(op);