In the past, we have always used the address of _mcount as the address of ftrace_caller. It reduces one ftrace_modify_code operation when do ftrace on modules on 64Bit platform in this way. In order to provide DYNAMIC_FTRACE_WITH_REGS, we have to take _mcount out of ftrace_caller and add a new definition of _mcount. It is necessary to modify 2 instructions. Also add the definition of ftrace_regs_caller. ftrace_regs_caller will store and restore more registers. Of course, some functions in ftrace.c also need to consider ftrace_regs_caller. Modify these functions and add the related code of ftrace_regs_caller. Signed-off-by: Jinyang He <hejinyang@xxxxxxxxxxx> --- arch/mips/Kconfig | 1 + arch/mips/include/asm/ftrace.h | 5 ++ arch/mips/kernel/ftrace.c | 159 ++++++++++++++++++++++++++++------------- arch/mips/kernel/mcount.S | 137 +++++++++++++++++++++++++++++------ 4 files changed, 229 insertions(+), 73 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 62475fc..00d36dd 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -58,6 +58,7 @@ config MIPS select HAVE_DEBUG_STACKOVERFLOW select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE + select HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_EBPF_JIT if 64BIT && !CPU_MICROMIPS && TARGET_ISA_REV >= 2 select HAVE_EXIT_THREAD select HAVE_FAST_GUP diff --git a/arch/mips/include/asm/ftrace.h b/arch/mips/include/asm/ftrace.h index 636c640..8afd1bc 100644 --- a/arch/mips/include/asm/ftrace.h +++ b/arch/mips/include/asm/ftrace.h @@ -76,6 +76,11 @@ do { \ #ifdef CONFIG_DYNAMIC_FTRACE + +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +#define ARCH_SUPPORTS_FTRACE_OPS 1 +#endif + static inline unsigned long ftrace_call_adjust(unsigned long addr) { return addr; diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c index fd6d1da..890429a 100644 --- a/arch/mips/kernel/ftrace.c +++ b/arch/mips/kernel/ftrace.c @@ -49,40 +49,89 @@ void arch_ftrace_update_code(int command) #define DONT_SET 0xffffffff static struct ftrace_insn jal_ftrace_caller __read_mostly; -static struct ftrace_insn la_mcount __read_mostly; +static struct ftrace_insn la_ftrace_caller __read_mostly; static struct ftrace_insn nop_kernel __read_mostly; static struct ftrace_insn nop_module __read_mostly; +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +static struct ftrace_insn jal_ftrace_regs_caller __read_mostly; +static struct ftrace_insn la_ftrace_regs_caller __read_mostly; +#endif + #ifdef CONFIG_FUNCTION_GRAPH_TRACER static struct ftrace_insn j_ftrace_graph_caller __read_mostly; #endif +/* + * The details about the calling site of mcount on MIPS + * + * 1. For kernel: + * + * move at, ra + * jal _mcount --> nop + * sub sp, sp, 8 --> nop (CONFIG_32BIT) + * + * 2. For modules: + * + * 2.1 For KBUILD_MCOUNT_RA_ADDRESS and CONFIG_32BIT + * + * lui v1, hi_16bit_of_mcount --> b 1f (0x10000005) + * addiu v1, v1, low_16bit_of_mcount --> nop (CONFIG_32BIT) + * move at, ra + * move $12, ra_address + * jalr v1 + * sub sp, sp, 8 + * 1: offset = 5 instructions + * 2.2 For the Other situations + * + * lui v1, hi_16bit_of_mcount --> b 1f (0x10000004) + * addiu v1, v1, low_16bit_of_mcount --> nop (CONFIG_32BIT) + * move at, ra + * jalr v1 + * nop | move $12, ra_address | sub sp, sp, 8 + * 1: offset = 4 instructions + */ + static inline void ftrace_dyn_arch_init_insns(void) { u32 *buf; unsigned int v1 = 3; - /* la v1, _mcount */ - buf = (u32 *)&la_mcount; - UASM_i_LA(&buf, v1, MCOUNT_ADDR); -#ifdef CONFIG_64BIT - la_mcount.code[1] = DONT_SET; -#endif + /* la v1, ftrace_caller */ + buf = (u32 *)&la_ftrace_caller; + UASM_i_LA(&buf, v1, FTRACE_ADDR); - /* jal (ftrace_caller + 8), jump over the first two instruction */ buf = (u32 *)&jal_ftrace_caller; - uasm_i_jal(&buf, (FTRACE_ADDR + 8) & JUMP_RANGE_MASK); +#ifdef CONFIG_32BIT + /* jal (ftrace_caller + 4), jump over the sp restore instruction */ + uasm_i_jal(&buf, (FTRACE_ADDR + 4) & JUMP_RANGE_MASK); +#else + uasm_i_jal(&buf, FTRACE_ADDR & JUMP_RANGE_MASK); +#endif jal_ftrace_caller.code[1] = DONT_SET; nop_kernel.code[0] = INSN_NOP; - nop_module.code[0] = INSN_B_1F; - #ifdef CONFIG_64BIT nop_kernel.code[1] = DONT_SET; - nop_module.code[1] = DONT_SET; #else nop_kernel.code[1] = INSN_NOP; +#endif + nop_module.code[0] = INSN_B_1F; nop_module.code[1] = INSN_NOP; + +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS + /* la v1, ftrace_regs_caller */ + buf = (u32 *)&la_ftrace_regs_caller; + UASM_i_LA(&buf, v1, FTRACE_REGS_ADDR); + + /* jal ftrace_regs_caller */ + buf = (u32 *)&jal_ftrace_regs_caller; +#ifdef CONFIG_32BIT + uasm_i_jal(&buf, (FTRACE_REGS_ADDR + 4) & JUMP_RANGE_MASK); +#else + uasm_i_jal(&buf, FTRACE_REGS_ADDR & JUMP_RANGE_MASK); +#endif + jal_ftrace_regs_caller.code[1] = DONT_SET; #endif #ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -119,36 +168,6 @@ static int ftrace_modify_code(unsigned long ip, struct ftrace_insn insns) return 0; } -/* - * The details about the calling site of mcount on MIPS - * - * 1. For kernel: - * - * move at, ra - * jal _mcount --> nop - * sub sp, sp, 8 --> nop (CONFIG_32BIT) - * - * 2. For modules: - * - * 2.1 For KBUILD_MCOUNT_RA_ADDRESS and CONFIG_32BIT - * - * lui v1, hi_16bit_of_mcount --> b 1f (0x10000005) - * addiu v1, v1, low_16bit_of_mcount --> nop (CONFIG_32BIT) - * move at, ra - * move $12, ra_address - * jalr v1 - * sub sp, sp, 8 - * 1: offset = 5 instructions - * 2.2 For the Other situations - * - * lui v1, hi_16bit_of_mcount --> b 1f (0x10000004) - * addiu v1, v1, low_16bit_of_mcount --> nop (CONFIG_32BIT) - * move at, ra - * jalr v1 - * nop | move $12, ra_address | sub sp, sp, 8 - * 1: offset = 4 instructions - */ - int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) { @@ -164,41 +183,79 @@ int ftrace_make_nop(struct module *mod, return ftrace_modify_code(ip, insns); } +static int __ftrace_make_call(unsigned long ip, unsigned long addr) +{ + u32 *buf; + struct ftrace_insn insns; + unsigned int v1 = 3; + + if (core_kernel_text(ip)) { + /* PC-region */ + if ((addr & ~JUMP_RANGE_MASK) != (ip & ~JUMP_RANGE_MASK)) + return -EINVAL; + + insns.code[0] = INSN_JAL(addr); + insns.code[1] = DONT_SET; + } else { + buf = (u32 *)&insns; + UASM_i_LA(&buf, v1, addr); + } + + return ftrace_modify_code(ip, insns); +} + int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) { struct ftrace_insn insns; unsigned long ip = rec->ip; - insns = core_kernel_text(ip) ? jal_ftrace_caller : la_mcount; + if (addr == FTRACE_ADDR) + insns = core_kernel_text(ip) ? jal_ftrace_caller : la_ftrace_caller; +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS + else if (addr == FTRACE_REGS_ADDR) + insns = core_kernel_text(ip) ? jal_ftrace_regs_caller : la_ftrace_regs_caller; +#endif + else + return __ftrace_make_call(ip, addr); return ftrace_modify_code(ip, insns); } +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, + unsigned long addr) +{ + unsigned long ip = rec->ip; + + return __ftrace_make_call(ip, addr); +} +#endif + #define FTRACE_CALL_IP ((unsigned long)(&ftrace_call)) int ftrace_update_ftrace_func(ftrace_func_t func) { + int faulted; struct ftrace_insn insns; insns.code[0] = INSN_JAL((unsigned long)func); insns.code[1] = DONT_SET; +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +#define FTRACE_REGS_CALL_IP ((unsigned long)(&ftrace_regs_call)) + faulted = ftrace_modify_code(FTRACE_REGS_CALL_IP, insns); + if (unlikely(faulted)) + return -EFAULT; +#endif + return ftrace_modify_code(FTRACE_CALL_IP, insns); } int __init ftrace_dyn_arch_init(void) { - struct ftrace_insn insns; - - insns.code[0] = INSN_NOP; - insns.code[1] = DONT_SET; - /* Encode the instructions when booting */ ftrace_dyn_arch_init_insns(); - /* Remove "b ftrace_stub" to ensure ftrace_caller() is executed */ - ftrace_modify_code(MCOUNT_ADDR, insns); - return 0; } #endif /* CONFIG_DYNAMIC_FTRACE */ diff --git a/arch/mips/kernel/mcount.S b/arch/mips/kernel/mcount.S index 808257a..2c9c061 100644 --- a/arch/mips/kernel/mcount.S +++ b/arch/mips/kernel/mcount.S @@ -51,11 +51,83 @@ PTR_ADDIU sp, PT_SIZE .endm + .macro MCOUNT_SAVE_MORE_REGS + PTR_SUBU sp, PT_SIZE + PTR_S ra, PT_R31(sp) + PTR_S AT, PT_R1(sp) + PTR_S a0, PT_R4(sp) + PTR_S a1, PT_R5(sp) + PTR_S a2, PT_R6(sp) + PTR_S a3, PT_R7(sp) +#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 + PTR_S a4, PT_R8(sp) + PTR_S a5, PT_R9(sp) + PTR_S a6, PT_R10(sp) + PTR_S a7, PT_R11(sp) +#endif + PTR_S s0, PT_R16(sp) + PTR_S s1, PT_R17(sp) + PTR_S s2, PT_R18(sp) + PTR_S s3, PT_R19(sp) + PTR_S s4, PT_R20(sp) + PTR_S s5, PT_R21(sp) + PTR_S s6, PT_R22(sp) + PTR_S s7, PT_R23(sp) + PTR_S gp, PT_R28(sp) + PTR_S sp, PT_R29(sp) + PTR_S s8, PT_R30(sp) + .endm + + .macro MCOUNT_RESTORE_MORE_REGS + PTR_L ra, PT_R31(sp) + PTR_L AT, PT_R1(sp) + PTR_L v0, PT_R2(sp) + PTR_L v1, PT_R3(sp) + PTR_L a0, PT_R4(sp) + PTR_L a1, PT_R5(sp) + PTR_L a2, PT_R6(sp) + PTR_L a3, PT_R7(sp) +#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 + PTR_L a4, PT_R8(sp) + PTR_L a5, PT_R9(sp) + PTR_L a6, PT_R10(sp) + PTR_L a7, PT_R11(sp) +#endif + PTR_L s0, PT_R16(sp) + PTR_L s1, PT_R17(sp) + PTR_L s2, PT_R18(sp) + PTR_L s3, PT_R19(sp) + PTR_L s4, PT_R20(sp) + PTR_L s5, PT_R21(sp) + PTR_L s6, PT_R22(sp) + PTR_L s7, PT_R23(sp) + PTR_L gp, PT_R28(sp) + PTR_L sp, PT_R29(sp) + PTR_L s8, PT_R30(sp) + PTR_ADDIU sp, PT_SIZE + .endm + .macro RETURN_BACK jr ra move ra, AT .endm + .macro is_in_module addr res temp1 temp2 + PTR_LA \res, _stext + sltu \temp1, \addr, \res /* temp1 = (addr < _stext) */ + PTR_LA \res, _etext + sltu \temp2, \res, \addr /* temp2 = (addr > _etext) */ + or \res, \temp1, \temp2 + .endm + + .macro adjust_module_callsite addr +#if defined(KBUILD_MCOUNT_RA_ADDRESS) && defined(CONFIG_32BIT) + PTR_SUBU \addr, \addr, 16 /* arg1: adjust to module's recorded callsite */ +#else + PTR_SUBU \addr, \addr, 12 +#endif + .endm + /* * The -mmcount-ra-address option of gcc 4.5 uses register $12 to pass * the location of the parent's return address. @@ -64,55 +136,76 @@ #ifdef CONFIG_DYNAMIC_FTRACE -NESTED(ftrace_caller, PT_SIZE, ra) - .globl _mcount -_mcount: +NESTED(_mcount, PT_SIZE, ra) EXPORT_SYMBOL(_mcount) - b ftrace_stub +#ifdef CONFIG_32BIT + addiu sp, sp, 8 +#endif + .globl ftrace_stub +ftrace_stub: + RETURN_BACK + END(_mcount) + +NESTED(ftrace_caller, PT_SIZE, ra) #ifdef CONFIG_32BIT addiu sp,sp,8 -#else - nop #endif - /* When tracing is activated, it calls ftrace_caller+8 (aka here) */ MCOUNT_SAVE_REGS #ifdef KBUILD_MCOUNT_RA_ADDRESS PTR_S MCOUNT_RA_ADDRESS_REG, PT_R12(sp) #endif PTR_SUBU a0, ra, 8 /* arg1: self address */ - PTR_LA t1, _stext - sltu t2, a0, t1 /* t2 = (a0 < _stext) */ - PTR_LA t1, _etext - sltu t3, t1, a0 /* t3 = (a0 > _etext) */ - or t1, t2, t3 + is_in_module a0, t1, t8, t9 beqz t1, ftrace_call - nop -#if defined(KBUILD_MCOUNT_RA_ADDRESS) && defined(CONFIG_32BIT) - PTR_SUBU a0, a0, 16 /* arg1: adjust to module's recorded callsite */ -#else - PTR_SUBU a0, a0, 12 -#endif + nop + adjust_module_callsite a0 .globl ftrace_call ftrace_call: nop /* a placeholder for the call to a real tracing function */ - move a1, AT /* arg2: parent's return address */ + move a1, AT /* arg2: parent's return address */ #ifdef CONFIG_FUNCTION_GRAPH_TRACER .globl ftrace_graph_call ftrace_graph_call: nop - nop + nop #endif MCOUNT_RESTORE_REGS - .globl ftrace_stub -ftrace_stub: RETURN_BACK END(ftrace_caller) +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +NESTED(ftrace_regs_caller, PT_SIZE, ra) +#ifdef CONFIG_32BIT + addiu sp, sp, 8 +#endif + MCOUNT_SAVE_MORE_REGS +#ifdef KBUILD_MCOUNT_RA_ADDRESS + PTR_S MCOUNT_RA_ADDRESS_REG, PT_R12(sp) +#endif + + move a2, zero /* arg3: NULL */ + move a3, sp /* arg4: fregs address */ + PTR_SUBU a0, ra, 8 /* arg1: self address */ + is_in_module a0, t1, t8, t9 + beqz t1, ftrace_regs_call + nop + adjust_module_callsite a0 + + .globl ftrace_regs_call +ftrace_regs_call: + nop + move a1, AT /* arg2: parent's return address */ + + MCOUNT_RESTORE_REGS + RETURN_BACK + END(ftrace_regs_caller) +#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */ + #else /* ! CONFIG_DYNAMIC_FTRACE */ NESTED(_mcount, PT_SIZE, ra) -- 2.1.0