diff options
-rw-r--r-- | arch/powerpc/Kconfig | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/Makefile | 9 | ||||
-rw-r--r-- | arch/powerpc/kernel/entry_64.S | 58 | ||||
-rw-r--r-- | arch/powerpc/kernel/ftrace.c | 79 | ||||
-rw-r--r-- | arch/powerpc/kernel/process.c | 16 | ||||
-rw-r--r-- | arch/powerpc/kernel/vmlinux.lds.S | 1 |
6 files changed, 154 insertions, 10 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index ccdd8de3c558..e122d241f17d 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -111,6 +111,7 @@ config PPC | |||
111 | select HAVE_FTRACE_MCOUNT_RECORD | 111 | select HAVE_FTRACE_MCOUNT_RECORD |
112 | select HAVE_DYNAMIC_FTRACE | 112 | select HAVE_DYNAMIC_FTRACE |
113 | select HAVE_FUNCTION_TRACER | 113 | select HAVE_FUNCTION_TRACER |
114 | select HAVE_FUNCTION_GRAPH_TRACER if !DYNAMIC_FTRACE && PPC64 | ||
114 | select ARCH_WANT_OPTIONAL_GPIOLIB | 115 | select ARCH_WANT_OPTIONAL_GPIOLIB |
115 | select HAVE_IDE | 116 | select HAVE_IDE |
116 | select HAVE_IOREMAP_PROT | 117 | select HAVE_IOREMAP_PROT |
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index d15992119085..583ba6493a62 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile | |||
@@ -18,12 +18,10 @@ CFLAGS_REMOVE_cputable.o = -pg -mno-sched-epilog | |||
18 | CFLAGS_REMOVE_prom_init.o = -pg -mno-sched-epilog | 18 | CFLAGS_REMOVE_prom_init.o = -pg -mno-sched-epilog |
19 | CFLAGS_REMOVE_btext.o = -pg -mno-sched-epilog | 19 | CFLAGS_REMOVE_btext.o = -pg -mno-sched-epilog |
20 | CFLAGS_REMOVE_prom.o = -pg -mno-sched-epilog | 20 | CFLAGS_REMOVE_prom.o = -pg -mno-sched-epilog |
21 | 21 | # do not trace tracer code | |
22 | ifdef CONFIG_DYNAMIC_FTRACE | ||
23 | # dynamic ftrace setup. | ||
24 | CFLAGS_REMOVE_ftrace.o = -pg -mno-sched-epilog | 22 | CFLAGS_REMOVE_ftrace.o = -pg -mno-sched-epilog |
25 | endif | 23 | # timers used by tracing |
26 | 24 | CFLAGS_REMOVE_time.o = -pg -mno-sched-epilog | |
27 | endif | 25 | endif |
28 | 26 | ||
29 | obj-y := cputable.o ptrace.o syscalls.o \ | 27 | obj-y := cputable.o ptrace.o syscalls.o \ |
@@ -95,6 +93,7 @@ obj-$(CONFIG_AUDIT) += audit.o | |||
95 | obj64-$(CONFIG_AUDIT) += compat_audit.o | 93 | obj64-$(CONFIG_AUDIT) += compat_audit.o |
96 | 94 | ||
97 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o | 95 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o |
96 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o | ||
98 | 97 | ||
99 | obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o | 98 | obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o |
100 | 99 | ||
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 383ed6eb0085..a32699e74c3c 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S | |||
@@ -931,13 +931,65 @@ _GLOBAL(_mcount) | |||
931 | ld r5,0(r5) | 931 | ld r5,0(r5) |
932 | mtctr r5 | 932 | mtctr r5 |
933 | bctrl | 933 | bctrl |
934 | |||
935 | nop | 934 | nop |
935 | |||
936 | |||
937 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
938 | b ftrace_graph_caller | ||
939 | #endif | ||
936 | ld r0, 128(r1) | 940 | ld r0, 128(r1) |
937 | mtlr r0 | 941 | mtlr r0 |
938 | addi r1, r1, 112 | 942 | addi r1, r1, 112 |
939 | _GLOBAL(ftrace_stub) | 943 | _GLOBAL(ftrace_stub) |
940 | blr | 944 | blr |
941 | 945 | ||
942 | #endif | 946 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
943 | #endif | 947 | |
948 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
949 | ftrace_graph_caller: | ||
950 | /* load r4 with local address */ | ||
951 | ld r4, 128(r1) | ||
952 | subi r4, r4, MCOUNT_INSN_SIZE | ||
953 | |||
954 | /* get the parent address */ | ||
955 | ld r11, 112(r1) | ||
956 | addi r3, r11, 16 | ||
957 | |||
958 | bl .prepare_ftrace_return | ||
959 | nop | ||
960 | |||
961 | ld r0, 128(r1) | ||
962 | mtlr r0 | ||
963 | addi r1, r1, 112 | ||
964 | blr | ||
965 | |||
966 | _GLOBAL(return_to_handler) | ||
967 | /* need to save return values */ | ||
968 | std r4, -32(r1) | ||
969 | std r3, -24(r1) | ||
970 | /* save TOC */ | ||
971 | std r2, -16(r1) | ||
972 | std r31, -8(r1) | ||
973 | mr r31, r1 | ||
974 | stdu r1, -112(r1) | ||
975 | |||
976 | /* update the TOC */ | ||
977 | LOAD_REG_IMMEDIATE(r4,ftrace_return_to_handler) | ||
978 | ld r2, 8(r4) | ||
979 | |||
980 | bl .ftrace_return_to_handler | ||
981 | nop | ||
982 | |||
983 | /* return value has real return address */ | ||
984 | mtlr r3 | ||
985 | |||
986 | ld r1, 0(r1) | ||
987 | ld r4, -32(r1) | ||
988 | ld r3, -24(r1) | ||
989 | ld r2, -16(r1) | ||
990 | ld r31, -8(r1) | ||
991 | |||
992 | /* Jump back to real return address */ | ||
993 | blr | ||
994 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
995 | #endif /* CONFIG_FUNCTION_TRACER */ | ||
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index 4112175183d3..c9b1547f65a5 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c | |||
@@ -5,6 +5,9 @@ | |||
5 | * | 5 | * |
6 | * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box. | 6 | * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box. |
7 | * | 7 | * |
8 | * Added function graph tracer code, taken from x86 that was written | ||
9 | * by Frederic Weisbecker, and ported to PPC by Steven Rostedt. | ||
10 | * | ||
8 | */ | 11 | */ |
9 | 12 | ||
10 | #include <linux/spinlock.h> | 13 | #include <linux/spinlock.h> |
@@ -20,8 +23,6 @@ | |||
20 | #include <asm/code-patching.h> | 23 | #include <asm/code-patching.h> |
21 | #include <asm/ftrace.h> | 24 | #include <asm/ftrace.h> |
22 | 25 | ||
23 | static unsigned int ftrace_nop = PPC_NOP_INSTR; | ||
24 | |||
25 | #ifdef CONFIG_PPC32 | 26 | #ifdef CONFIG_PPC32 |
26 | # define GET_ADDR(addr) addr | 27 | # define GET_ADDR(addr) addr |
27 | #else | 28 | #else |
@@ -29,6 +30,8 @@ static unsigned int ftrace_nop = PPC_NOP_INSTR; | |||
29 | # define GET_ADDR(addr) (*(unsigned long *)addr) | 30 | # define GET_ADDR(addr) (*(unsigned long *)addr) |
30 | #endif | 31 | #endif |
31 | 32 | ||
33 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
34 | static unsigned int ftrace_nop = PPC_NOP_INSTR; | ||
32 | 35 | ||
33 | static unsigned int ftrace_calc_offset(long ip, long addr) | 36 | static unsigned int ftrace_calc_offset(long ip, long addr) |
34 | { | 37 | { |
@@ -525,3 +528,75 @@ int __init ftrace_dyn_arch_init(void *data) | |||
525 | 528 | ||
526 | return 0; | 529 | return 0; |
527 | } | 530 | } |
531 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
532 | |||
533 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
534 | |||
535 | /* | ||
536 | * Hook the return address and push it in the stack of return addrs | ||
537 | * in current thread info. | ||
538 | */ | ||
539 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | ||
540 | { | ||
541 | unsigned long old; | ||
542 | unsigned long long calltime; | ||
543 | int faulted; | ||
544 | struct ftrace_graph_ent trace; | ||
545 | unsigned long return_hooker = (unsigned long) | ||
546 | &return_to_handler; | ||
547 | |||
548 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) | ||
549 | return; | ||
550 | |||
551 | return_hooker = GET_ADDR(return_hooker); | ||
552 | |||
553 | /* | ||
554 | * Protect against fault, even if it shouldn't | ||
555 | * happen. This tool is too much intrusive to | ||
556 | * ignore such a protection. | ||
557 | */ | ||
558 | asm volatile( | ||
559 | "1: " PPC_LL "%[old], 0(%[parent])\n" | ||
560 | "2: " PPC_STL "%[return_hooker], 0(%[parent])\n" | ||
561 | " li %[faulted], 0\n" | ||
562 | "3:" | ||
563 | |||
564 | ".section .fixup, \"ax\"\n" | ||
565 | "4: li %[faulted], 1\n" | ||
566 | " b 3b\n" | ||
567 | ".previous\n" | ||
568 | |||
569 | ".section __ex_table,\"a\"\n" | ||
570 | PPC_LONG_ALIGN "\n" | ||
571 | PPC_LONG "1b,4b\n" | ||
572 | PPC_LONG "2b,4b\n" | ||
573 | ".previous" | ||
574 | |||
575 | : [old] "=r" (old), [faulted] "=r" (faulted) | ||
576 | : [parent] "r" (parent), [return_hooker] "r" (return_hooker) | ||
577 | : "memory" | ||
578 | ); | ||
579 | |||
580 | if (unlikely(faulted)) { | ||
581 | ftrace_graph_stop(); | ||
582 | WARN_ON(1); | ||
583 | return; | ||
584 | } | ||
585 | |||
586 | calltime = cpu_clock(raw_smp_processor_id()); | ||
587 | |||
588 | if (ftrace_push_return_trace(old, calltime, | ||
589 | self_addr, &trace.depth) == -EBUSY) { | ||
590 | *parent = old; | ||
591 | return; | ||
592 | } | ||
593 | |||
594 | trace.func = self_addr; | ||
595 | |||
596 | /* Only trace if the calling function expects to */ | ||
597 | if (!ftrace_graph_entry(&trace)) { | ||
598 | current->curr_ret_stack--; | ||
599 | *parent = old; | ||
600 | } | ||
601 | } | ||
602 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index fb7049c054c0..8ede428e76c0 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/mqueue.h> | 33 | #include <linux/mqueue.h> |
34 | #include <linux/hardirq.h> | 34 | #include <linux/hardirq.h> |
35 | #include <linux/utsname.h> | 35 | #include <linux/utsname.h> |
36 | #include <linux/ftrace.h> | ||
36 | #include <linux/kernel_stat.h> | 37 | #include <linux/kernel_stat.h> |
37 | 38 | ||
38 | #include <asm/pgtable.h> | 39 | #include <asm/pgtable.h> |
@@ -1008,6 +1009,14 @@ void show_stack(struct task_struct *tsk, unsigned long *stack) | |||
1008 | unsigned long sp, ip, lr, newsp; | 1009 | unsigned long sp, ip, lr, newsp; |
1009 | int count = 0; | 1010 | int count = 0; |
1010 | int firstframe = 1; | 1011 | int firstframe = 1; |
1012 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
1013 | int curr_frame = current->curr_ret_stack; | ||
1014 | extern void return_to_handler(void); | ||
1015 | unsigned long addr = (unsigned long)return_to_handler; | ||
1016 | #ifdef CONFIG_PPC64 | ||
1017 | addr = *(unsigned long*)addr; | ||
1018 | #endif | ||
1019 | #endif | ||
1011 | 1020 | ||
1012 | sp = (unsigned long) stack; | 1021 | sp = (unsigned long) stack; |
1013 | if (tsk == NULL) | 1022 | if (tsk == NULL) |
@@ -1030,6 +1039,13 @@ void show_stack(struct task_struct *tsk, unsigned long *stack) | |||
1030 | ip = stack[STACK_FRAME_LR_SAVE]; | 1039 | ip = stack[STACK_FRAME_LR_SAVE]; |
1031 | if (!firstframe || ip != lr) { | 1040 | if (!firstframe || ip != lr) { |
1032 | printk("["REG"] ["REG"] %pS", sp, ip, (void *)ip); | 1041 | printk("["REG"] ["REG"] %pS", sp, ip, (void *)ip); |
1042 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
1043 | if (ip == addr && curr_frame >= 0) { | ||
1044 | printk(" (%pS)", | ||
1045 | (void *)current->ret_stack[curr_frame].ret); | ||
1046 | curr_frame--; | ||
1047 | } | ||
1048 | #endif | ||
1033 | if (firstframe) | 1049 | if (firstframe) |
1034 | printk(" (unreliable)"); | 1050 | printk(" (unreliable)"); |
1035 | printk("\n"); | 1051 | printk("\n"); |
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 161b9b9691f0..895af44bf1f4 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S | |||
@@ -58,6 +58,7 @@ SECTIONS | |||
58 | SCHED_TEXT | 58 | SCHED_TEXT |
59 | LOCK_TEXT | 59 | LOCK_TEXT |
60 | KPROBES_TEXT | 60 | KPROBES_TEXT |
61 | IRQENTRY_TEXT | ||
61 | 62 | ||
62 | #ifdef CONFIG_PPC32 | 63 | #ifdef CONFIG_PPC32 |
63 | *(.got1) | 64 | *(.got1) |