diff options
| -rw-r--r-- | arch/x86/kernel/ftrace.c | 83 | ||||
| -rw-r--r-- | kernel/trace/ring_buffer.c | 7 |
2 files changed, 54 insertions, 36 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index d4bdd253fea7..e6253195a301 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
| @@ -77,8 +77,7 @@ within(unsigned long addr, unsigned long start, unsigned long end) | |||
| 77 | return addr >= start && addr < end; | 77 | return addr >= start && addr < end; |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | static int | 80 | static unsigned long text_ip_addr(unsigned long ip) |
| 81 | do_ftrace_mod_code(unsigned long ip, const void *new_code) | ||
| 82 | { | 81 | { |
| 83 | /* | 82 | /* |
| 84 | * On x86_64, kernel text mappings are mapped read-only with | 83 | * On x86_64, kernel text mappings are mapped read-only with |
| @@ -91,7 +90,7 @@ do_ftrace_mod_code(unsigned long ip, const void *new_code) | |||
| 91 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) | 90 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) |
| 92 | ip = (unsigned long)__va(__pa_symbol(ip)); | 91 | ip = (unsigned long)__va(__pa_symbol(ip)); |
| 93 | 92 | ||
| 94 | return probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE); | 93 | return ip; |
| 95 | } | 94 | } |
| 96 | 95 | ||
| 97 | static const unsigned char *ftrace_nop_replace(void) | 96 | static const unsigned char *ftrace_nop_replace(void) |
| @@ -123,8 +122,10 @@ ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code, | |||
| 123 | if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) | 122 | if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0) |
| 124 | return -EINVAL; | 123 | return -EINVAL; |
| 125 | 124 | ||
| 125 | ip = text_ip_addr(ip); | ||
| 126 | |||
| 126 | /* replace the text with the new text */ | 127 | /* replace the text with the new text */ |
| 127 | if (do_ftrace_mod_code(ip, new_code)) | 128 | if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE)) |
| 128 | return -EPERM; | 129 | return -EPERM; |
| 129 | 130 | ||
| 130 | sync_core(); | 131 | sync_core(); |
| @@ -221,37 +222,51 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, | |||
| 221 | return -EINVAL; | 222 | return -EINVAL; |
| 222 | } | 223 | } |
| 223 | 224 | ||
| 224 | int ftrace_update_ftrace_func(ftrace_func_t func) | 225 | static unsigned long ftrace_update_func; |
| 226 | |||
| 227 | static int update_ftrace_func(unsigned long ip, void *new) | ||
| 225 | { | 228 | { |
| 226 | unsigned long ip = (unsigned long)(&ftrace_call); | 229 | unsigned char old[MCOUNT_INSN_SIZE]; |
| 227 | unsigned char old[MCOUNT_INSN_SIZE], *new; | ||
| 228 | int ret; | 230 | int ret; |
| 229 | 231 | ||
| 230 | memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); | 232 | memcpy(old, (void *)ip, MCOUNT_INSN_SIZE); |
| 231 | new = ftrace_call_replace(ip, (unsigned long)func); | 233 | |
| 234 | ftrace_update_func = ip; | ||
| 235 | /* Make sure the breakpoints see the ftrace_update_func update */ | ||
| 236 | smp_wmb(); | ||
| 232 | 237 | ||
| 233 | /* See comment above by declaration of modifying_ftrace_code */ | 238 | /* See comment above by declaration of modifying_ftrace_code */ |
| 234 | atomic_inc(&modifying_ftrace_code); | 239 | atomic_inc(&modifying_ftrace_code); |
| 235 | 240 | ||
| 236 | ret = ftrace_modify_code(ip, old, new); | 241 | ret = ftrace_modify_code(ip, old, new); |
| 237 | 242 | ||
| 243 | atomic_dec(&modifying_ftrace_code); | ||
| 244 | |||
| 245 | return ret; | ||
| 246 | } | ||
| 247 | |||
| 248 | int ftrace_update_ftrace_func(ftrace_func_t func) | ||
| 249 | { | ||
| 250 | unsigned long ip = (unsigned long)(&ftrace_call); | ||
| 251 | unsigned char *new; | ||
| 252 | int ret; | ||
| 253 | |||
| 254 | new = ftrace_call_replace(ip, (unsigned long)func); | ||
| 255 | ret = update_ftrace_func(ip, new); | ||
| 256 | |||
| 238 | /* Also update the regs callback function */ | 257 | /* Also update the regs callback function */ |
| 239 | if (!ret) { | 258 | if (!ret) { |
| 240 | ip = (unsigned long)(&ftrace_regs_call); | 259 | ip = (unsigned long)(&ftrace_regs_call); |
| 241 | memcpy(old, &ftrace_regs_call, MCOUNT_INSN_SIZE); | ||
| 242 | new = ftrace_call_replace(ip, (unsigned long)func); | 260 | new = ftrace_call_replace(ip, (unsigned long)func); |
| 243 | ret = ftrace_modify_code(ip, old, new); | 261 | ret = update_ftrace_func(ip, new); |
| 244 | } | 262 | } |
| 245 | 263 | ||
| 246 | atomic_dec(&modifying_ftrace_code); | ||
| 247 | |||
| 248 | return ret; | 264 | return ret; |
| 249 | } | 265 | } |
| 250 | 266 | ||
| 251 | static int is_ftrace_caller(unsigned long ip) | 267 | static int is_ftrace_caller(unsigned long ip) |
| 252 | { | 268 | { |
| 253 | if (ip == (unsigned long)(&ftrace_call) || | 269 | if (ip == ftrace_update_func) |
| 254 | ip == (unsigned long)(&ftrace_regs_call)) | ||
| 255 | return 1; | 270 | return 1; |
| 256 | 271 | ||
| 257 | return 0; | 272 | return 0; |
| @@ -677,45 +692,41 @@ int __init ftrace_dyn_arch_init(void *data) | |||
| 677 | #ifdef CONFIG_DYNAMIC_FTRACE | 692 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 678 | extern void ftrace_graph_call(void); | 693 | extern void ftrace_graph_call(void); |
| 679 | 694 | ||
| 680 | static int ftrace_mod_jmp(unsigned long ip, | 695 | static unsigned char *ftrace_jmp_replace(unsigned long ip, unsigned long addr) |
| 681 | int old_offset, int new_offset) | ||
| 682 | { | 696 | { |
| 683 | unsigned char code[MCOUNT_INSN_SIZE]; | 697 | static union ftrace_code_union calc; |
| 684 | 698 | ||
| 685 | if (probe_kernel_read(code, (void *)ip, MCOUNT_INSN_SIZE)) | 699 | /* Jmp not a call (ignore the .e8) */ |
| 686 | return -EFAULT; | 700 | calc.e8 = 0xe9; |
| 701 | calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr); | ||
| 687 | 702 | ||
| 688 | if (code[0] != 0xe9 || old_offset != *(int *)(&code[1])) | 703 | /* |
| 689 | return -EINVAL; | 704 | * ftrace external locks synchronize the access to the static variable. |
| 705 | */ | ||
| 706 | return calc.code; | ||
| 707 | } | ||
| 690 | 708 | ||
| 691 | *(int *)(&code[1]) = new_offset; | 709 | static int ftrace_mod_jmp(unsigned long ip, void *func) |
| 710 | { | ||
| 711 | unsigned char *new; | ||
| 692 | 712 | ||
| 693 | if (do_ftrace_mod_code(ip, &code)) | 713 | new = ftrace_jmp_replace(ip, (unsigned long)func); |
| 694 | return -EPERM; | ||
| 695 | 714 | ||
| 696 | return 0; | 715 | return update_ftrace_func(ip, new); |
| 697 | } | 716 | } |
| 698 | 717 | ||
| 699 | int ftrace_enable_ftrace_graph_caller(void) | 718 | int ftrace_enable_ftrace_graph_caller(void) |
| 700 | { | 719 | { |
| 701 | unsigned long ip = (unsigned long)(&ftrace_graph_call); | 720 | unsigned long ip = (unsigned long)(&ftrace_graph_call); |
| 702 | int old_offset, new_offset; | ||
| 703 | 721 | ||
| 704 | old_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE); | 722 | return ftrace_mod_jmp(ip, &ftrace_graph_caller); |
| 705 | new_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE); | ||
| 706 | |||
| 707 | return ftrace_mod_jmp(ip, old_offset, new_offset); | ||
| 708 | } | 723 | } |
| 709 | 724 | ||
| 710 | int ftrace_disable_ftrace_graph_caller(void) | 725 | int ftrace_disable_ftrace_graph_caller(void) |
| 711 | { | 726 | { |
| 712 | unsigned long ip = (unsigned long)(&ftrace_graph_call); | 727 | unsigned long ip = (unsigned long)(&ftrace_graph_call); |
| 713 | int old_offset, new_offset; | ||
| 714 | |||
| 715 | old_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE); | ||
| 716 | new_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE); | ||
| 717 | 728 | ||
| 718 | return ftrace_mod_jmp(ip, old_offset, new_offset); | 729 | return ftrace_mod_jmp(ip, &ftrace_stub); |
| 719 | } | 730 | } |
| 720 | 731 | ||
| 721 | #endif /* !CONFIG_DYNAMIC_FTRACE */ | 732 | #endif /* !CONFIG_DYNAMIC_FTRACE */ |
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 294b8a271a04..fc4da2d97f9b 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c | |||
| @@ -2397,6 +2397,13 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, | |||
| 2397 | write &= RB_WRITE_MASK; | 2397 | write &= RB_WRITE_MASK; |
| 2398 | tail = write - length; | 2398 | tail = write - length; |
| 2399 | 2399 | ||
| 2400 | /* | ||
| 2401 | * If this is the first commit on the page, then it has the same | ||
| 2402 | * timestamp as the page itself. | ||
| 2403 | */ | ||
| 2404 | if (!tail) | ||
| 2405 | delta = 0; | ||
| 2406 | |||
| 2400 | /* See if we shot pass the end of this buffer page */ | 2407 | /* See if we shot pass the end of this buffer page */ |
| 2401 | if (unlikely(write > BUF_PAGE_SIZE)) | 2408 | if (unlikely(write > BUF_PAGE_SIZE)) |
| 2402 | return rb_move_tail(cpu_buffer, length, tail, | 2409 | return rb_move_tail(cpu_buffer, length, tail, |
