diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 91 |
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) | |||
1652 | static int | 1713 | static 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; |