diff options
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/ftrace.c | 239 |
1 files changed, 84 insertions, 155 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index e43c793093e5..d325a1e76554 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -1373,6 +1373,21 @@ update: | |||
| 1373 | return 0; | 1373 | return 0; |
| 1374 | } | 1374 | } |
| 1375 | 1375 | ||
| 1376 | static bool hash_contains_ip(unsigned long ip, | ||
| 1377 | struct ftrace_ops_hash *hash) | ||
| 1378 | { | ||
| 1379 | /* | ||
| 1380 | * The function record is a match if it exists in the filter | ||
| 1381 | * hash and not in the notrace hash. Note, an emty hash is | ||
| 1382 | * considered a match for the filter hash, but an empty | ||
| 1383 | * notrace hash is considered not in the notrace hash. | ||
| 1384 | */ | ||
| 1385 | return (ftrace_hash_empty(hash->filter_hash) || | ||
| 1386 | ftrace_lookup_ip(hash->filter_hash, ip)) && | ||
| 1387 | (ftrace_hash_empty(hash->notrace_hash) || | ||
| 1388 | !ftrace_lookup_ip(hash->notrace_hash, ip)); | ||
| 1389 | } | ||
| 1390 | |||
| 1376 | /* | 1391 | /* |
| 1377 | * Test the hashes for this ops to see if we want to call | 1392 | * Test the hashes for this ops to see if we want to call |
| 1378 | * the ops->func or not. | 1393 | * the ops->func or not. |
| @@ -1388,8 +1403,7 @@ update: | |||
| 1388 | static int | 1403 | static int |
| 1389 | ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) | 1404 | ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) |
| 1390 | { | 1405 | { |
| 1391 | struct ftrace_hash *filter_hash; | 1406 | struct ftrace_ops_hash hash; |
| 1392 | struct ftrace_hash *notrace_hash; | ||
| 1393 | int ret; | 1407 | int ret; |
| 1394 | 1408 | ||
| 1395 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | 1409 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS |
| @@ -1402,13 +1416,10 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) | |||
| 1402 | return 0; | 1416 | return 0; |
| 1403 | #endif | 1417 | #endif |
| 1404 | 1418 | ||
| 1405 | filter_hash = rcu_dereference_raw_notrace(ops->func_hash->filter_hash); | 1419 | hash.filter_hash = rcu_dereference_raw_notrace(ops->func_hash->filter_hash); |
| 1406 | notrace_hash = rcu_dereference_raw_notrace(ops->func_hash->notrace_hash); | 1420 | hash.notrace_hash = rcu_dereference_raw_notrace(ops->func_hash->notrace_hash); |
| 1407 | 1421 | ||
| 1408 | if ((ftrace_hash_empty(filter_hash) || | 1422 | if (hash_contains_ip(ip, &hash)) |
| 1409 | ftrace_lookup_ip(filter_hash, ip)) && | ||
| 1410 | (ftrace_hash_empty(notrace_hash) || | ||
| 1411 | !ftrace_lookup_ip(notrace_hash, ip))) | ||
| 1412 | ret = 1; | 1423 | ret = 1; |
| 1413 | else | 1424 | else |
| 1414 | ret = 0; | 1425 | ret = 0; |
| @@ -1520,46 +1531,6 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec) | |||
| 1520 | return keep_regs; | 1531 | return keep_regs; |
| 1521 | } | 1532 | } |
| 1522 | 1533 | ||
| 1523 | static void ftrace_remove_tramp(struct ftrace_ops *ops, | ||
| 1524 | struct dyn_ftrace *rec) | ||
| 1525 | { | ||
| 1526 | /* If TRAMP is not set, no ops should have a trampoline for this */ | ||
| 1527 | if (!(rec->flags & FTRACE_FL_TRAMP)) | ||
| 1528 | return; | ||
| 1529 | |||
| 1530 | rec->flags &= ~FTRACE_FL_TRAMP; | ||
| 1531 | |||
| 1532 | if ((!ftrace_hash_empty(ops->func_hash->filter_hash) && | ||
| 1533 | !ftrace_lookup_ip(ops->func_hash->filter_hash, rec->ip)) || | ||
| 1534 | ftrace_lookup_ip(ops->func_hash->notrace_hash, rec->ip)) | ||
| 1535 | return; | ||
| 1536 | /* | ||
| 1537 | * The tramp_hash entry will be removed at time | ||
| 1538 | * of update. | ||
| 1539 | */ | ||
| 1540 | ops->nr_trampolines--; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | static void ftrace_clear_tramps(struct dyn_ftrace *rec, struct ftrace_ops *ops) | ||
| 1544 | { | ||
| 1545 | struct ftrace_ops *op; | ||
| 1546 | |||
| 1547 | /* If TRAMP is not set, no ops should have a trampoline for this */ | ||
| 1548 | if (!(rec->flags & FTRACE_FL_TRAMP)) | ||
| 1549 | return; | ||
| 1550 | |||
| 1551 | do_for_each_ftrace_op(op, ftrace_ops_list) { | ||
| 1552 | /* | ||
| 1553 | * This function is called to clear other tramps | ||
| 1554 | * not the one that is being updated. | ||
| 1555 | */ | ||
| 1556 | if (op == ops) | ||
| 1557 | continue; | ||
| 1558 | if (op->nr_trampolines) | ||
| 1559 | ftrace_remove_tramp(op, rec); | ||
| 1560 | } while_for_each_ftrace_op(op); | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | 1534 | static void __ftrace_hash_rec_update(struct ftrace_ops *ops, |
| 1564 | int filter_hash, | 1535 | int filter_hash, |
| 1565 | bool inc) | 1536 | bool inc) |
| @@ -1648,18 +1619,16 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | |||
| 1648 | * function, and the ops has a trampoline registered | 1619 | * function, and the ops has a trampoline registered |
| 1649 | * for it, then we can call it directly. | 1620 | * for it, then we can call it directly. |
| 1650 | */ | 1621 | */ |
| 1651 | if (ftrace_rec_count(rec) == 1 && ops->trampoline) { | 1622 | if (ftrace_rec_count(rec) == 1 && ops->trampoline) |
| 1652 | rec->flags |= FTRACE_FL_TRAMP; | 1623 | rec->flags |= FTRACE_FL_TRAMP; |
| 1653 | ops->nr_trampolines++; | 1624 | else |
| 1654 | } else { | ||
| 1655 | /* | 1625 | /* |
| 1656 | * If we are adding another function callback | 1626 | * If we are adding another function callback |
| 1657 | * to this function, and the previous had a | 1627 | * to this function, and the previous had a |
| 1658 | * custom trampoline in use, then we need to go | 1628 | * custom trampoline in use, then we need to go |
| 1659 | * back to the default trampoline. | 1629 | * back to the default trampoline. |
| 1660 | */ | 1630 | */ |
| 1661 | ftrace_clear_tramps(rec, ops); | 1631 | rec->flags &= ~FTRACE_FL_TRAMP; |
| 1662 | } | ||
| 1663 | 1632 | ||
| 1664 | /* | 1633 | /* |
| 1665 | * If any ops wants regs saved for this function | 1634 | * If any ops wants regs saved for this function |
| @@ -1672,9 +1641,6 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | |||
| 1672 | return; | 1641 | return; |
| 1673 | rec->flags--; | 1642 | rec->flags--; |
| 1674 | 1643 | ||
| 1675 | if (ops->trampoline && !ftrace_rec_count(rec)) | ||
| 1676 | ftrace_remove_tramp(ops, rec); | ||
| 1677 | |||
| 1678 | /* | 1644 | /* |
| 1679 | * If the rec had REGS enabled and the ops that is | 1645 | * If the rec had REGS enabled and the ops that is |
| 1680 | * being removed had REGS set, then see if there is | 1646 | * being removed had REGS set, then see if there is |
| @@ -1689,6 +1655,17 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | |||
| 1689 | } | 1655 | } |
| 1690 | 1656 | ||
| 1691 | /* | 1657 | /* |
| 1658 | * If the rec had TRAMP enabled, then it needs to | ||
| 1659 | * be cleared. As TRAMP can only be enabled iff | ||
| 1660 | * there is only a single ops attached to it. | ||
| 1661 | * In otherwords, always disable it on decrementing. | ||
| 1662 | * In the future, we may set it if rec count is | ||
| 1663 | * decremented to one, and the ops that is left | ||
| 1664 | * has a trampoline. | ||
| 1665 | */ | ||
| 1666 | rec->flags &= ~FTRACE_FL_TRAMP; | ||
| 1667 | |||
| 1668 | /* | ||
| 1692 | * flags will be cleared in ftrace_check_record() | 1669 | * flags will be cleared in ftrace_check_record() |
| 1693 | * if rec count is zero. | 1670 | * if rec count is zero. |
| 1694 | */ | 1671 | */ |
| @@ -1910,15 +1887,14 @@ static struct ftrace_ops * | |||
| 1910 | ftrace_find_tramp_ops_any(struct dyn_ftrace *rec) | 1887 | ftrace_find_tramp_ops_any(struct dyn_ftrace *rec) |
| 1911 | { | 1888 | { |
| 1912 | struct ftrace_ops *op; | 1889 | struct ftrace_ops *op; |
| 1890 | unsigned long ip = rec->ip; | ||
| 1913 | 1891 | ||
| 1914 | do_for_each_ftrace_op(op, ftrace_ops_list) { | 1892 | do_for_each_ftrace_op(op, ftrace_ops_list) { |
| 1915 | 1893 | ||
| 1916 | if (!op->trampoline) | 1894 | if (!op->trampoline) |
| 1917 | continue; | 1895 | continue; |
| 1918 | 1896 | ||
| 1919 | if (ftrace_lookup_ip(op->func_hash->filter_hash, rec->ip) && | 1897 | if (hash_contains_ip(ip, op->func_hash)) |
| 1920 | (ftrace_hash_empty(op->func_hash->notrace_hash) || | ||
| 1921 | !ftrace_lookup_ip(op->func_hash->notrace_hash, rec->ip))) | ||
| 1922 | return op; | 1898 | return op; |
| 1923 | } while_for_each_ftrace_op(op); | 1899 | } while_for_each_ftrace_op(op); |
| 1924 | 1900 | ||
| @@ -1929,18 +1905,51 @@ static struct ftrace_ops * | |||
| 1929 | ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec) | 1905 | ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec) |
| 1930 | { | 1906 | { |
| 1931 | struct ftrace_ops *op; | 1907 | struct ftrace_ops *op; |
| 1908 | unsigned long ip = rec->ip; | ||
| 1932 | 1909 | ||
| 1933 | /* Removed ops need to be tested first */ | 1910 | /* |
| 1934 | if (removed_ops && removed_ops->tramp_hash) { | 1911 | * Need to check removed ops first. |
| 1935 | if (ftrace_lookup_ip(removed_ops->tramp_hash, rec->ip)) | 1912 | * If they are being removed, and this rec has a tramp, |
| 1913 | * and this rec is in the ops list, then it would be the | ||
| 1914 | * one with the tramp. | ||
| 1915 | */ | ||
| 1916 | if (removed_ops) { | ||
| 1917 | if (hash_contains_ip(ip, &removed_ops->old_hash)) | ||
| 1936 | return removed_ops; | 1918 | return removed_ops; |
| 1937 | } | 1919 | } |
| 1938 | 1920 | ||
| 1921 | /* | ||
| 1922 | * Need to find the current trampoline for a rec. | ||
| 1923 | * Now, a trampoline is only attached to a rec if there | ||
| 1924 | * was a single 'ops' attached to it. But this can be called | ||
| 1925 | * when we are adding another op to the rec or removing the | ||
| 1926 | * current one. Thus, if the op is being added, we can | ||
| 1927 | * ignore it because it hasn't attached itself to the rec | ||
| 1928 | * yet. That means we just need to find the op that has a | ||
| 1929 | * trampoline and is not beeing added. | ||
| 1930 | */ | ||
| 1939 | do_for_each_ftrace_op(op, ftrace_ops_list) { | 1931 | do_for_each_ftrace_op(op, ftrace_ops_list) { |
| 1940 | if (!op->tramp_hash) | 1932 | |
| 1933 | if (!op->trampoline) | ||
| 1934 | continue; | ||
| 1935 | |||
| 1936 | /* | ||
| 1937 | * If the ops is being added, it hasn't gotten to | ||
| 1938 | * the point to be removed from this tree yet. | ||
| 1939 | */ | ||
| 1940 | if (op->flags & FTRACE_OPS_FL_ADDING) | ||
| 1941 | continue; | 1941 | continue; |
| 1942 | 1942 | ||
| 1943 | if (ftrace_lookup_ip(op->tramp_hash, rec->ip)) | 1943 | /* |
| 1944 | * If the ops is not being added and has a trampoline, | ||
| 1945 | * then it must be the one that we want! | ||
| 1946 | */ | ||
| 1947 | if (hash_contains_ip(ip, op->func_hash)) | ||
| 1948 | return op; | ||
| 1949 | |||
| 1950 | /* If the ops is being modified, it may be in the old hash. */ | ||
| 1951 | if ((op->flags & FTRACE_OPS_FL_MODIFYING) && | ||
| 1952 | hash_contains_ip(ip, &op->old_hash)) | ||
| 1944 | return op; | 1953 | return op; |
| 1945 | 1954 | ||
| 1946 | } while_for_each_ftrace_op(op); | 1955 | } while_for_each_ftrace_op(op); |
| @@ -1952,10 +1961,11 @@ static struct ftrace_ops * | |||
| 1952 | ftrace_find_tramp_ops_new(struct dyn_ftrace *rec) | 1961 | ftrace_find_tramp_ops_new(struct dyn_ftrace *rec) |
| 1953 | { | 1962 | { |
| 1954 | struct ftrace_ops *op; | 1963 | struct ftrace_ops *op; |
| 1964 | unsigned long ip = rec->ip; | ||
| 1955 | 1965 | ||
| 1956 | do_for_each_ftrace_op(op, ftrace_ops_list) { | 1966 | do_for_each_ftrace_op(op, ftrace_ops_list) { |
| 1957 | /* pass rec in as regs to have non-NULL val */ | 1967 | /* pass rec in as regs to have non-NULL val */ |
| 1958 | if (ftrace_ops_test(op, rec->ip, rec)) | 1968 | if (hash_contains_ip(ip, op->func_hash)) |
| 1959 | return op; | 1969 | return op; |
| 1960 | } while_for_each_ftrace_op(op); | 1970 | } while_for_each_ftrace_op(op); |
| 1961 | 1971 | ||
| @@ -2262,92 +2272,6 @@ void __weak arch_ftrace_update_code(int command) | |||
| 2262 | ftrace_run_stop_machine(command); | 2272 | ftrace_run_stop_machine(command); |
| 2263 | } | 2273 | } |
| 2264 | 2274 | ||
| 2265 | static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops) | ||
| 2266 | { | ||
| 2267 | struct ftrace_page *pg; | ||
| 2268 | struct dyn_ftrace *rec; | ||
| 2269 | int size, bits; | ||
| 2270 | int ret; | ||
| 2271 | |||
| 2272 | size = ops->nr_trampolines; | ||
| 2273 | bits = 0; | ||
| 2274 | /* | ||
| 2275 | * Make the hash size about 1/2 the # found | ||
| 2276 | */ | ||
| 2277 | for (size /= 2; size; size >>= 1) | ||
| 2278 | bits++; | ||
| 2279 | |||
| 2280 | ops->tramp_hash = alloc_ftrace_hash(bits); | ||
| 2281 | /* | ||
| 2282 | * TODO: a failed allocation is going to screw up | ||
| 2283 | * the accounting of what needs to be modified | ||
| 2284 | * and not. For now, we kill ftrace if we fail | ||
| 2285 | * to allocate here. But there are ways around this, | ||
| 2286 | * but that will take a little more work. | ||
| 2287 | */ | ||
| 2288 | if (!ops->tramp_hash) | ||
| 2289 | return -ENOMEM; | ||
| 2290 | |||
| 2291 | do_for_each_ftrace_rec(pg, rec) { | ||
| 2292 | if (ftrace_rec_count(rec) == 1 && | ||
| 2293 | ftrace_ops_test(ops, rec->ip, rec)) { | ||
| 2294 | |||
| 2295 | /* | ||
| 2296 | * If another ops adds to a rec, the rec will | ||
| 2297 | * lose its trampoline and never get it back | ||
| 2298 | * until all ops are off of it. | ||
| 2299 | */ | ||
| 2300 | if (!(rec->flags & FTRACE_FL_TRAMP)) | ||
| 2301 | continue; | ||
| 2302 | |||
| 2303 | /* This record had better have a trampoline */ | ||
| 2304 | if (FTRACE_WARN_ON(!(rec->flags & FTRACE_FL_TRAMP_EN))) | ||
| 2305 | return -1; | ||
| 2306 | |||
| 2307 | ret = add_hash_entry(ops->tramp_hash, rec->ip); | ||
| 2308 | if (ret < 0) | ||
| 2309 | return ret; | ||
| 2310 | } | ||
| 2311 | } while_for_each_ftrace_rec(); | ||
| 2312 | |||
| 2313 | /* The number of recs in the hash must match nr_trampolines */ | ||
| 2314 | if (FTRACE_WARN_ON(ops->tramp_hash->count != ops->nr_trampolines)) | ||
| 2315 | pr_warn("count=%ld trampolines=%d\n", | ||
| 2316 | ops->tramp_hash->count, | ||
| 2317 | ops->nr_trampolines); | ||
| 2318 | |||
| 2319 | return 0; | ||
| 2320 | } | ||
| 2321 | |||
| 2322 | static int ftrace_save_tramp_hashes(void) | ||
| 2323 | { | ||
| 2324 | struct ftrace_ops *op; | ||
| 2325 | int ret; | ||
| 2326 | |||
| 2327 | /* | ||
| 2328 | * Now that any trampoline is being used, we need to save the | ||
| 2329 | * hashes for the ops that have them. This allows the mapping | ||
| 2330 | * back from the record to the ops that has the trampoline to | ||
| 2331 | * know what code is being replaced. Modifying code must always | ||
| 2332 | * verify what it is changing. | ||
| 2333 | */ | ||
| 2334 | do_for_each_ftrace_op(op, ftrace_ops_list) { | ||
| 2335 | |||
| 2336 | /* The tramp_hash is recreated each time. */ | ||
| 2337 | free_ftrace_hash(op->tramp_hash); | ||
| 2338 | op->tramp_hash = NULL; | ||
| 2339 | |||
| 2340 | if (op->nr_trampolines) { | ||
| 2341 | ret = ftrace_save_ops_tramp_hash(op); | ||
| 2342 | if (ret) | ||
| 2343 | return ret; | ||
| 2344 | } | ||
| 2345 | |||
| 2346 | } while_for_each_ftrace_op(op); | ||
| 2347 | |||
| 2348 | return 0; | ||
| 2349 | } | ||
| 2350 | |||
| 2351 | static void ftrace_run_update_code(int command) | 2275 | static void ftrace_run_update_code(int command) |
| 2352 | { | 2276 | { |
| 2353 | int ret; | 2277 | int ret; |
| @@ -2367,9 +2291,6 @@ static void ftrace_run_update_code(int command) | |||
| 2367 | 2291 | ||
| 2368 | ret = ftrace_arch_code_modify_post_process(); | 2292 | ret = ftrace_arch_code_modify_post_process(); |
| 2369 | FTRACE_WARN_ON(ret); | 2293 | FTRACE_WARN_ON(ret); |
| 2370 | |||
| 2371 | ret = ftrace_save_tramp_hashes(); | ||
| 2372 | FTRACE_WARN_ON(ret); | ||
| 2373 | } | 2294 | } |
| 2374 | 2295 | ||
| 2375 | static void ftrace_run_modify_code(struct ftrace_ops *ops, int command) | 2296 | static void ftrace_run_modify_code(struct ftrace_ops *ops, int command) |
| @@ -2489,8 +2410,16 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) | |||
| 2489 | ops->flags |= FTRACE_OPS_FL_REMOVING; | 2410 | ops->flags |= FTRACE_OPS_FL_REMOVING; |
| 2490 | removed_ops = ops; | 2411 | removed_ops = ops; |
| 2491 | 2412 | ||
| 2413 | /* The trampoline logic checks the old hashes */ | ||
| 2414 | ops->old_hash.filter_hash = ops->func_hash->filter_hash; | ||
| 2415 | ops->old_hash.notrace_hash = ops->func_hash->notrace_hash; | ||
| 2416 | |||
| 2492 | ftrace_run_update_code(command); | 2417 | ftrace_run_update_code(command); |
| 2493 | 2418 | ||
| 2419 | ops->old_hash.filter_hash = NULL; | ||
| 2420 | ops->old_hash.notrace_hash = NULL; | ||
| 2421 | |||
| 2422 | removed_ops = NULL; | ||
| 2494 | ops->flags &= ~FTRACE_OPS_FL_REMOVING; | 2423 | ops->flags &= ~FTRACE_OPS_FL_REMOVING; |
| 2495 | 2424 | ||
| 2496 | /* | 2425 | /* |
| @@ -3017,7 +2946,7 @@ static int t_show(struct seq_file *m, void *v) | |||
| 3017 | struct ftrace_ops *ops; | 2946 | struct ftrace_ops *ops; |
| 3018 | 2947 | ||
| 3019 | ops = ftrace_find_tramp_ops_any(rec); | 2948 | ops = ftrace_find_tramp_ops_any(rec); |
| 3020 | if (ops && ops->trampoline) | 2949 | if (ops) |
| 3021 | seq_printf(m, "\ttramp: %pS", | 2950 | seq_printf(m, "\ttramp: %pS", |
| 3022 | (void *)ops->trampoline); | 2951 | (void *)ops->trampoline); |
| 3023 | else | 2952 | else |
