aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/ftrace.c
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2014-05-06 21:56:17 -0400
committerSteven Rostedt <rostedt@goodmis.org>2014-07-01 07:13:31 -0400
commit79922b8009c074e30d3a97f5a24519f11814ad03 (patch)
tree1cbbaa8d85c666629a960c076dc92d1252e3bc6d /kernel/trace/ftrace.c
parent0376bde11be5b87c9fd7d6813ac5fd7e1798b1bf (diff)
ftrace: Optimize function graph to be called directly
Function graph tracing is a bit different than the function tracers, as it is processed after either the ftrace_caller or ftrace_regs_caller and we only have one place to modify the jump to ftrace_graph_caller, the jump needs to happen after the restore of registeres. The function graph tracer is dependent on the function tracer, where even if the function graph tracing is going on by itself, the save and restore of registers is still done for function tracing regardless of if function tracing is happening, before it calls the function graph code. If there's no function tracing happening, it is possible to just call the function graph tracer directly, and avoid the wasted effort to save and restore regs for function tracing. This requires adding new flags to the dyn_ftrace records: FTRACE_FL_TRAMP FTRACE_FL_TRAMP_EN The first is set if the count for the record is one, and the ftrace_ops associated to that record has its own trampoline. That way the mcount code can call that trampoline directly. In the future, trampolines can be added to arbitrary ftrace_ops, where you can have two or more ftrace_ops registered to ftrace (like kprobes and perf) and if they are not tracing the same functions, then instead of doing a loop to check all registered ftrace_ops against their hashes, just call the ftrace_ops trampoline directly, which would call the registered ftrace_ops function directly. Without this patch perf showed: 0.05% hackbench [kernel.kallsyms] [k] ftrace_caller 0.05% hackbench [kernel.kallsyms] [k] arch_local_irq_save 0.05% hackbench [kernel.kallsyms] [k] native_sched_clock 0.04% hackbench [kernel.kallsyms] [k] __buffer_unlock_commit 0.04% hackbench [kernel.kallsyms] [k] preempt_trace 0.04% hackbench [kernel.kallsyms] [k] prepare_ftrace_return 0.04% hackbench [kernel.kallsyms] [k] __this_cpu_preempt_check 0.04% hackbench [kernel.kallsyms] [k] ftrace_graph_caller See that the ftrace_caller took up more time than the ftrace_graph_caller did. With this patch: 0.05% hackbench [kernel.kallsyms] [k] __buffer_unlock_commit 0.04% hackbench [kernel.kallsyms] [k] call_filter_check_discard 0.04% hackbench [kernel.kallsyms] [k] ftrace_graph_caller 0.04% hackbench [kernel.kallsyms] [k] sched_clock The ftrace_caller is no where to be found and ftrace_graph_caller still takes up the same percentage. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r--kernel/trace/ftrace.c242
1 files changed, 235 insertions, 7 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index a58d840305c3..5d15eb8146a7 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1042,6 +1042,8 @@ static struct pid * const ftrace_swapper_pid = &init_struct_pid;
1042 1042
1043#ifdef CONFIG_DYNAMIC_FTRACE 1043#ifdef CONFIG_DYNAMIC_FTRACE
1044 1044
1045static struct ftrace_ops *removed_ops;
1046
1045#ifndef CONFIG_FTRACE_MCOUNT_RECORD 1047#ifndef CONFIG_FTRACE_MCOUNT_RECORD
1046# error Dynamic ftrace depends on MCOUNT_RECORD 1048# error Dynamic ftrace depends on MCOUNT_RECORD
1047#endif 1049#endif
@@ -1512,6 +1514,33 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
1512 return keep_regs; 1514 return keep_regs;
1513} 1515}
1514 1516
1517static void ftrace_remove_tramp(struct ftrace_ops *ops,
1518 struct dyn_ftrace *rec)
1519{
1520 struct ftrace_func_entry *entry;
1521
1522 entry = ftrace_lookup_ip(ops->tramp_hash, rec->ip);
1523 if (!entry)
1524 return;
1525
1526 /*
1527 * The tramp_hash entry will be removed at time
1528 * of update.
1529 */
1530 ops->trampolines--;
1531 rec->flags &= ~FTRACE_FL_TRAMP;
1532}
1533
1534static void ftrace_clear_tramps(struct dyn_ftrace *rec)
1535{
1536 struct ftrace_ops *op;
1537
1538 do_for_each_ftrace_op(op, ftrace_ops_list) {
1539 if (op->trampolines)
1540 ftrace_remove_tramp(op, rec);
1541 } while_for_each_ftrace_op(op);
1542}
1543
1515static void __ftrace_hash_rec_update(struct ftrace_ops *ops, 1544static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1516 int filter_hash, 1545 int filter_hash,
1517 bool inc) 1546 bool inc)
@@ -1594,6 +1623,28 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1594 rec->flags++; 1623 rec->flags++;
1595 if (FTRACE_WARN_ON(ftrace_rec_count(rec) == FTRACE_REF_MAX)) 1624 if (FTRACE_WARN_ON(ftrace_rec_count(rec) == FTRACE_REF_MAX))
1596 return; 1625 return;
1626
1627 /*
1628 * If there's only a single callback registered to a
1629 * function, and the ops has a trampoline registered
1630 * for it, then we can call it directly.
1631 */
1632 if (ftrace_rec_count(rec) == 1 && ops->trampoline) {
1633 rec->flags |= FTRACE_FL_TRAMP;
1634 ops->trampolines++;
1635 } else {
1636 /*
1637 * If we are adding another function callback
1638 * to this function, and the previous had a
1639 * trampoline used, then we need to go back to
1640 * the default trampoline.
1641 */
1642 rec->flags &= ~FTRACE_FL_TRAMP;
1643
1644 /* remove trampolines from any ops for this rec */
1645 ftrace_clear_tramps(rec);
1646 }
1647
1597 /* 1648 /*
1598 * If any ops wants regs saved for this function 1649 * If any ops wants regs saved for this function
1599 * then all ops will get saved regs. 1650 * then all ops will get saved regs.
@@ -1604,6 +1655,10 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1604 if (FTRACE_WARN_ON(ftrace_rec_count(rec) == 0)) 1655 if (FTRACE_WARN_ON(ftrace_rec_count(rec) == 0))
1605 return; 1656 return;
1606 rec->flags--; 1657 rec->flags--;
1658
1659 if (ops->trampoline && !ftrace_rec_count(rec))
1660 ftrace_remove_tramp(ops, rec);
1661
1607 /* 1662 /*
1608 * If the rec had REGS enabled and the ops that is 1663 * If the rec had REGS enabled and the ops that is
1609 * being removed had REGS set, then see if there is 1664 * being removed had REGS set, then see if there is
@@ -1616,6 +1671,11 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1616 if (!test_rec_ops_needs_regs(rec)) 1671 if (!test_rec_ops_needs_regs(rec))
1617 rec->flags &= ~FTRACE_FL_REGS; 1672 rec->flags &= ~FTRACE_FL_REGS;
1618 } 1673 }
1674
1675 /*
1676 * flags will be cleared in ftrace_check_record()
1677 * if rec count is zero.
1678 */
1619 } 1679 }
1620 count++; 1680 count++;
1621 /* Shortcut, if we handled all records, we are done. */ 1681 /* Shortcut, if we handled all records, we are done. */
@@ -1704,13 +1764,19 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
1704 flag = FTRACE_FL_ENABLED; 1764 flag = FTRACE_FL_ENABLED;
1705 1765
1706 /* 1766 /*
1707 * If enabling and the REGS flag does not match the REGS_EN, then 1767 * If enabling and the REGS flag does not match the REGS_EN, or
1708 * do not ignore this record. Set flags to fail the compare against 1768 * the TRAMP flag doesn't match the TRAMP_EN, then do not ignore
1709 * ENABLED. 1769 * this record. Set flags to fail the compare against ENABLED.
1710 */ 1770 */
1711 if (flag && 1771 if (flag) {
1712 (!(rec->flags & FTRACE_FL_REGS) != !(rec->flags & FTRACE_FL_REGS_EN))) 1772 if (!(rec->flags & FTRACE_FL_REGS) !=
1713 flag |= FTRACE_FL_REGS; 1773 !(rec->flags & FTRACE_FL_REGS_EN))
1774 flag |= FTRACE_FL_REGS;
1775
1776 if (!(rec->flags & FTRACE_FL_TRAMP) !=
1777 !(rec->flags & FTRACE_FL_TRAMP_EN))
1778 flag |= FTRACE_FL_TRAMP;
1779 }
1714 1780
1715 /* If the state of this record hasn't changed, then do nothing */ 1781 /* If the state of this record hasn't changed, then do nothing */
1716 if ((rec->flags & FTRACE_FL_ENABLED) == flag) 1782 if ((rec->flags & FTRACE_FL_ENABLED) == flag)
@@ -1728,6 +1794,12 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
1728 else 1794 else
1729 rec->flags &= ~FTRACE_FL_REGS_EN; 1795 rec->flags &= ~FTRACE_FL_REGS_EN;
1730 } 1796 }
1797 if (flag & FTRACE_FL_TRAMP) {
1798 if (rec->flags & FTRACE_FL_TRAMP)
1799 rec->flags |= FTRACE_FL_TRAMP_EN;
1800 else
1801 rec->flags &= ~FTRACE_FL_TRAMP_EN;
1802 }
1731 } 1803 }
1732 1804
1733 /* 1805 /*
@@ -1736,7 +1808,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
1736 * Otherwise, 1808 * Otherwise,
1737 * return UPDATE_MODIFY_CALL to tell the caller to convert 1809 * return UPDATE_MODIFY_CALL to tell the caller to convert
1738 * from the save regs, to a non-save regs function or 1810 * from the save regs, to a non-save regs function or
1739 * vice versa. 1811 * vice versa, or from a trampoline call.
1740 */ 1812 */
1741 if (flag & FTRACE_FL_ENABLED) 1813 if (flag & FTRACE_FL_ENABLED)
1742 return FTRACE_UPDATE_MAKE_CALL; 1814 return FTRACE_UPDATE_MAKE_CALL;
@@ -1783,6 +1855,43 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable)
1783 return ftrace_check_record(rec, enable, 0); 1855 return ftrace_check_record(rec, enable, 0);
1784} 1856}
1785 1857
1858static struct ftrace_ops *
1859ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec)
1860{
1861 struct ftrace_ops *op;
1862
1863 /* Removed ops need to be tested first */
1864 if (removed_ops && removed_ops->tramp_hash) {
1865 if (ftrace_lookup_ip(removed_ops->tramp_hash, rec->ip))
1866 return removed_ops;
1867 }
1868
1869 do_for_each_ftrace_op(op, ftrace_ops_list) {
1870 if (!op->tramp_hash)
1871 continue;
1872
1873 if (ftrace_lookup_ip(op->tramp_hash, rec->ip))
1874 return op;
1875
1876 } while_for_each_ftrace_op(op);
1877
1878 return NULL;
1879}
1880
1881static struct ftrace_ops *
1882ftrace_find_tramp_ops_new(struct dyn_ftrace *rec)
1883{
1884 struct ftrace_ops *op;
1885
1886 do_for_each_ftrace_op(op, ftrace_ops_list) {
1887 /* pass rec in as regs to have non-NULL val */
1888 if (ftrace_ops_test(op, rec->ip, rec))
1889 return op;
1890 } while_for_each_ftrace_op(op);
1891
1892 return NULL;
1893}
1894
1786/** 1895/**
1787 * ftrace_get_addr_new - Get the call address to set to 1896 * ftrace_get_addr_new - Get the call address to set to
1788 * @rec: The ftrace record descriptor 1897 * @rec: The ftrace record descriptor
@@ -1795,6 +1904,20 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable)
1795 */ 1904 */
1796unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec) 1905unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec)
1797{ 1906{
1907 struct ftrace_ops *ops;
1908
1909 /* Trampolines take precedence over regs */
1910 if (rec->flags & FTRACE_FL_TRAMP) {
1911 ops = ftrace_find_tramp_ops_new(rec);
1912 if (FTRACE_WARN_ON(!ops || !ops->trampoline)) {
1913 pr_warning("Bad trampoline accounting at: %p (%pS)\n",
1914 (void *)rec->ip, (void *)rec->ip);
1915 /* Ftrace is shutting down, return anything */
1916 return (unsigned long)FTRACE_ADDR;
1917 }
1918 return ops->trampoline;
1919 }
1920
1798 if (rec->flags & FTRACE_FL_REGS) 1921 if (rec->flags & FTRACE_FL_REGS)
1799 return (unsigned long)FTRACE_REGS_ADDR; 1922 return (unsigned long)FTRACE_REGS_ADDR;
1800 else 1923 else
@@ -1813,6 +1936,20 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec)
1813 */ 1936 */
1814unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec) 1937unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec)
1815{ 1938{
1939 struct ftrace_ops *ops;
1940
1941 /* Trampolines take precedence over regs */
1942 if (rec->flags & FTRACE_FL_TRAMP_EN) {
1943 ops = ftrace_find_tramp_ops_curr(rec);
1944 if (FTRACE_WARN_ON(!ops)) {
1945 pr_warning("Bad trampoline accounting at: %p (%pS)\n",
1946 (void *)rec->ip, (void *)rec->ip);
1947 /* Ftrace is shutting down, return anything */
1948 return (unsigned long)FTRACE_ADDR;
1949 }
1950 return ops->trampoline;
1951 }
1952
1816 if (rec->flags & FTRACE_FL_REGS_EN) 1953 if (rec->flags & FTRACE_FL_REGS_EN)
1817 return (unsigned long)FTRACE_REGS_ADDR; 1954 return (unsigned long)FTRACE_REGS_ADDR;
1818 else 1955 else
@@ -2055,6 +2192,78 @@ void __weak arch_ftrace_update_code(int command)
2055 ftrace_run_stop_machine(command); 2192 ftrace_run_stop_machine(command);
2056} 2193}
2057 2194
2195static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops)
2196{
2197 struct ftrace_page *pg;
2198 struct dyn_ftrace *rec;
2199 int size, bits;
2200 int ret;
2201
2202 size = ops->trampolines;
2203 bits = 0;
2204 /*
2205 * Make the hash size about 1/2 the # found
2206 */
2207 for (size /= 2; size; size >>= 1)
2208 bits++;
2209
2210 ops->tramp_hash = alloc_ftrace_hash(bits);
2211 /*
2212 * TODO: a failed allocation is going to screw up
2213 * the accounting of what needs to be modified
2214 * and not. For now, we kill ftrace if we fail
2215 * to allocate here. But there are ways around this,
2216 * but that will take a little more work.
2217 */
2218 if (!ops->tramp_hash)
2219 return -ENOMEM;
2220
2221 do_for_each_ftrace_rec(pg, rec) {
2222 if (ftrace_rec_count(rec) == 1 &&
2223 ftrace_ops_test(ops, rec->ip, rec)) {
2224
2225 /* This record had better have a trampoline */
2226 if (FTRACE_WARN_ON(!(rec->flags & FTRACE_FL_TRAMP_EN)))
2227 return -1;
2228
2229 ret = add_hash_entry(ops->tramp_hash, rec->ip);
2230 if (ret < 0)
2231 return ret;
2232 }
2233 } while_for_each_ftrace_rec();
2234
2235 return 0;
2236}
2237
2238static int ftrace_save_tramp_hashes(void)
2239{
2240 struct ftrace_ops *op;
2241 int ret;
2242
2243 /*
2244 * Now that any trampoline is being used, we need to save the
2245 * hashes for the ops that have them. This allows the mapping
2246 * back from the record to the ops that has the trampoline to
2247 * know what code is being replaced. Modifying code must always
2248 * verify what it is changing.
2249 */
2250 do_for_each_ftrace_op(op, ftrace_ops_list) {
2251
2252 /* The tramp_hash is recreated each time. */
2253 free_ftrace_hash(op->tramp_hash);
2254 op->tramp_hash = NULL;
2255
2256 if (op->trampolines) {
2257 ret = ftrace_save_ops_tramp_hash(op);
2258 if (ret)
2259 return ret;
2260 }
2261
2262 } while_for_each_ftrace_op(op);
2263
2264 return 0;
2265}
2266
2058static void ftrace_run_update_code(int command) 2267static void ftrace_run_update_code(int command)
2059{ 2268{
2060 int ret; 2269 int ret;
@@ -2081,6 +2290,9 @@ static void ftrace_run_update_code(int command)
2081 2290
2082 ret = ftrace_arch_code_modify_post_process(); 2291 ret = ftrace_arch_code_modify_post_process();
2083 FTRACE_WARN_ON(ret); 2292 FTRACE_WARN_ON(ret);
2293
2294 ret = ftrace_save_tramp_hashes();
2295 FTRACE_WARN_ON(ret);
2084} 2296}
2085 2297
2086static ftrace_func_t saved_ftrace_func; 2298static ftrace_func_t saved_ftrace_func;
@@ -2171,8 +2383,16 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
2171 return 0; 2383 return 0;
2172 } 2384 }
2173 2385
2386 /*
2387 * If the ops uses a trampoline, then it needs to be
2388 * tested first on update.
2389 */
2390 removed_ops = ops;
2391
2174 ftrace_run_update_code(command); 2392 ftrace_run_update_code(command);
2175 2393
2394 removed_ops = NULL;
2395
2176 /* 2396 /*
2177 * Dynamic ops may be freed, we must make sure that all 2397 * Dynamic ops may be freed, we must make sure that all
2178 * callers are done before leaving this function. 2398 * callers are done before leaving this function.
@@ -5116,6 +5336,11 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,
5116 /* Function graph doesn't use the .func field of global_ops */ 5336 /* Function graph doesn't use the .func field of global_ops */
5117 global_ops.flags |= FTRACE_OPS_FL_STUB; 5337 global_ops.flags |= FTRACE_OPS_FL_STUB;
5118 5338
5339#ifdef CONFIG_DYNAMIC_FTRACE
5340 /* Optimize function graph calling (if implemented by arch) */
5341 global_ops.trampoline = FTRACE_GRAPH_ADDR;
5342#endif
5343
5119 ret = ftrace_startup(&global_ops, FTRACE_START_FUNC_RET); 5344 ret = ftrace_startup(&global_ops, FTRACE_START_FUNC_RET);
5120 5345
5121out: 5346out:
@@ -5136,6 +5361,9 @@ void unregister_ftrace_graph(void)
5136 __ftrace_graph_entry = ftrace_graph_entry_stub; 5361 __ftrace_graph_entry = ftrace_graph_entry_stub;
5137 ftrace_shutdown(&global_ops, FTRACE_STOP_FUNC_RET); 5362 ftrace_shutdown(&global_ops, FTRACE_STOP_FUNC_RET);
5138 global_ops.flags &= ~FTRACE_OPS_FL_STUB; 5363 global_ops.flags &= ~FTRACE_OPS_FL_STUB;
5364#ifdef CONFIG_DYNAMIC_FTRACE
5365 global_ops.trampoline = 0;
5366#endif
5139 unregister_pm_notifier(&ftrace_suspend_notifier); 5367 unregister_pm_notifier(&ftrace_suspend_notifier);
5140 unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL); 5368 unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
5141 5369