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) |