Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/ftrace: Make function graph use ftrace directly

We don't need special hook for graph tracer entry point,
but instead we can use graph_ops::func function to install
the return_hooker.

This moves the graph tracing setup _before_ the direct
trampoline prepares the stack, so the return_hooker will
be called when the direct trampoline is finished.

This simplifies the code, because we don't need to take into
account the direct trampoline setup when preparing the graph
tracer hooker and we can allow function graph tracer on entries
registered with direct trampoline.

Link: https://lkml.kernel.org/r/20211008091336.33616-4-jolsa@kernel.org

[fixed compile error reported by kernel test robot <lkp@intel.com>]
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>

+55 -35
+7 -2
arch/x86/include/asm/ftrace.h
··· 57 57 58 58 #define ftrace_instruction_pointer_set(fregs, _ip) \ 59 59 do { (fregs)->regs.ip = (_ip); } while (0) 60 + 61 + struct ftrace_ops; 62 + #define ftrace_graph_func ftrace_graph_func 63 + void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, 64 + struct ftrace_ops *op, struct ftrace_regs *fregs); 65 + #else 66 + #define FTRACE_GRAPH_TRAMP_ADDR FTRACE_GRAPH_ADDR 60 67 #endif 61 68 62 69 #ifdef CONFIG_DYNAMIC_FTRACE ··· 71 64 struct dyn_arch_ftrace { 72 65 /* No extra data needed for x86 */ 73 66 }; 74 - 75 - #define FTRACE_GRAPH_TRAMP_ADDR FTRACE_GRAPH_ADDR 76 67 77 68 #endif /* CONFIG_DYNAMIC_FTRACE */ 78 69 #endif /* __ASSEMBLY__ */
+34 -3
arch/x86/kernel/ftrace.c
··· 522 522 return ptr + CALL_INSN_SIZE + call.disp; 523 523 } 524 524 525 - void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, 525 + void prepare_ftrace_return(unsigned long ip, unsigned long *parent, 526 526 unsigned long frame_pointer); 527 527 528 528 /* ··· 536 536 void *ptr; 537 537 538 538 if (ops && ops->trampoline) { 539 - #ifdef CONFIG_FUNCTION_GRAPH_TRACER 539 + #if !defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS) && \ 540 + defined(CONFIG_FUNCTION_GRAPH_TRACER) 540 541 /* 541 542 * We only know about function graph tracer setting as static 542 543 * trampoline. ··· 585 584 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 586 585 587 586 #ifdef CONFIG_DYNAMIC_FTRACE 588 - extern void ftrace_graph_call(void); 589 587 588 + #ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS 589 + extern void ftrace_graph_call(void); 590 590 static const char *ftrace_jmp_replace(unsigned long ip, unsigned long addr) 591 591 { 592 592 return text_gen_insn(JMP32_INSN_OPCODE, (void *)ip, (void *)addr); ··· 615 613 616 614 return ftrace_mod_jmp(ip, &ftrace_stub); 617 615 } 616 + #else /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ 617 + int ftrace_enable_ftrace_graph_caller(void) 618 + { 619 + return 0; 620 + } 618 621 622 + int ftrace_disable_ftrace_graph_caller(void) 623 + { 624 + return 0; 625 + } 626 + #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ 619 627 #endif /* !CONFIG_DYNAMIC_FTRACE */ 620 628 621 629 /* ··· 636 624 unsigned long frame_pointer) 637 625 { 638 626 unsigned long return_hooker = (unsigned long)&return_to_handler; 627 + int bit; 639 628 640 629 /* 641 630 * When resuming from suspend-to-ram, this function can be indirectly ··· 656 643 if (unlikely(atomic_read(&current->tracing_graph_pause))) 657 644 return; 658 645 646 + bit = ftrace_test_recursion_trylock(ip, *parent); 647 + if (bit < 0) 648 + return; 649 + 659 650 if (!function_graph_enter(*parent, ip, frame_pointer, parent)) 660 651 *parent = return_hooker; 652 + 653 + ftrace_test_recursion_unlock(bit); 661 654 } 655 + 656 + #ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS 657 + void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, 658 + struct ftrace_ops *op, struct ftrace_regs *fregs) 659 + { 660 + struct pt_regs *regs = &fregs->regs; 661 + unsigned long *stack = (unsigned long *)kernel_stack_pointer(regs); 662 + 663 + prepare_ftrace_return(ip, (unsigned long *)stack, 0); 664 + } 665 + #endif 666 + 662 667 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+1 -28
arch/x86/kernel/ftrace_64.S
··· 174 174 SYM_FUNC_END(ftrace_caller); 175 175 176 176 SYM_FUNC_START(ftrace_epilogue) 177 - #ifdef CONFIG_FUNCTION_GRAPH_TRACER 178 - SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL) 179 - jmp ftrace_stub 180 - #endif 181 - 182 177 /* 183 178 * This is weak to keep gas from relaxing the jumps. 184 179 * It is also used to copy the retq for trampolines. ··· 283 288 cmpq $ftrace_stub, ftrace_trace_function 284 289 jnz trace 285 290 286 - fgraph_trace: 287 - #ifdef CONFIG_FUNCTION_GRAPH_TRACER 288 - cmpq $ftrace_stub, ftrace_graph_return 289 - jnz ftrace_graph_caller 290 - 291 - cmpq $ftrace_graph_entry_stub, ftrace_graph_entry 292 - jnz ftrace_graph_caller 293 - #endif 294 - 295 291 SYM_INNER_LABEL(ftrace_stub, SYM_L_GLOBAL) 296 292 retq 297 293 ··· 300 314 CALL_NOSPEC r8 301 315 restore_mcount_regs 302 316 303 - jmp fgraph_trace 317 + jmp ftrace_stub 304 318 SYM_FUNC_END(__fentry__) 305 319 EXPORT_SYMBOL(__fentry__) 306 320 #endif /* CONFIG_DYNAMIC_FTRACE */ 307 321 308 322 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 309 - SYM_FUNC_START(ftrace_graph_caller) 310 - /* Saves rbp into %rdx and fills first parameter */ 311 - save_mcount_regs 312 - 313 - leaq MCOUNT_REG_SIZE+8(%rsp), %rsi 314 - movq $0, %rdx /* No framepointers needed */ 315 - call prepare_ftrace_return 316 - 317 - restore_mcount_regs 318 - 319 - retq 320 - SYM_FUNC_END(ftrace_graph_caller) 321 - 322 323 SYM_FUNC_START(return_to_handler) 323 324 subq $24, %rsp 324 325
+9
include/linux/ftrace.h
··· 803 803 } 804 804 #endif /* CONFIG_DYNAMIC_FTRACE */ 805 805 806 + #ifdef CONFIG_FUNCTION_GRAPH_TRACER 807 + #ifndef ftrace_graph_func 808 + #define ftrace_graph_func ftrace_stub 809 + #define FTRACE_OPS_GRAPH_STUB FTRACE_OPS_FL_STUB 810 + #else 811 + #define FTRACE_OPS_GRAPH_STUB 0 812 + #endif 813 + #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 814 + 806 815 /* totally disable ftrace - can not re-enable after this */ 807 816 void ftrace_kill(void); 808 817
+4 -2
kernel/trace/fgraph.c
··· 115 115 { 116 116 struct ftrace_graph_ent trace; 117 117 118 + #ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS 118 119 /* 119 120 * Skip graph tracing if the return location is served by direct trampoline, 120 121 * since call sequence and return addresses are unpredictable anyway. ··· 125 124 if (ftrace_direct_func_count && 126 125 ftrace_find_rec_direct(ret - MCOUNT_INSN_SIZE)) 127 126 return -EBUSY; 127 + #endif 128 128 trace.func = func; 129 129 trace.depth = ++current->curr_ret_depth; 130 130 ··· 335 333 #endif /* HAVE_FUNCTION_GRAPH_RET_ADDR_PTR */ 336 334 337 335 static struct ftrace_ops graph_ops = { 338 - .func = ftrace_stub, 336 + .func = ftrace_graph_func, 339 337 .flags = FTRACE_OPS_FL_INITIALIZED | 340 338 FTRACE_OPS_FL_PID | 341 - FTRACE_OPS_FL_STUB, 339 + FTRACE_OPS_GRAPH_STUB, 342 340 #ifdef FTRACE_GRAPH_TRAMP_ADDR 343 341 .trampoline = FTRACE_GRAPH_TRAMP_ADDR, 344 342 /* trampoline_size is only needed for dynamically allocated tramps */