aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/ftrace.c
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2012-04-30 16:20:23 -0400
committerSteven Rostedt <rostedt@goodmis.org>2012-07-19 13:20:03 -0400
commit08f6fba503111e0336f2b4d6915a4a18f9b60e51 (patch)
tree119f971348614154552b70fb5cbe358f7c532a18 /kernel/trace/ftrace.c
parent28fb5dfa783c25dbeeb25a72663f8066a3a517f5 (diff)
ftrace/x86: Add separate function to save regs
Add a way to have different functions calling different trampolines. If a ftrace_ops wants regs saved on the return, then have only the functions with ops registered to save regs. Functions registered by other ops would not be affected, unless the functions overlap. If one ftrace_ops registered functions A, B and C and another ops registered fucntions to save regs on A, and D, then only functions A and D would be saving regs. Function B and C would work as normal. Although A is registered by both ops: normal and saves regs; this is fine as saving the regs is needed to satisfy one of the ops that calls it but the regs are ignored by the other ops function. x86_64 implements the full regs saving, and i386 just passes a NULL for regs to satisfy the ftrace_ops passing. Where an arch must supply both regs and ftrace_ops parameters, even if regs is just NULL. It is OK for an arch to pass NULL regs. All function trace users that require regs passing must add the flag FTRACE_OPS_FL_SAVE_REGS when registering the ftrace_ops. If the arch does not support saving regs then the ftrace_ops will fail to register. The flag FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED may be set that will prevent the ftrace_ops from failing to register. In this case, the handler may either check if regs is not NULL or check if ARCH_SUPPORTS_FTRACE_SAVE_REGS. If the arch supports passing regs it will set this macro and pass regs for ops that request them. All other archs will just pass NULL. Link: Link: http://lkml.kernel.org/r/20120711195745.107705970@goodmis.org Cc: Alexander van Heukelum <heukelum@fastmail.fm> Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r--kernel/trace/ftrace.c91
1 files changed, 83 insertions, 8 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 6ff07ad0ede3..c55f7e274613 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -314,6 +314,20 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
314 if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK) 314 if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK)
315 return -EINVAL; 315 return -EINVAL;
316 316
317#ifndef ARCH_SUPPORTS_FTRACE_SAVE_REGS
318 /*
319 * If the ftrace_ops specifies SAVE_REGS, then it only can be used
320 * if the arch supports it, or SAVE_REGS_IF_SUPPORTED is also set.
321 * Setting SAVE_REGS_IF_SUPPORTED makes SAVE_REGS irrelevant.
322 */
323 if (ops->flags & FTRACE_OPS_FL_SAVE_REGS &&
324 !(ops->flags & FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED))
325 return -EINVAL;
326
327 if (ops->flags & FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED)
328 ops->flags |= FTRACE_OPS_FL_SAVE_REGS;
329#endif
330
317 if (!core_kernel_data((unsigned long)ops)) 331 if (!core_kernel_data((unsigned long)ops))
318 ops->flags |= FTRACE_OPS_FL_DYNAMIC; 332 ops->flags |= FTRACE_OPS_FL_DYNAMIC;
319 333
@@ -1515,6 +1529,12 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1515 rec->flags++; 1529 rec->flags++;
1516 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == FTRACE_REF_MAX)) 1530 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == FTRACE_REF_MAX))
1517 return; 1531 return;
1532 /*
1533 * If any ops wants regs saved for this function
1534 * then all ops will get saved regs.
1535 */
1536 if (ops->flags & FTRACE_OPS_FL_SAVE_REGS)
1537 rec->flags |= FTRACE_FL_REGS;
1518 } else { 1538 } else {
1519 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0)) 1539 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0))
1520 return; 1540 return;
@@ -1606,18 +1626,59 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
1606 if (enable && (rec->flags & ~FTRACE_FL_MASK)) 1626 if (enable && (rec->flags & ~FTRACE_FL_MASK))
1607 flag = FTRACE_FL_ENABLED; 1627 flag = FTRACE_FL_ENABLED;
1608 1628
1629 /*
1630 * If enabling and the REGS flag does not match the REGS_EN, then
1631 * do not ignore this record. Set flags to fail the compare against
1632 * ENABLED.
1633 */
1634 if (flag &&
1635 (!(rec->flags & FTRACE_FL_REGS) != !(rec->flags & FTRACE_FL_REGS_EN)))
1636 flag |= FTRACE_FL_REGS;
1637
1609 /* If the state of this record hasn't changed, then do nothing */ 1638 /* If the state of this record hasn't changed, then do nothing */
1610 if ((rec->flags & FTRACE_FL_ENABLED) == flag) 1639 if ((rec->flags & FTRACE_FL_ENABLED) == flag)
1611 return FTRACE_UPDATE_IGNORE; 1640 return FTRACE_UPDATE_IGNORE;
1612 1641
1613 if (flag) { 1642 if (flag) {
1614 if (update) 1643 /* Save off if rec is being enabled (for return value) */
1644 flag ^= rec->flags & FTRACE_FL_ENABLED;
1645
1646 if (update) {
1615 rec->flags |= FTRACE_FL_ENABLED; 1647 rec->flags |= FTRACE_FL_ENABLED;
1616 return FTRACE_UPDATE_MAKE_CALL; 1648 if (flag & FTRACE_FL_REGS) {
1649 if (rec->flags & FTRACE_FL_REGS)
1650 rec->flags |= FTRACE_FL_REGS_EN;
1651 else
1652 rec->flags &= ~FTRACE_FL_REGS_EN;
1653 }
1654 }
1655
1656 /*
1657 * If this record is being updated from a nop, then
1658 * return UPDATE_MAKE_CALL.
1659 * Otherwise, if the EN flag is set, then return
1660 * UPDATE_MODIFY_CALL_REGS to tell the caller to convert
1661 * from the non-save regs, to a save regs function.
1662 * Otherwise,
1663 * return UPDATE_MODIFY_CALL to tell the caller to convert
1664 * from the save regs, to a non-save regs function.
1665 */
1666 if (flag & FTRACE_FL_ENABLED)
1667 return FTRACE_UPDATE_MAKE_CALL;
1668 else if (rec->flags & FTRACE_FL_REGS_EN)
1669 return FTRACE_UPDATE_MODIFY_CALL_REGS;
1670 else
1671 return FTRACE_UPDATE_MODIFY_CALL;
1617 } 1672 }
1618 1673
1619 if (update) 1674 if (update) {
1620 rec->flags &= ~FTRACE_FL_ENABLED; 1675 /* If there's no more users, clear all flags */
1676 if (!(rec->flags & ~FTRACE_FL_MASK))
1677 rec->flags = 0;
1678 else
1679 /* Just disable the record (keep REGS state) */
1680 rec->flags &= ~FTRACE_FL_ENABLED;
1681 }
1621 1682
1622 return FTRACE_UPDATE_MAKE_NOP; 1683 return FTRACE_UPDATE_MAKE_NOP;
1623} 1684}
@@ -1652,13 +1713,17 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable)
1652static int 1713static int
1653__ftrace_replace_code(struct dyn_ftrace *rec, int enable) 1714__ftrace_replace_code(struct dyn_ftrace *rec, int enable)
1654{ 1715{
1716 unsigned long ftrace_old_addr;
1655 unsigned long ftrace_addr; 1717 unsigned long ftrace_addr;
1656 int ret; 1718 int ret;
1657 1719
1658 ftrace_addr = (unsigned long)FTRACE_ADDR;
1659
1660 ret = ftrace_update_record(rec, enable); 1720 ret = ftrace_update_record(rec, enable);
1661 1721
1722 if (rec->flags & FTRACE_FL_REGS)
1723 ftrace_addr = (unsigned long)FTRACE_REGS_ADDR;
1724 else
1725 ftrace_addr = (unsigned long)FTRACE_ADDR;
1726
1662 switch (ret) { 1727 switch (ret) {
1663 case FTRACE_UPDATE_IGNORE: 1728 case FTRACE_UPDATE_IGNORE:
1664 return 0; 1729 return 0;
@@ -1668,6 +1733,15 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
1668 1733
1669 case FTRACE_UPDATE_MAKE_NOP: 1734 case FTRACE_UPDATE_MAKE_NOP:
1670 return ftrace_make_nop(NULL, rec, ftrace_addr); 1735 return ftrace_make_nop(NULL, rec, ftrace_addr);
1736
1737 case FTRACE_UPDATE_MODIFY_CALL_REGS:
1738 case FTRACE_UPDATE_MODIFY_CALL:
1739 if (rec->flags & FTRACE_FL_REGS)
1740 ftrace_old_addr = (unsigned long)FTRACE_ADDR;
1741 else
1742 ftrace_old_addr = (unsigned long)FTRACE_REGS_ADDR;
1743
1744 return ftrace_modify_call(rec, ftrace_old_addr, ftrace_addr);
1671 } 1745 }
1672 1746
1673 return -1; /* unknow ftrace bug */ 1747 return -1; /* unknow ftrace bug */
@@ -2421,8 +2495,9 @@ static int t_show(struct seq_file *m, void *v)
2421 2495
2422 seq_printf(m, "%ps", (void *)rec->ip); 2496 seq_printf(m, "%ps", (void *)rec->ip);
2423 if (iter->flags & FTRACE_ITER_ENABLED) 2497 if (iter->flags & FTRACE_ITER_ENABLED)
2424 seq_printf(m, " (%ld)", 2498 seq_printf(m, " (%ld)%s",
2425 rec->flags & ~FTRACE_FL_MASK); 2499 rec->flags & ~FTRACE_FL_MASK,
2500 rec->flags & FTRACE_FL_REGS ? " R" : "");
2426 seq_printf(m, "\n"); 2501 seq_printf(m, "\n");
2427 2502
2428 return 0; 2503 return 0;