diff options
| -rw-r--r-- | include/asm-x86/ftrace.h | 10 | ||||
| -rw-r--r-- | include/linux/ftrace.h | 6 | ||||
| -rw-r--r-- | init/main.c | 3 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 148 |
4 files changed, 124 insertions, 43 deletions
diff --git a/include/asm-x86/ftrace.h b/include/asm-x86/ftrace.h index be0e004ad148..1bb6f9bbe1ab 100644 --- a/include/asm-x86/ftrace.h +++ b/include/asm-x86/ftrace.h | |||
| @@ -7,6 +7,16 @@ | |||
| 7 | 7 | ||
| 8 | #ifndef __ASSEMBLY__ | 8 | #ifndef __ASSEMBLY__ |
| 9 | extern void mcount(void); | 9 | extern void mcount(void); |
| 10 | |||
| 11 | static inline unsigned long ftrace_call_adjust(unsigned long addr) | ||
| 12 | { | ||
| 13 | /* | ||
| 14 | * call mcount is "e8 <4 byte offset>" | ||
| 15 | * The addr points to the 4 byte offset and the caller of this | ||
| 16 | * function wants the pointer to e8. Simply subtract one. | ||
| 17 | */ | ||
| 18 | return addr - 1; | ||
| 19 | } | ||
| 10 | #endif | 20 | #endif |
| 11 | 21 | ||
| 12 | #endif /* CONFIG_FTRACE */ | 22 | #endif /* CONFIG_FTRACE */ |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index bb384068272e..d4d6ab453b78 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -162,4 +162,10 @@ static inline void | |||
| 162 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } | 162 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { } |
| 163 | #endif | 163 | #endif |
| 164 | 164 | ||
| 165 | #ifdef CONFIG_FTRACE_MCOUNT_RECORD | ||
| 166 | extern void ftrace_init(void); | ||
| 167 | #else | ||
| 168 | static inline void ftrace_init(void) { } | ||
| 169 | #endif | ||
| 170 | |||
| 165 | #endif /* _LINUX_FTRACE_H */ | 171 | #endif /* _LINUX_FTRACE_H */ |
diff --git a/init/main.c b/init/main.c index 3820323c4c84..ded1fae965ab 100644 --- a/init/main.c +++ b/init/main.c | |||
| @@ -60,6 +60,7 @@ | |||
| 60 | #include <linux/sched.h> | 60 | #include <linux/sched.h> |
| 61 | #include <linux/signal.h> | 61 | #include <linux/signal.h> |
| 62 | #include <linux/idr.h> | 62 | #include <linux/idr.h> |
| 63 | #include <linux/ftrace.h> | ||
| 63 | 64 | ||
| 64 | #include <asm/io.h> | 65 | #include <asm/io.h> |
| 65 | #include <asm/bugs.h> | 66 | #include <asm/bugs.h> |
| @@ -687,6 +688,8 @@ asmlinkage void __init start_kernel(void) | |||
| 687 | 688 | ||
| 688 | acpi_early_init(); /* before LAPIC and SMP init */ | 689 | acpi_early_init(); /* before LAPIC and SMP init */ |
| 689 | 690 | ||
| 691 | ftrace_init(); | ||
| 692 | |||
| 690 | /* Do the rest non-__init'ed, we're now alive */ | 693 | /* Do the rest non-__init'ed, we're now alive */ |
| 691 | rest_init(); | 694 | rest_init(); |
| 692 | } | 695 | } |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f6e3af31b403..df96d5990c04 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -792,47 +792,7 @@ static int ftrace_update_code(void) | |||
| 792 | return 1; | 792 | return 1; |
| 793 | } | 793 | } |
| 794 | 794 | ||
| 795 | static int ftraced(void *ignore) | 795 | static int __init ftrace_dyn_table_alloc(unsigned long num_to_init) |
| 796 | { | ||
| 797 | unsigned long usecs; | ||
| 798 | |||
| 799 | while (!kthread_should_stop()) { | ||
| 800 | |||
| 801 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 802 | |||
| 803 | /* check once a second */ | ||
| 804 | schedule_timeout(HZ); | ||
| 805 | |||
| 806 | if (unlikely(ftrace_disabled)) | ||
| 807 | continue; | ||
| 808 | |||
| 809 | mutex_lock(&ftrace_sysctl_lock); | ||
| 810 | mutex_lock(&ftraced_lock); | ||
| 811 | if (!ftraced_suspend && !ftraced_stop && | ||
| 812 | ftrace_update_code()) { | ||
| 813 | usecs = nsecs_to_usecs(ftrace_update_time); | ||
| 814 | if (ftrace_update_tot_cnt > 100000) { | ||
| 815 | ftrace_update_tot_cnt = 0; | ||
| 816 | pr_info("hm, dftrace overflow: %lu change%s" | ||
| 817 | " (%lu total) in %lu usec%s\n", | ||
| 818 | ftrace_update_cnt, | ||
| 819 | ftrace_update_cnt != 1 ? "s" : "", | ||
| 820 | ftrace_update_tot_cnt, | ||
| 821 | usecs, usecs != 1 ? "s" : ""); | ||
| 822 | ftrace_disabled = 1; | ||
| 823 | WARN_ON_ONCE(1); | ||
| 824 | } | ||
| 825 | } | ||
| 826 | mutex_unlock(&ftraced_lock); | ||
| 827 | mutex_unlock(&ftrace_sysctl_lock); | ||
| 828 | |||
| 829 | ftrace_shutdown_replenish(); | ||
| 830 | } | ||
| 831 | __set_current_state(TASK_RUNNING); | ||
| 832 | return 0; | ||
| 833 | } | ||
| 834 | |||
| 835 | static int __init ftrace_dyn_table_alloc(void) | ||
| 836 | { | 796 | { |
| 837 | struct ftrace_page *pg; | 797 | struct ftrace_page *pg; |
| 838 | int cnt; | 798 | int cnt; |
| @@ -859,7 +819,9 @@ static int __init ftrace_dyn_table_alloc(void) | |||
| 859 | 819 | ||
| 860 | pg = ftrace_pages = ftrace_pages_start; | 820 | pg = ftrace_pages = ftrace_pages_start; |
| 861 | 821 | ||
| 862 | cnt = NR_TO_INIT / ENTRIES_PER_PAGE; | 822 | cnt = num_to_init / ENTRIES_PER_PAGE; |
| 823 | pr_info("ftrace: allocating %ld hash entries in %d pages\n", | ||
| 824 | num_to_init, cnt); | ||
| 863 | 825 | ||
| 864 | for (i = 0; i < cnt; i++) { | 826 | for (i = 0; i < cnt; i++) { |
| 865 | pg->next = (void *)get_zeroed_page(GFP_KERNEL); | 827 | pg->next = (void *)get_zeroed_page(GFP_KERNEL); |
| @@ -1556,6 +1518,104 @@ static __init int ftrace_init_debugfs(void) | |||
| 1556 | 1518 | ||
| 1557 | fs_initcall(ftrace_init_debugfs); | 1519 | fs_initcall(ftrace_init_debugfs); |
| 1558 | 1520 | ||
| 1521 | #ifdef CONFIG_FTRACE_MCOUNT_RECORD | ||
| 1522 | static int ftrace_convert_nops(unsigned long *start, | ||
| 1523 | unsigned long *end) | ||
| 1524 | { | ||
| 1525 | unsigned long *p; | ||
| 1526 | unsigned long addr; | ||
| 1527 | unsigned long flags; | ||
| 1528 | |||
| 1529 | p = start; | ||
| 1530 | while (p < end) { | ||
| 1531 | addr = ftrace_call_adjust(*p++); | ||
| 1532 | ftrace_record_ip(addr); | ||
| 1533 | ftrace_shutdown_replenish(); | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | /* p is ignored */ | ||
| 1537 | local_irq_save(flags); | ||
| 1538 | __ftrace_update_code(p); | ||
| 1539 | local_irq_restore(flags); | ||
| 1540 | |||
| 1541 | return 0; | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | extern unsigned long __start_mcount_loc[]; | ||
| 1545 | extern unsigned long __stop_mcount_loc[]; | ||
| 1546 | |||
| 1547 | void __init ftrace_init(void) | ||
| 1548 | { | ||
| 1549 | unsigned long count, addr, flags; | ||
| 1550 | int ret; | ||
| 1551 | |||
| 1552 | /* Keep the ftrace pointer to the stub */ | ||
| 1553 | addr = (unsigned long)ftrace_stub; | ||
| 1554 | |||
| 1555 | local_irq_save(flags); | ||
| 1556 | ftrace_dyn_arch_init(&addr); | ||
| 1557 | local_irq_restore(flags); | ||
| 1558 | |||
| 1559 | /* ftrace_dyn_arch_init places the return code in addr */ | ||
| 1560 | if (addr) | ||
| 1561 | goto failed; | ||
| 1562 | |||
| 1563 | count = __stop_mcount_loc - __start_mcount_loc; | ||
| 1564 | |||
| 1565 | ret = ftrace_dyn_table_alloc(count); | ||
| 1566 | if (ret) | ||
| 1567 | goto failed; | ||
| 1568 | |||
| 1569 | last_ftrace_enabled = ftrace_enabled = 1; | ||
| 1570 | |||
| 1571 | ret = ftrace_convert_nops(__start_mcount_loc, | ||
| 1572 | __stop_mcount_loc); | ||
| 1573 | |||
| 1574 | return; | ||
| 1575 | failed: | ||
| 1576 | ftrace_disabled = 1; | ||
| 1577 | } | ||
| 1578 | #else /* CONFIG_FTRACE_MCOUNT_RECORD */ | ||
| 1579 | static int ftraced(void *ignore) | ||
| 1580 | { | ||
| 1581 | unsigned long usecs; | ||
| 1582 | |||
| 1583 | while (!kthread_should_stop()) { | ||
| 1584 | |||
| 1585 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 1586 | |||
| 1587 | /* check once a second */ | ||
| 1588 | schedule_timeout(HZ); | ||
| 1589 | |||
| 1590 | if (unlikely(ftrace_disabled)) | ||
| 1591 | continue; | ||
| 1592 | |||
| 1593 | mutex_lock(&ftrace_sysctl_lock); | ||
| 1594 | mutex_lock(&ftraced_lock); | ||
| 1595 | if (!ftraced_suspend && !ftraced_stop && | ||
| 1596 | ftrace_update_code()) { | ||
| 1597 | usecs = nsecs_to_usecs(ftrace_update_time); | ||
| 1598 | if (ftrace_update_tot_cnt > 100000) { | ||
| 1599 | ftrace_update_tot_cnt = 0; | ||
| 1600 | pr_info("hm, dftrace overflow: %lu change%s" | ||
| 1601 | " (%lu total) in %lu usec%s\n", | ||
| 1602 | ftrace_update_cnt, | ||
| 1603 | ftrace_update_cnt != 1 ? "s" : "", | ||
| 1604 | ftrace_update_tot_cnt, | ||
| 1605 | usecs, usecs != 1 ? "s" : ""); | ||
| 1606 | ftrace_disabled = 1; | ||
| 1607 | WARN_ON_ONCE(1); | ||
| 1608 | } | ||
| 1609 | } | ||
| 1610 | mutex_unlock(&ftraced_lock); | ||
| 1611 | mutex_unlock(&ftrace_sysctl_lock); | ||
| 1612 | |||
| 1613 | ftrace_shutdown_replenish(); | ||
| 1614 | } | ||
| 1615 | __set_current_state(TASK_RUNNING); | ||
| 1616 | return 0; | ||
| 1617 | } | ||
| 1618 | |||
| 1559 | static int __init ftrace_dynamic_init(void) | 1619 | static int __init ftrace_dynamic_init(void) |
| 1560 | { | 1620 | { |
| 1561 | struct task_struct *p; | 1621 | struct task_struct *p; |
| @@ -1572,7 +1632,7 @@ static int __init ftrace_dynamic_init(void) | |||
| 1572 | goto failed; | 1632 | goto failed; |
| 1573 | } | 1633 | } |
| 1574 | 1634 | ||
| 1575 | ret = ftrace_dyn_table_alloc(); | 1635 | ret = ftrace_dyn_table_alloc(NR_TO_INIT); |
| 1576 | if (ret) | 1636 | if (ret) |
| 1577 | goto failed; | 1637 | goto failed; |
| 1578 | 1638 | ||
| @@ -1593,6 +1653,8 @@ static int __init ftrace_dynamic_init(void) | |||
| 1593 | } | 1653 | } |
| 1594 | 1654 | ||
| 1595 | core_initcall(ftrace_dynamic_init); | 1655 | core_initcall(ftrace_dynamic_init); |
| 1656 | #endif /* CONFIG_FTRACE_MCOUNT_RECORD */ | ||
| 1657 | |||
| 1596 | #else | 1658 | #else |
| 1597 | # define ftrace_startup() do { } while (0) | 1659 | # define ftrace_startup() do { } while (0) |
| 1598 | # define ftrace_shutdown() do { } while (0) | 1660 | # define ftrace_shutdown() do { } while (0) |
