diff options
-rw-r--r-- | arch/x86/kernel/ftrace.c | 88 |
1 files changed, 72 insertions, 16 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 2407a6d81cb7..c3a7cb4bf6e6 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,20 +165,8 @@ 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 */ |
156 | } | 169 | return ftrace_modify_code_direct(rec->ip, old, new); |
157 | |||
158 | int ftrace_update_ftrace_func(ftrace_func_t func) | ||
159 | { | ||
160 | unsigned long ip = (unsigned long)(&ftrace_call); | ||
161 | unsigned char old[MCOUNT_INSN_SIZE], *new; | ||
162 | int ret; | ||
163 | |||
164 | memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); | ||
165 | new = ftrace_call_replace(ip, (unsigned long)func); | ||
166 | ret = ftrace_modify_code(ip, old, new); | ||
167 | |||
168 | return ret; | ||
169 | } | 170 | } |
170 | 171 | ||
171 | /* | 172 | /* |
@@ -201,6 +202,29 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
201 | */ | 202 | */ |
202 | atomic_t modifying_ftrace_code __read_mostly; | 203 | atomic_t modifying_ftrace_code __read_mostly; |
203 | 204 | ||
205 | static int | ||
206 | ftrace_modify_code(unsigned long ip, unsigned const char *old_code, | ||
207 | unsigned const char *new_code); | ||
208 | |||
209 | int ftrace_update_ftrace_func(ftrace_func_t func) | ||
210 | { | ||
211 | unsigned long ip = (unsigned long)(&ftrace_call); | ||
212 | unsigned char old[MCOUNT_INSN_SIZE], *new; | ||
213 | int ret; | ||
214 | |||
215 | memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); | ||
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 | |||
221 | ret = ftrace_modify_code(ip, old, new); | ||
222 | |||
223 | atomic_dec(&modifying_ftrace_code); | ||
224 | |||
225 | return ret; | ||
226 | } | ||
227 | |||
204 | /* | 228 | /* |
205 | * 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 |
206 | * 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. |
@@ -520,6 +544,38 @@ void ftrace_replace_code(int enable) | |||
520 | } | 544 | } |
521 | } | 545 | } |
522 | 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 | |||
523 | void arch_ftrace_update_code(int command) | 579 | void arch_ftrace_update_code(int command) |
524 | { | 580 | { |
525 | /* See comment above by declaration of modifying_ftrace_code */ | 581 | /* See comment above by declaration of modifying_ftrace_code */ |