diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2012-09-28 04:15:20 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-01-21 13:22:36 -0500 |
commit | e7dbfe349d12eabb7783b117e0c115f6f3d9ef9e (patch) | |
tree | 8b567abaef12e5bb82171eea70e7f02816958ae9 | |
parent | 06aeaaeabf69da4a3e86df532425640f51b01cef (diff) |
kprobes/x86: Move ftrace-based kprobe code into kprobes-ftrace.c
Split ftrace-based kprobes code from kprobes, and introduce
CONFIG_(HAVE_)KPROBES_ON_FTRACE Kconfig flags.
For the cleanup reason, this also moves kprobe_ftrace check
into skip_singlestep.
Link: http://lkml.kernel.org/r/20120928081520.3560.25624.stgit@ltc138.sdl.hitachi.co.jp
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | arch/Kconfig | 12 | ||||
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/kprobes-common.h | 11 | ||||
-rw-r--r-- | arch/x86/kernel/kprobes-ftrace.c | 93 | ||||
-rw-r--r-- | arch/x86/kernel/kprobes.c | 70 | ||||
-rw-r--r-- | include/linux/kprobes.h | 12 | ||||
-rw-r--r-- | kernel/kprobes.c | 8 |
8 files changed, 125 insertions, 83 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index 7f8f281f2585..97fb7d0365d1 100644 --- a/arch/Kconfig +++ b/arch/Kconfig | |||
@@ -76,6 +76,15 @@ config OPTPROBES | |||
76 | depends on KPROBES && HAVE_OPTPROBES | 76 | depends on KPROBES && HAVE_OPTPROBES |
77 | depends on !PREEMPT | 77 | depends on !PREEMPT |
78 | 78 | ||
79 | config KPROBES_ON_FTRACE | ||
80 | def_bool y | ||
81 | depends on KPROBES && HAVE_KPROBES_ON_FTRACE | ||
82 | depends on DYNAMIC_FTRACE_WITH_REGS | ||
83 | help | ||
84 | If function tracer is enabled and the arch supports full | ||
85 | passing of pt_regs to function tracing, then kprobes can | ||
86 | optimize on top of function tracing. | ||
87 | |||
79 | config UPROBES | 88 | config UPROBES |
80 | bool "Transparent user-space probes (EXPERIMENTAL)" | 89 | bool "Transparent user-space probes (EXPERIMENTAL)" |
81 | depends on UPROBE_EVENT && PERF_EVENTS | 90 | depends on UPROBE_EVENT && PERF_EVENTS |
@@ -158,6 +167,9 @@ config HAVE_KRETPROBES | |||
158 | config HAVE_OPTPROBES | 167 | config HAVE_OPTPROBES |
159 | bool | 168 | bool |
160 | 169 | ||
170 | config HAVE_KPROBES_ON_FTRACE | ||
171 | bool | ||
172 | |||
161 | config HAVE_NMI_WATCHDOG | 173 | config HAVE_NMI_WATCHDOG |
162 | bool | 174 | bool |
163 | # | 175 | # |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 996ccecc694c..be8b2b3ab979 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -40,6 +40,7 @@ config X86 | |||
40 | select HAVE_DMA_CONTIGUOUS if !SWIOTLB | 40 | select HAVE_DMA_CONTIGUOUS if !SWIOTLB |
41 | select HAVE_KRETPROBES | 41 | select HAVE_KRETPROBES |
42 | select HAVE_OPTPROBES | 42 | select HAVE_OPTPROBES |
43 | select HAVE_KPROBES_ON_FTRACE | ||
43 | select HAVE_FTRACE_MCOUNT_RECORD | 44 | select HAVE_FTRACE_MCOUNT_RECORD |
44 | select HAVE_FENTRY if X86_64 | 45 | select HAVE_FENTRY if X86_64 |
45 | select HAVE_C_RECORDMCOUNT | 46 | select HAVE_C_RECORDMCOUNT |
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 34e923a53762..cc5d31f8830c 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -67,6 +67,7 @@ obj-$(CONFIG_KEXEC) += relocate_kernel_$(BITS).o crash.o | |||
67 | obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o | 67 | obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o |
68 | obj-$(CONFIG_KPROBES) += kprobes.o | 68 | obj-$(CONFIG_KPROBES) += kprobes.o |
69 | obj-$(CONFIG_OPTPROBES) += kprobes-opt.o | 69 | obj-$(CONFIG_OPTPROBES) += kprobes-opt.o |
70 | obj-$(CONFIG_KPROBES_ON_FTRACE) += kprobes-ftrace.o | ||
70 | obj-$(CONFIG_MODULES) += module.o | 71 | obj-$(CONFIG_MODULES) += module.o |
71 | obj-$(CONFIG_DOUBLEFAULT) += doublefault_32.o | 72 | obj-$(CONFIG_DOUBLEFAULT) += doublefault_32.o |
72 | obj-$(CONFIG_KGDB) += kgdb.o | 73 | obj-$(CONFIG_KGDB) += kgdb.o |
diff --git a/arch/x86/kernel/kprobes-common.h b/arch/x86/kernel/kprobes-common.h index 3230b68ef29a..2e9d4b5af036 100644 --- a/arch/x86/kernel/kprobes-common.h +++ b/arch/x86/kernel/kprobes-common.h | |||
@@ -99,4 +99,15 @@ static inline unsigned long __recover_optprobed_insn(kprobe_opcode_t *buf, unsig | |||
99 | return addr; | 99 | return addr; |
100 | } | 100 | } |
101 | #endif | 101 | #endif |
102 | |||
103 | #ifdef CONFIG_KPROBES_ON_FTRACE | ||
104 | extern int skip_singlestep(struct kprobe *p, struct pt_regs *regs, | ||
105 | struct kprobe_ctlblk *kcb); | ||
106 | #else | ||
107 | static inline int skip_singlestep(struct kprobe *p, struct pt_regs *regs, | ||
108 | struct kprobe_ctlblk *kcb) | ||
109 | { | ||
110 | return 0; | ||
111 | } | ||
112 | #endif | ||
102 | #endif | 113 | #endif |
diff --git a/arch/x86/kernel/kprobes-ftrace.c b/arch/x86/kernel/kprobes-ftrace.c new file mode 100644 index 000000000000..70a81c7aa0a7 --- /dev/null +++ b/arch/x86/kernel/kprobes-ftrace.c | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * Dynamic Ftrace based Kprobes Optimization | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * Copyright (C) Hitachi Ltd., 2012 | ||
19 | */ | ||
20 | #include <linux/kprobes.h> | ||
21 | #include <linux/ptrace.h> | ||
22 | #include <linux/hardirq.h> | ||
23 | #include <linux/preempt.h> | ||
24 | #include <linux/ftrace.h> | ||
25 | |||
26 | #include "kprobes-common.h" | ||
27 | |||
28 | static int __skip_singlestep(struct kprobe *p, struct pt_regs *regs, | ||
29 | struct kprobe_ctlblk *kcb) | ||
30 | { | ||
31 | /* | ||
32 | * Emulate singlestep (and also recover regs->ip) | ||
33 | * as if there is a 5byte nop | ||
34 | */ | ||
35 | regs->ip = (unsigned long)p->addr + MCOUNT_INSN_SIZE; | ||
36 | if (unlikely(p->post_handler)) { | ||
37 | kcb->kprobe_status = KPROBE_HIT_SSDONE; | ||
38 | p->post_handler(p, regs, 0); | ||
39 | } | ||
40 | __this_cpu_write(current_kprobe, NULL); | ||
41 | return 1; | ||
42 | } | ||
43 | |||
44 | int __kprobes skip_singlestep(struct kprobe *p, struct pt_regs *regs, | ||
45 | struct kprobe_ctlblk *kcb) | ||
46 | { | ||
47 | if (kprobe_ftrace(p)) | ||
48 | return __skip_singlestep(p, regs, kcb); | ||
49 | else | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | /* Ftrace callback handler for kprobes */ | ||
54 | void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, | ||
55 | struct ftrace_ops *ops, struct pt_regs *regs) | ||
56 | { | ||
57 | struct kprobe *p; | ||
58 | struct kprobe_ctlblk *kcb; | ||
59 | unsigned long flags; | ||
60 | |||
61 | /* Disable irq for emulating a breakpoint and avoiding preempt */ | ||
62 | local_irq_save(flags); | ||
63 | |||
64 | p = get_kprobe((kprobe_opcode_t *)ip); | ||
65 | if (unlikely(!p) || kprobe_disabled(p)) | ||
66 | goto end; | ||
67 | |||
68 | kcb = get_kprobe_ctlblk(); | ||
69 | if (kprobe_running()) { | ||
70 | kprobes_inc_nmissed_count(p); | ||
71 | } else { | ||
72 | /* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */ | ||
73 | regs->ip = ip + sizeof(kprobe_opcode_t); | ||
74 | |||
75 | __this_cpu_write(current_kprobe, p); | ||
76 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; | ||
77 | if (!p->pre_handler || !p->pre_handler(p, regs)) | ||
78 | __skip_singlestep(p, regs, kcb); | ||
79 | /* | ||
80 | * If pre_handler returns !0, it sets regs->ip and | ||
81 | * resets current kprobe. | ||
82 | */ | ||
83 | } | ||
84 | end: | ||
85 | local_irq_restore(flags); | ||
86 | } | ||
87 | |||
88 | int __kprobes arch_prepare_kprobe_ftrace(struct kprobe *p) | ||
89 | { | ||
90 | p->ainsn.insn = NULL; | ||
91 | p->ainsn.boostable = -1; | ||
92 | return 0; | ||
93 | } | ||
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 57916c0d3cf6..18114bfb10f3 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
@@ -541,23 +541,6 @@ reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb | |||
541 | return 1; | 541 | return 1; |
542 | } | 542 | } |
543 | 543 | ||
544 | #ifdef KPROBES_CAN_USE_FTRACE | ||
545 | static void __kprobes skip_singlestep(struct kprobe *p, struct pt_regs *regs, | ||
546 | struct kprobe_ctlblk *kcb) | ||
547 | { | ||
548 | /* | ||
549 | * Emulate singlestep (and also recover regs->ip) | ||
550 | * as if there is a 5byte nop | ||
551 | */ | ||
552 | regs->ip = (unsigned long)p->addr + MCOUNT_INSN_SIZE; | ||
553 | if (unlikely(p->post_handler)) { | ||
554 | kcb->kprobe_status = KPROBE_HIT_SSDONE; | ||
555 | p->post_handler(p, regs, 0); | ||
556 | } | ||
557 | __this_cpu_write(current_kprobe, NULL); | ||
558 | } | ||
559 | #endif | ||
560 | |||
561 | /* | 544 | /* |
562 | * Interrupts are disabled on entry as trap3 is an interrupt gate and they | 545 | * Interrupts are disabled on entry as trap3 is an interrupt gate and they |
563 | * remain disabled throughout this function. | 546 | * remain disabled throughout this function. |
@@ -616,13 +599,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) | |||
616 | } else if (kprobe_running()) { | 599 | } else if (kprobe_running()) { |
617 | p = __this_cpu_read(current_kprobe); | 600 | p = __this_cpu_read(current_kprobe); |
618 | if (p->break_handler && p->break_handler(p, regs)) { | 601 | if (p->break_handler && p->break_handler(p, regs)) { |
619 | #ifdef KPROBES_CAN_USE_FTRACE | 602 | if (!skip_singlestep(p, regs, kcb)) |
620 | if (kprobe_ftrace(p)) { | 603 | setup_singlestep(p, regs, kcb, 0); |
621 | skip_singlestep(p, regs, kcb); | ||
622 | return 1; | ||
623 | } | ||
624 | #endif | ||
625 | setup_singlestep(p, regs, kcb, 0); | ||
626 | return 1; | 604 | return 1; |
627 | } | 605 | } |
628 | } /* else: not a kprobe fault; let the kernel handle it */ | 606 | } /* else: not a kprobe fault; let the kernel handle it */ |
@@ -1075,50 +1053,6 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | |||
1075 | return 0; | 1053 | return 0; |
1076 | } | 1054 | } |
1077 | 1055 | ||
1078 | #ifdef KPROBES_CAN_USE_FTRACE | ||
1079 | /* Ftrace callback handler for kprobes */ | ||
1080 | void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, | ||
1081 | struct ftrace_ops *ops, struct pt_regs *regs) | ||
1082 | { | ||
1083 | struct kprobe *p; | ||
1084 | struct kprobe_ctlblk *kcb; | ||
1085 | unsigned long flags; | ||
1086 | |||
1087 | /* Disable irq for emulating a breakpoint and avoiding preempt */ | ||
1088 | local_irq_save(flags); | ||
1089 | |||
1090 | p = get_kprobe((kprobe_opcode_t *)ip); | ||
1091 | if (unlikely(!p) || kprobe_disabled(p)) | ||
1092 | goto end; | ||
1093 | |||
1094 | kcb = get_kprobe_ctlblk(); | ||
1095 | if (kprobe_running()) { | ||
1096 | kprobes_inc_nmissed_count(p); | ||
1097 | } else { | ||
1098 | /* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */ | ||
1099 | regs->ip = ip + sizeof(kprobe_opcode_t); | ||
1100 | |||
1101 | __this_cpu_write(current_kprobe, p); | ||
1102 | kcb->kprobe_status = KPROBE_HIT_ACTIVE; | ||
1103 | if (!p->pre_handler || !p->pre_handler(p, regs)) | ||
1104 | skip_singlestep(p, regs, kcb); | ||
1105 | /* | ||
1106 | * If pre_handler returns !0, it sets regs->ip and | ||
1107 | * resets current kprobe. | ||
1108 | */ | ||
1109 | } | ||
1110 | end: | ||
1111 | local_irq_restore(flags); | ||
1112 | } | ||
1113 | |||
1114 | int __kprobes arch_prepare_kprobe_ftrace(struct kprobe *p) | ||
1115 | { | ||
1116 | p->ainsn.insn = NULL; | ||
1117 | p->ainsn.boostable = -1; | ||
1118 | return 0; | ||
1119 | } | ||
1120 | #endif | ||
1121 | |||
1122 | int __init arch_init_kprobes(void) | 1056 | int __init arch_init_kprobes(void) |
1123 | { | 1057 | { |
1124 | return arch_init_optprobes(); | 1058 | return arch_init_optprobes(); |
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 23755ba42abc..4b6ef4d33cc2 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h | |||
@@ -49,16 +49,6 @@ | |||
49 | #define KPROBE_REENTER 0x00000004 | 49 | #define KPROBE_REENTER 0x00000004 |
50 | #define KPROBE_HIT_SSDONE 0x00000008 | 50 | #define KPROBE_HIT_SSDONE 0x00000008 |
51 | 51 | ||
52 | /* | ||
53 | * If function tracer is enabled and the arch supports full | ||
54 | * passing of pt_regs to function tracing, then kprobes can | ||
55 | * optimize on top of function tracing. | ||
56 | */ | ||
57 | #if defined(CONFIG_FUNCTION_TRACER) && defined(ARCH_SUPPORTS_FTRACE_SAVE_REGS) \ | ||
58 | && defined(ARCH_SUPPORTS_KPROBES_ON_FTRACE) | ||
59 | # define KPROBES_CAN_USE_FTRACE | ||
60 | #endif | ||
61 | |||
62 | /* Attach to insert probes on any functions which should be ignored*/ | 52 | /* Attach to insert probes on any functions which should be ignored*/ |
63 | #define __kprobes __attribute__((__section__(".kprobes.text"))) | 53 | #define __kprobes __attribute__((__section__(".kprobes.text"))) |
64 | 54 | ||
@@ -316,7 +306,7 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table, | |||
316 | #endif | 306 | #endif |
317 | 307 | ||
318 | #endif /* CONFIG_OPTPROBES */ | 308 | #endif /* CONFIG_OPTPROBES */ |
319 | #ifdef KPROBES_CAN_USE_FTRACE | 309 | #ifdef CONFIG_KPROBES_ON_FTRACE |
320 | extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, | 310 | extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, |
321 | struct ftrace_ops *ops, struct pt_regs *regs); | 311 | struct ftrace_ops *ops, struct pt_regs *regs); |
322 | extern int arch_prepare_kprobe_ftrace(struct kprobe *p); | 312 | extern int arch_prepare_kprobe_ftrace(struct kprobe *p); |
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 098f396aa409..f423c3ef4a82 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -919,7 +919,7 @@ static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p) | |||
919 | } | 919 | } |
920 | #endif /* CONFIG_OPTPROBES */ | 920 | #endif /* CONFIG_OPTPROBES */ |
921 | 921 | ||
922 | #ifdef KPROBES_CAN_USE_FTRACE | 922 | #ifdef CONFIG_KPROBES_ON_FTRACE |
923 | static struct ftrace_ops kprobe_ftrace_ops __read_mostly = { | 923 | static struct ftrace_ops kprobe_ftrace_ops __read_mostly = { |
924 | .func = kprobe_ftrace_handler, | 924 | .func = kprobe_ftrace_handler, |
925 | .flags = FTRACE_OPS_FL_SAVE_REGS, | 925 | .flags = FTRACE_OPS_FL_SAVE_REGS, |
@@ -964,7 +964,7 @@ static void __kprobes disarm_kprobe_ftrace(struct kprobe *p) | |||
964 | (unsigned long)p->addr, 1, 0); | 964 | (unsigned long)p->addr, 1, 0); |
965 | WARN(ret < 0, "Failed to disarm kprobe-ftrace at %p (%d)\n", p->addr, ret); | 965 | WARN(ret < 0, "Failed to disarm kprobe-ftrace at %p (%d)\n", p->addr, ret); |
966 | } | 966 | } |
967 | #else /* !KPROBES_CAN_USE_FTRACE */ | 967 | #else /* !CONFIG_KPROBES_ON_FTRACE */ |
968 | #define prepare_kprobe(p) arch_prepare_kprobe(p) | 968 | #define prepare_kprobe(p) arch_prepare_kprobe(p) |
969 | #define arm_kprobe_ftrace(p) do {} while (0) | 969 | #define arm_kprobe_ftrace(p) do {} while (0) |
970 | #define disarm_kprobe_ftrace(p) do {} while (0) | 970 | #define disarm_kprobe_ftrace(p) do {} while (0) |
@@ -1414,12 +1414,12 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p, | |||
1414 | */ | 1414 | */ |
1415 | ftrace_addr = ftrace_location((unsigned long)p->addr); | 1415 | ftrace_addr = ftrace_location((unsigned long)p->addr); |
1416 | if (ftrace_addr) { | 1416 | if (ftrace_addr) { |
1417 | #ifdef KPROBES_CAN_USE_FTRACE | 1417 | #ifdef CONFIG_KPROBES_ON_FTRACE |
1418 | /* Given address is not on the instruction boundary */ | 1418 | /* Given address is not on the instruction boundary */ |
1419 | if ((unsigned long)p->addr != ftrace_addr) | 1419 | if ((unsigned long)p->addr != ftrace_addr) |
1420 | return -EILSEQ; | 1420 | return -EILSEQ; |
1421 | p->flags |= KPROBE_FLAG_FTRACE; | 1421 | p->flags |= KPROBE_FLAG_FTRACE; |
1422 | #else /* !KPROBES_CAN_USE_FTRACE */ | 1422 | #else /* !CONFIG_KPROBES_ON_FTRACE */ |
1423 | return -EINVAL; | 1423 | return -EINVAL; |
1424 | #endif | 1424 | #endif |
1425 | } | 1425 | } |