Skip to content
Permalink
Browse files
MIPS: add DYNAMIC_FTRACE_WITH_REGS and KPROBES_ON_FTACE
Add DYNAMIC_FTRACE_WITH_REGS and KPROBES_ON_FTACE suppport with
another ftrace implementation in parallel with mcount-based ftrace

+. Depend on GCC with -fpatchable-function-entry.

+. Depend on DYNAMIC_FTRACE.

+. Use 3 nops for stub in module and vmlinux, smaller than old one.

+. Simplify ftrace_regs_caller/ftrace_caller handling, especially
on MIPS O32.

+. No adjustment on sp, so callee(the traced function) can get caller's
sp, very friendly to livepatch.

Signed-off-by: Huang Pei <huangpei@loongson.cn>
  • Loading branch information
mips-hp authored and intel-lab-lkp committed Mar 13, 2021
1 parent d882aca commit c81a1cbcfd0d4b65f668fd824466b9bce02cee74
Show file tree
Hide file tree
Showing 6 changed files with 488 additions and 5 deletions.
@@ -57,6 +57,10 @@ config MIPS
select HAVE_DEBUG_STACKOVERFLOW
select HAVE_DMA_CONTIGUOUS
select HAVE_DYNAMIC_FTRACE
select HAVE_DYNAMIC_FTRACE_WITH_REGS \
if $(cc-option, -fpatchable-function-entry=3)
select HAVE_KPROBES_ON_FTRACE \
if $(cc-option, -fpatchable-function-entry=3)
select HAVE_EBPF_JIT if 64BIT && !CPU_MICROMIPS && TARGET_ISA_REV >= 2
select HAVE_EXIT_THREAD
select HAVE_FAST_GUP
@@ -56,13 +56,16 @@ ifneq ($(SUBARCH),$(ARCH))
endif
endif

ifdef CONFIG_FUNCTION_GRAPH_TRACER
ifndef KBUILD_MCOUNT_RA_ADDRESS
ifeq ($(call cc-option-yn,-mmcount-ra-address), y)
cflags-y += -mmcount-ra-address -DKBUILD_MCOUNT_RA_ADDRESS
ifndef CONFIG_DYNMAIC_FTRACE_WITH_REGS
ifdef CONFIG_FUNCTION_GRAPH_TRACER
ifndef KBUILD_MCOUNT_RA_ADDRESS
ifeq ($(call cc-option-yn,-mmcount-ra-address), y)
cflags-y += -mmcount-ra-address -DKBUILD_MCOUNT_RA_ADDRESS
endif
endif
endif
endif

cflags-y += $(call cc-option, -mno-check-zero-division)

ifdef CONFIG_32BIT
@@ -293,6 +296,11 @@ ifdef CONFIG_64BIT
bootvars-y += ADDR_BITS=64
endif

ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
CC_FLAGS_FTRACE := -fpatchable-function-entry=3
endif

# This is required to get dwarf unwinding tables into .debug_frame
# instead of .eh_frame so we don't discard them.
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
@@ -14,6 +14,7 @@

#define MCOUNT_ADDR ((unsigned long)(_mcount))
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
#define FTRACE_IP_EXTENSION (2 * MCOUNT_INSN_SIZE)

