OSDN Git Service

ftrace: Allow no regs if no more callbacks require it
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>
Thu, 1 May 2014 02:35:48 +0000 (22:35 -0400)
committerSteven Rostedt <rostedt@goodmis.org>
Mon, 30 Jun 2014 14:09:53 +0000 (10:09 -0400)
When registering a function callback for the function tracer, the ops
can specify if it wants to save full regs (like an interrupt would)
for each function that it traces, or if it does not care about regs
and just wants to have the fastest return possible.

Once a ops has registered a function, if other ops register that
function they all will receive the regs too. That's because it does
the work once, it does it for everyone.

Now if the ops wanting regs unregisters the function so that there's
only ops left that do not care about regs, those ops will still
continue getting regs and going through the work for it on that
function. This is because the disabling of the rec counter only
sees the ops registered, and does not see the ops that are still
attached, and does not know if the current ops that are still attached
want regs or not. To play it safe, it just keeps regs being processed
until no function is registered anymore.

Instead of doing that, check the ops that are still registered for that
function and if none want regs for it anymore, then disable the
processing of regs.

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
kernel/trace/ftrace.c

index 5b372e3..b867c64 100644 (file)
@@ -1492,6 +1492,26 @@ int ftrace_text_reserved(const void *start, const void *end)
        return (int)!!ret;
 }
 
+/* Test if ops registered to this rec needs regs */
+static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
+{
+       struct ftrace_ops *ops;
+       bool keep_regs = false;
+
+       for (ops = ftrace_ops_list;
+            ops != &ftrace_list_end; ops = ops->next) {
+               /* pass rec in as regs to have non-NULL val */
+               if (ftrace_ops_test(ops, rec->ip, rec)) {
+                       if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) {
+                               keep_regs = true;
+                               break;
+                       }
+               }
+       }
+
+       return  keep_regs;
+}
+
 static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
                                     int filter_hash,
                                     bool inc)
@@ -1584,6 +1604,18 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
                        if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0))
                                return;
                        rec->flags--;
+                       /*
+                        * If the rec had REGS enabled and the ops that is
+                        * being removed had REGS set, then see if there is
+                        * still any ops for this record that wants regs.
+                        * If not, we can stop recording them.
+                        */
+                       if ((rec->flags & ~FTRACE_FL_MASK) > 0 &&
+                           rec->flags & FTRACE_FL_REGS &&
+                           ops->flags & FTRACE_OPS_FL_SAVE_REGS) {
+                               if (!test_rec_ops_needs_regs(rec))
+                                       rec->flags &= ~FTRACE_FL_REGS;
+                       }
                }
                count++;
                /* Shortcut, if we handled all records, we are done. */