aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2014-04-30 22:35:48 -0400
committerSteven Rostedt <rostedt@goodmis.org>2014-06-30 10:09:53 -0400
commit4fbb48cb110be653adcd97a87506e0ba8c16d585 (patch)
treefa46f2abc3cf7a9eaf2148e4b980261ba0dae513 /kernel/trace
parent4c834452aad01531db949414f94f817a86348d59 (diff)
ftrace: Allow no regs if no more callbacks require it
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>
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/ftrace.c32
1 files changed, 32 insertions, 0 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 5b372e3ed675..b867c647e5bc 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1492,6 +1492,26 @@ int ftrace_text_reserved(const void *start, const void *end)
1492 return (int)!!ret; 1492 return (int)!!ret;
1493} 1493}
1494 1494
1495/* Test if ops registered to this rec needs regs */
1496static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
1497{
1498 struct ftrace_ops *ops;
1499 bool keep_regs = false;
1500
1501 for (ops = ftrace_ops_list;
1502 ops != &ftrace_list_end; ops = ops->next) {
1503 /* pass rec in as regs to have non-NULL val */
1504 if (ftrace_ops_test(ops, rec->ip, rec)) {
1505 if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) {
1506 keep_regs = true;
1507 break;
1508 }
1509 }
1510 }
1511
1512 return keep_regs;
1513}
1514
1495static void __ftrace_hash_rec_update(struct ftrace_ops *ops, 1515static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1496 int filter_hash, 1516 int filter_hash,
1497 bool inc) 1517 bool inc)
@@ -1584,6 +1604,18 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1584 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0)) 1604 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0))
1585 return; 1605 return;
1586 rec->flags--; 1606 rec->flags--;
1607 /*
1608 * If the rec had REGS enabled and the ops that is
1609 * being removed had REGS set, then see if there is
1610 * still any ops for this record that wants regs.
1611 * If not, we can stop recording them.
1612 */
1613 if ((rec->flags & ~FTRACE_FL_MASK) > 0 &&
1614 rec->flags & FTRACE_FL_REGS &&
1615 ops->flags & FTRACE_OPS_FL_SAVE_REGS) {
1616 if (!test_rec_ops_needs_regs(rec))
1617 rec->flags &= ~FTRACE_FL_REGS;
1618 }
1587 } 1619 }
1588 count++; 1620 count++;
1589 /* Shortcut, if we handled all records, we are done. */ 1621 /* Shortcut, if we handled all records, we are done. */