#ifndef __ASSEMBLY__
extern void _mcount(void);
@@ -87,4 +88,12 @@ struct dyn_arch_ftrace {
#endif /* CONFIG_DYNAMIC_FTRACE */
#endif /* __ASSEMBLY__ */
#endif /* CONFIG_FUNCTION_TRACER */

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
#define ARCH_SUPPORTS_FTRACE_OPS 1
struct dyn_ftrace;
int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
#define ftrace_init_nop ftrace_init_nop
#endif

#endif /* _ASM_MIPS_FTRACE_H */
@@ -21,6 +21,7 @@ CFLAGS_REMOVE_ftrace-mcount.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_early_printk.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_perf_event.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_perf_event_mipsxx.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
endif

obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o
@@ -39,7 +40,9 @@ obj-$(CONFIG_DEBUG_FS) += segment.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += module.o

obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace-mcount.o
ft-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace-mcount.o
ft-$(CONFIG_DYNAMIC_FTRACE_WITH_REGS) := entry-ftrace.o ftrace.o
obj-y += $(ft-y)

sw-y := r4k_switch.o
sw-$(CONFIG_CPU_R3000) := r2300_switch.o
@@ -0,0 +1,188 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* arch/mips/kernel/entry_ftrace.S
*
* Copyright (C) 2021 Loongson Corp
* Author: Huang Pei <huangpei@loongson.cn>
*/

#include <asm/export.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>

/*
* ftrace_regs_caller() is the function that replaces _mcount() when ftrace
* is active.
*
* we arrive here after a function A calls function B, and B is what we
* are tracing for. When we enter, sp points to A's stack frame, B has not
* yet had a chance to allocate one yet. (This is different from -pg case
* , in which the B's stack is allocated))

* when ftrace initialized, it replace three nops from all function with
* "lui + nop + move"
* B:
* lui at, %hi(ftrace_regs_caller)
* nop
* li t0, 0
* # B's real start
*
* at B's entry, when tracing enabled, replace the 'nop' with 'jalr'
*
* # B's entry, three nop for both in vmlinux and in kernel modules
* B:
* lui at, %hi(ftrace_regs_caller)
* jalr at, at
* move t0, zero
* # B's real start
*
* if set t0 to 1, then calling ftrace_regs_caller with partial regs saved
*
* B:
* lui at, %hi(ftrace_regs_caller)
* jalr at, at
* li t0, 1
* # B's real start
*
* we make ftrace_regs_caller 64KB aligned, when entring ftrace_regs_caller
* AT points to the return address to B, and ra points to return address
* to A,
*
* if patched to new funcition, then clobbered the first real instruction
*
* B:
* lui at, %hi(new_B)
* addiu at, at, %lo(new_B)
* jr at
* # B's real start, now clobbered with zero
* nop
*
*/
.text
.set push
.set noreorder
.set noat
.align 16
NESTED(ftrace_regs_caller, PT_SIZE, ra)
PTR_ADDIU sp, sp, -PT_SIZE
.globl ftrace_caller
ftrace_caller:
#ifdef CONFIG_64BIT
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 a0, PT_R4(sp)
PTR_S a1, PT_R5(sp)
PTR_S a2, PT_R6(sp)

bnez t0, 1f
PTR_S a3, PT_R7(sp)

PTR_S t0, PT_R12(sp)
PTR_S t1, PT_R13(sp)
PTR_S t2, PT_R14(sp)
PTR_S t3, PT_R15(sp)

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 t8, PT_R24(sp)
PTR_S t9, PT_R25(sp)
PTR_S s8, PT_R30(sp)
PTR_S gp, PT_R28(sp)

PTR_S AT, PT_R1(sp)
1:
PTR_LA t0, PT_SIZE(sp)
PTR_S AT, PT_R0(sp) //R0 for expected epc
PTR_S t0, PT_R29(sp)

PTR_S ra, PT_R31(sp)
PTR_S AT, PT_EPC(sp) //PT_EPC maybe changed by kprobe handler

END(ftrace_regs_caller)

ftrace_common:
PTR_ADDIU a0, AT, -12 //a0 points to B's entry address
move a1, ra //a1 points to return address to A
PTR_L a2, function_trace_op //a2 points to function_trace op

.globl ftrace_call
ftrace_call:
jal ftrace_stub
move a3, sp //a3 point to pt_regs

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
.globl ftrace_graph_call
ftrace_graph_call:
nop
nop
#endif

ftrace_common_return:
PTR_L AT, PT_R31(sp)
ftrace_graph_return:
PTR_L ra, PT_EPC(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)
#ifdef CONFIG_64BIT
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_ADDIU sp, sp, PT_SIZE //retore stack frame
jr ra
move ra, AT


.globl ftrace_stub
ftrace_stub:
jr ra
nop

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
.globl ftrace_graph_caller
ftrace_graph_caller:
PTR_L a0, PT_R31(sp)
PTR_L a1, PT_EPC(sp)
jal prepare_ftrace_return
PTR_ADDIU a2, sp, PT_SIZE

b ftrace_graph_return
move AT, v0


.align 2
.globl return_to_handler
return_to_handler:
PTR_SUBU sp, PT_SIZE
PTR_S v0, PT_R2(sp)

PTR_S v1, PT_R3(sp)
jal ftrace_return_to_handler
PTR_LA a0, PT_SIZE(sp)

/* restore the real parent address: v0 -> ra */
move ra, v0

PTR_L v0, PT_R2(sp)
PTR_L v1, PT_R3(sp)
jr ra
PTR_ADDIU sp, PT_SIZE

.set at
.set reorder
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */

0 comments on commit c81a1cb

Please sign in to comment.