aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2014-07-24 12:25:47 -0400
committerSteven Rostedt <rostedt@goodmis.org>2014-09-10 10:48:45 -0400
commitfef5aeeee9e3717e7aea991a7ae9ff6a7a2d4c85 (patch)
tree1f74c6eb67ec987361f32e3d891132ecc5b1aa68 /kernel/trace
parente1effa0144a1ddf5b456c388ffaf784f3c5163fd (diff)
ftrace: Replace tramp_hash with old_*_hash to save space
Allowing function callbacks to declare their own trampolines requires that each ftrace_ops that has a trampoline must have some sort of accounting that keeps track of which ops has a trampoline attached to a record. The easy way to solve this was to add a "tramp_hash" that created a hash entry for every function that a ops uses with a trampoline. But since we can have literally tens of thousands of functions being traced, that means we need tens of thousands of descriptors to map the ops to the function in the hash. This is quite expensive and can cause enabling and disabling the function graph tracer to take some time to start and stop. It can take up to several seconds to disable or enable all functions in the function graph tracer for this reason. The better approach albeit more complex, is to keep track of how ops are being enabled and disabled, and use that along with the counting of the number of ops attached to records, to determive what ops has a trampoline attached to a record at enabling and disabling of tracing. To do this, the tramp_hash has been replaced with an old_filter_hash and old_notrace_hash, which get the copy of the ops filter_hash and notrace_hash respectively. The old hashes is kept until the ops has been modified or removed and the old hashes are used with the logic of the accounting to determine the ops that have the trampoline of a record. The reason this has less of a footprint is due to the trick that an "empty" hash in the filter_hash means "all functions" and an empty hash in the notrace hash means "no functions" in the hash. This is much more efficienct, doesn't have the delay, and takes up much less memory, as we do not need to map all the functions but just figure out which functions are mapped at the time it is enabled or disabled. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/ftrace.c239
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
1376static 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:
1388static int 1403static int
1389ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) 1404ftrace_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
1523static 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
1543static 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
1563static void __ftrace_hash_rec_update(struct ftrace_ops *ops, 1534static 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 *
1910ftrace_find_tramp_ops_any(struct dyn_ftrace *rec) 1887ftrace_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 *
1929ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec) 1905ftrace_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 *
1952ftrace_find_tramp_ops_new(struct dyn_ftrace *rec) 1961ftrace_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
2265static 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
2322static 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
2351static void ftrace_run_update_code(int command) 2275static 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
2375static void ftrace_run_modify_code(struct ftrace_ops *ops, int command) 2296static 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