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 */ |
