Skip to content

Commit bce0b6c

Browse files
committed
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: [email protected] # 3.16+ (apply after 3.17-rc4 is out) Signed-off-by: Steven Rostedt <[email protected]>
1 parent 8426191 commit bce0b6c

File tree

1 file changed

+28
-15
lines changed

1 file changed

+28
-15
lines changed

kernel/trace/ftrace.c

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,25 +1507,38 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
15071507
static void ftrace_remove_tramp(struct ftrace_ops *ops,
15081508
struct dyn_ftrace *rec)
15091509
{
1510-
struct ftrace_func_entry *entry;
1511-
1512-
entry = ftrace_lookup_ip(ops->tramp_hash, rec->ip);
1513-
if (!entry)
1510+
/* If TRAMP is not set, no ops should have a trampoline for this */
1511+
if (!(rec->flags & FTRACE_FL_TRAMP))
15141512
return;
15151513

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;
15161520
/*
15171521
* The tramp_hash entry will be removed at time
15181522
* of update.
15191523
*/
15201524
ops->nr_trampolines--;
1521-
rec->flags &= ~FTRACE_FL_TRAMP;
15221525
}
15231526

1524-
static void ftrace_clear_tramps(struct dyn_ftrace *rec)
1527+
static void ftrace_clear_tramps(struct dyn_ftrace *rec, struct ftrace_ops *ops)
15251528
{
15261529
struct ftrace_ops *op;
15271530

1531+
/* If TRAMP is not set, no ops should have a trampoline for this */
1532+
if (!(rec->flags & FTRACE_FL_TRAMP))
1533+
return;
1534+
15281535
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;
15291542
if (op->nr_trampolines)
15301543
ftrace_remove_tramp(op, rec);
15311544
} while_for_each_ftrace_op(op);
@@ -1626,13 +1639,10 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
16261639
/*
16271640
* If we are adding another function callback
16281641
* to this function, and the previous had a
1629-
* trampoline used, then we need to go back to
1630-
* the default trampoline.
1642+
* custom trampoline in use, then we need to go
1643+
* back to the default trampoline.
16311644
*/
1632-
rec->flags &= ~FTRACE_FL_TRAMP;
1633-
1634-
/* remove trampolines from any ops for this rec */
1635-
ftrace_clear_tramps(rec);
1645+
ftrace_clear_tramps(rec, ops);
16361646
}
16371647

16381648
/*
@@ -1935,8 +1945,8 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec)
19351945
if (rec->flags & FTRACE_FL_TRAMP) {
19361946
ops = ftrace_find_tramp_ops_new(rec);
19371947
if (FTRACE_WARN_ON(!ops || !ops->trampoline)) {
1938-
pr_warning("Bad trampoline accounting at: %p (%pS)\n",
1939-
(void *)rec->ip, (void *)rec->ip);
1948+
pr_warn("Bad trampoline accounting at: %p (%pS) (%lx)\n",
1949+
(void *)rec->ip, (void *)rec->ip, rec->flags);
19401950
/* Ftrace is shutting down, return anything */
19411951
return (unsigned long)FTRACE_ADDR;
19421952
}
@@ -2266,7 +2276,10 @@ static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops)
22662276
} while_for_each_ftrace_rec();
22672277

22682278
/* 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);
22702283

22712284
return 0;
22722285
}

0 commit comments

Comments
 (0)