aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2014-08-20 23:57:04 -0400
committerSteven Rostedt <rostedt@goodmis.org>2014-08-22 15:24:12 -0400
commitbce0b6c51ac76fc0e763262a6c2a9d05e486f0d8 (patch)
tree1510bdb140f99c06c660b3606f9c06bf274307fc /kernel/trace
parent84261912ebee41269004e8a9f3614ba38ef6b206 (diff)
ftrace: Fix up trampoline accounting with looping on hash ops
Now that a ftrace_hash can be shared by multiple ftrace_ops, they can dec the rec->flags by more than once (one per those that share the ftrace_hash). This means that the tramp_hash may not have a hash item when it was added. For example, if two ftrace_ops share a hash for a ftrace record, and the first ops has a trampoline, when it adds itself it will set the rec->flags TRAMP flag and increments its nr_trampolines counter. When the second ops is added, it must clear that tramp flag but also decrement the other ops that shares its hash. As the update to the function callbacks has not yet been performed, the other ops will not have the tramp hash set yet and it can not be used to know to decrement its nr_trampolines. Luckily, the tramp_hash does not need to be used. As the ftrace_mutex is held, a ops with a trampoline to a record during an update of another ops that shares the record will have its func_hash pointing to it. Since a trampoline can only be set for a record if only one ops is attached to it, we can just check if the record has a trampoline (the FTRACE_FL_TRAMP flag is set) and then find the ops that has this record in its hashes. Also added some output to help debug when things go wrong. Cc: stable@vger.kernel.org # 3.16+ (apply after 3.17-rc4 is out) Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/ftrace.c43
1 files changed, 28 insertions, 15 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 37f9e90d241c..92376aeac4a7 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1507,25 +1507,38 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
1507static void ftrace_remove_tramp(struct ftrace_ops *ops, 1507static void ftrace_remove_tramp(struct ftrace_ops *ops,
1508 struct dyn_ftrace *rec) 1508 struct dyn_ftrace *rec)
1509{ 1509{
1510 struct ftrace_func_entry *entry; 1510 /* If TRAMP is not set, no ops should have a trampoline for this */
1511 1511 if (!(rec->flags & FTRACE_FL_TRAMP))
1512 entry = ftrace_lookup_ip(ops->tramp_hash, rec->ip);
1513 if (!entry)
1514 return; 1512 return;
1515 1513
1514 rec->flags &= ~FTRACE_FL_TRAMP;
1515
1516 if ((!ftrace_hash_empty(ops->func_hash->filter_hash) &&
1517 !ftrace_lookup_ip(ops->func_hash->filter_hash, rec->ip)) ||
1518 ftrace_lookup_ip(ops->func_hash->notrace_hash, rec->ip))
1519 return;
1516 /* 1520 /*
1517 * The tramp_hash entry will be removed at time 1521 * The tramp_hash entry will be removed at time
1518 * of update. 1522 * of update.
1519 */ 1523 */
1520 ops->nr_trampolines--; 1524 ops->nr_trampolines--;
1521 rec->flags &= ~FTRACE_FL_TRAMP;
1522} 1525}
1523 1526
1524static void ftrace_clear_tramps(struct dyn_ftrace *rec) 1527static void ftrace_clear_tramps(struct dyn_ftrace *rec, struct ftrace_ops *ops)
1525{ 1528{
1526 struct ftrace_ops *op; 1529 struct ftrace_ops *op;
1527 1530
1531 /* If TRAMP is not set, no ops should have a trampoline for this */
1532 if (!(rec->flags & FTRACE_FL_TRAMP))
1533 return;
1534
1528 do_for_each_ftrace_op(op, ftrace_ops_list) { 1535 do_for_each_ftrace_op(op, ftrace_ops_list) {
1536 /*
1537 * This function is called to clear other tramps
1538 * not the one that is being updated.
1539 */
1540 if (op == ops)
1541 continue;
1529 if (op->nr_trampolines) 1542 if (op->nr_trampolines)
1530 ftrace_remove_tramp(op, rec); 1543 ftrace_remove_tramp(op, rec);
1531 } while_for_each_ftrace_op(op); 1544 } while_for_each_ftrace_op(op);
@@ -1626,13 +1639,10 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1626 /* 1639 /*
1627 * If we are adding another function callback 1640 * If we are adding another function callback
1628 * to this function, and the previous had a 1641 * to this function, and the previous had a
1629 * trampoline used, then we need to go back to 1642 * custom trampoline in use, then we need to go
1630 * the default trampoline. 1643 * back to the default trampoline.
1631 */ 1644 */
1632 rec->flags &= ~FTRACE_FL_TRAMP; 1645 ftrace_clear_tramps(rec, ops);
1633
1634 /* remove trampolines from any ops for this rec */
1635 ftrace_clear_tramps(rec);
1636 } 1646 }
1637 1647
1638 /* 1648 /*
@@ -1935,8 +1945,8 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec)
1935 if (rec->flags & FTRACE_FL_TRAMP) { 1945 if (rec->flags & FTRACE_FL_TRAMP) {
1936 ops = ftrace_find_tramp_ops_new(rec); 1946 ops = ftrace_find_tramp_ops_new(rec);
1937 if (FTRACE_WARN_ON(!ops || !ops->trampoline)) { 1947 if (FTRACE_WARN_ON(!ops || !ops->trampoline)) {
1938 pr_warning("Bad trampoline accounting at: %p (%pS)\n", 1948 pr_warn("Bad trampoline accounting at: %p (%pS) (%lx)\n",
1939 (void *)rec->ip, (void *)rec->ip); 1949 (void *)rec->ip, (void *)rec->ip, rec->flags);
1940 /* Ftrace is shutting down, return anything */ 1950 /* Ftrace is shutting down, return anything */
1941 return (unsigned long)FTRACE_ADDR; 1951 return (unsigned long)FTRACE_ADDR;
1942 } 1952 }
@@ -2266,7 +2276,10 @@ static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops)
2266 } while_for_each_ftrace_rec(); 2276 } while_for_each_ftrace_rec();
2267 2277
2268 /* The number of recs in the hash must match nr_trampolines */ 2278 /* The number of recs in the hash must match nr_trampolines */
2269 FTRACE_WARN_ON(ops->tramp_hash->count != ops->nr_trampolines); 2279 if (FTRACE_WARN_ON(ops->tramp_hash->count != ops->nr_trampolines))
2280 pr_warn("count=%ld trampolines=%d\n",
2281 ops->tramp_hash->count,
2282 ops->nr_trampolines);
2270 2283
2271 return 0; 2284 return 0;
2272} 2285}