diff options
author | H. Peter Anvin <hpa@zytor.com> | 2012-06-01 18:55:31 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2012-06-01 18:55:31 -0400 |
commit | 40b46a7d2938589a5abab132a7824fd17ae18f62 (patch) | |
tree | 2bdf9d2449b6f3437675f1398698e170aebb37cb /arch/x86/kernel/ftrace.c | |
parent | bad1a753d4d4deb09d4bc0bac1dd4fc3298502e9 (diff) | |
parent | 5963e317b1e9d2a4511503916d8fd664bb8fa8fb (diff) |
Merge remote-tracking branch 'rostedt/tip/perf/urgent-2' into x86-urgent-for-linus
Diffstat (limited to 'arch/x86/kernel/ftrace.c')
-rw-r--r-- | arch/x86/kernel/ftrace.c | 102 |
1 files changed, 95 insertions, 7 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 32ff36596ab..c3a7cb4bf6e 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -100,7 +100,7 @@ static const unsigned char *ftrace_nop_replace(void) | |||
100 | } | 100 | } |
101 | 101 | ||
102 | static int | 102 | static int |
103 | ftrace_modify_code(unsigned long ip, unsigned const char *old_code, | 103 | ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code, |
104 | unsigned const char *new_code) | 104 | unsigned const char *new_code) |
105 | { | 105 | { |
106 | unsigned char replaced[MCOUNT_INSN_SIZE]; | 106 | unsigned char replaced[MCOUNT_INSN_SIZE]; |
@@ -141,7 +141,20 @@ int ftrace_make_nop(struct module *mod, | |||
141 | old = ftrace_call_replace(ip, addr); | 141 | old = ftrace_call_replace(ip, addr); |
142 | new = ftrace_nop_replace(); | 142 | new = ftrace_nop_replace(); |
143 | 143 | ||
144 | return ftrace_modify_code(rec->ip, old, new); | 144 | /* |
145 | * On boot up, and when modules are loaded, the MCOUNT_ADDR | ||
146 | * is converted to a nop, and will never become MCOUNT_ADDR | ||
147 | * again. This code is either running before SMP (on boot up) | ||
148 | * or before the code will ever be executed (module load). | ||
149 | * We do not want to use the breakpoint version in this case, | ||
150 | * just modify the code directly. | ||
151 | */ | ||
152 | if (addr == MCOUNT_ADDR) | ||
153 | return ftrace_modify_code_direct(rec->ip, old, new); | ||
154 | |||
155 | /* Normal cases use add_brk_on_nop */ | ||
156 | WARN_ONCE(1, "invalid use of ftrace_make_nop"); | ||
157 | return -EINVAL; | ||
145 | } | 158 | } |
146 | 159 | ||
147 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 160 | int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) |
@@ -152,9 +165,47 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
152 | old = ftrace_nop_replace(); | 165 | old = ftrace_nop_replace(); |
153 | new = ftrace_call_replace(ip, addr); | 166 | new = ftrace_call_replace(ip, addr); |
154 | 167 | ||
155 | return ftrace_modify_code(rec->ip, old, new); | 168 | /* Should only be called when module is loaded */ |
169 | return ftrace_modify_code_direct(rec->ip, old, new); | ||
156 | } | 170 | } |
157 | 171 | ||
172 | /* | ||
173 | * The modifying_ftrace_code is used to tell the breakpoint | ||
174 | * handler to call ftrace_int3_handler(). If it fails to | ||
175 | * call this handler for a breakpoint added by ftrace, then | ||
176 | * the kernel may crash. | ||
177 | * | ||
178 | * As atomic_writes on x86 do not need a barrier, we do not | ||
179 | * need to add smp_mb()s for this to work. It is also considered | ||
180 | * that we can not read the modifying_ftrace_code before | ||
181 | * executing the breakpoint. That would be quite remarkable if | ||
182 | * it could do that. Here's the flow that is required: | ||
183 | * | ||
184 | * CPU-0 CPU-1 | ||
185 | * | ||
186 | * atomic_inc(mfc); | ||
187 | * write int3s | ||
188 | * <trap-int3> // implicit (r)mb | ||
189 | * if (atomic_read(mfc)) | ||
190 | * call ftrace_int3_handler() | ||
191 | * | ||
192 | * Then when we are finished: | ||
193 | * | ||
194 | * atomic_dec(mfc); | ||
195 | * | ||
196 | * If we hit a breakpoint that was not set by ftrace, it does not | ||
197 | * matter if ftrace_int3_handler() is called or not. It will | ||
198 | * simply be ignored. But it is crucial that a ftrace nop/caller | ||
199 | * breakpoint is handled. No other user should ever place a | ||
200 | * breakpoint on an ftrace nop/caller location. It must only | ||
201 | * be done by this code. | ||
202 | */ | ||
203 | atomic_t modifying_ftrace_code __read_mostly; | ||
204 | |||
205 | static int | ||
206 | ftrace_modify_code(unsigned long ip, unsigned const char *old_code, | ||
207 | unsigned const char *new_code); | ||
208 | |||
158 | int ftrace_update_ftrace_func(ftrace_func_t func) | 209 | int ftrace_update_ftrace_func(ftrace_func_t func) |
159 | { | 210 | { |
160 | unsigned long ip = (unsigned long)(&ftrace_call); | 211 | unsigned long ip = (unsigned long)(&ftrace_call); |
@@ -163,13 +214,17 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
163 | 214 | ||
164 | memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); | 215 | memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); |
165 | new = ftrace_call_replace(ip, (unsigned long)func); | 216 | new = ftrace_call_replace(ip, (unsigned long)func); |
217 | |||
218 | /* See comment above by declaration of modifying_ftrace_code */ | ||
219 | atomic_inc(&modifying_ftrace_code); | ||
220 | |||
166 | ret = ftrace_modify_code(ip, old, new); | 221 | ret = ftrace_modify_code(ip, old, new); |
167 | 222 | ||
223 | atomic_dec(&modifying_ftrace_code); | ||
224 | |||
168 | return ret; | 225 | return ret; |
169 | } | 226 | } |
170 | 227 | ||
171 | int modifying_ftrace_code __read_mostly; | ||
172 | |||
173 | /* | 228 | /* |
174 | * A breakpoint was added to the code address we are about to | 229 | * A breakpoint was added to the code address we are about to |
175 | * modify, and this is the handle that will just skip over it. | 230 | * modify, and this is the handle that will just skip over it. |
@@ -489,13 +544,46 @@ void ftrace_replace_code(int enable) | |||
489 | } | 544 | } |
490 | } | 545 | } |
491 | 546 | ||
547 | static int | ||
548 | ftrace_modify_code(unsigned long ip, unsigned const char *old_code, | ||
549 | unsigned const char *new_code) | ||
550 | { | ||
551 | int ret; | ||
552 | |||
553 | ret = add_break(ip, old_code); | ||
554 | if (ret) | ||
555 | goto out; | ||
556 | |||
557 | run_sync(); | ||
558 | |||
559 | ret = add_update_code(ip, new_code); | ||
560 | if (ret) | ||
561 | goto fail_update; | ||
562 | |||
563 | run_sync(); | ||
564 | |||
565 | ret = ftrace_write(ip, new_code, 1); | ||
566 | if (ret) { | ||
567 | ret = -EPERM; | ||
568 | goto out; | ||
569 | } | ||
570 | run_sync(); | ||
571 | out: | ||
572 | return ret; | ||
573 | |||
574 | fail_update: | ||
575 | probe_kernel_write((void *)ip, &old_code[0], 1); | ||
576 | goto out; | ||
577 | } | ||
578 | |||
492 | void arch_ftrace_update_code(int command) | 579 | void arch_ftrace_update_code(int command) |
493 | { | 580 | { |
494 | modifying_ftrace_code++; | 581 | /* See comment above by declaration of modifying_ftrace_code */ |
582 | atomic_inc(&modifying_ftrace_code); | ||
495 | 583 | ||
496 | ftrace_modify_all_code(command); | 584 | ftrace_modify_all_code(command); |
497 | 585 | ||
498 | modifying_ftrace_code--; | 586 | atomic_dec(&modifying_ftrace_code); |
499 | } | 587 | } |
500 | 588 | ||
501 | int __init ftrace_dyn_arch_init(void *data) | 589 | int __init ftrace_dyn_arch_init(void *data) |