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

powerpc/ftrace: Add support for -fpatchable-function-entry

GCC v13.1 updated support for -fpatchable-function-entry on ppc64le to
emit nops after the local entry point, rather than before it. This
allows us to use this in the kernel for ftrace purposes. A new script is
added under arch/powerpc/tools/ to help detect if nops are emitted after
the function local entry point, or before the global entry point.

With -fpatchable-function-entry, we no longer have the profiling
instructions generated at function entry, so we only need to validate
the presence of two nops at the ftrace location in ftrace_init_nop(). We
patch the preceding instruction with 'mflr r0' to match the
-mprofile-kernel ABI for subsequent ftrace use.

This changes the profiling instructions used on ppc32. The default -pg
option emits an additional 'stw' instruction after 'mflr r0' and before
the branch to _mcount 'bl _mcount'. This is very similar to the original
-mprofile-kernel implementation on ppc64le, where an additional 'std'
instruction was used to save LR to its save location in the caller's
stackframe. Subsequently, this additional store was removed in later
compiler versions for performance reasons. The same reasons apply for
ppc32 so we only patch in a 'mflr r0'.

Signed-off-by: Naveen N Rao <naveen@kernel.org>
Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/68586d22981a2c3bb45f27a2b621173d10a7d092.1687166935.git.naveen@kernel.org

authored by

Naveen N Rao and committed by
Michael Ellerman
0f71dcfb c91c5a82

+64 -9
+11 -3
arch/powerpc/Kconfig
··· 186 186 select DYNAMIC_FTRACE if FUNCTION_TRACER 187 187 select EDAC_ATOMIC_SCRUB 188 188 select EDAC_SUPPORT 189 + select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY if ARCH_USING_PATCHABLE_FUNCTION_ENTRY 189 190 select GENERIC_ATOMIC64 if PPC32 190 191 select GENERIC_CLOCKEVENTS_BROADCAST if SMP 191 192 select GENERIC_CMOS_UPDATE ··· 228 227 select HAVE_DEBUG_KMEMLEAK 229 228 select HAVE_DEBUG_STACKOVERFLOW 230 229 select HAVE_DYNAMIC_FTRACE 231 - select HAVE_DYNAMIC_FTRACE_WITH_ARGS if MPROFILE_KERNEL || PPC32 232 - select HAVE_DYNAMIC_FTRACE_WITH_REGS if MPROFILE_KERNEL || PPC32 230 + select HAVE_DYNAMIC_FTRACE_WITH_ARGS if ARCH_USING_PATCHABLE_FUNCTION_ENTRY || MPROFILE_KERNEL || PPC32 231 + select HAVE_DYNAMIC_FTRACE_WITH_REGS if ARCH_USING_PATCHABLE_FUNCTION_ENTRY || MPROFILE_KERNEL || PPC32 233 232 select HAVE_EBPF_JIT 234 233 select HAVE_EFFICIENT_UNALIGNED_ACCESS 235 234 select HAVE_FAST_GUP ··· 257 256 select HAVE_MOD_ARCH_SPECIFIC 258 257 select HAVE_NMI if PERF_EVENTS || (PPC64 && PPC_BOOK3S) 259 258 select HAVE_OPTPROBES 260 - select HAVE_OBJTOOL if PPC32 || MPROFILE_KERNEL 259 + select HAVE_OBJTOOL if ARCH_USING_PATCHABLE_FUNCTION_ENTRY || MPROFILE_KERNEL || PPC32 261 260 select HAVE_OBJTOOL_MCOUNT if HAVE_OBJTOOL 262 261 select HAVE_PERF_EVENTS 263 262 select HAVE_PERF_EVENTS_NMI if PPC64 ··· 554 553 depends on PPC64_ELF_ABI_V2 && FUNCTION_TRACER 555 554 def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-mprofile-kernel.sh $(CC) -mlittle-endian) if CPU_LITTLE_ENDIAN 556 555 def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-mprofile-kernel.sh $(CC) -mbig-endian) if CPU_BIG_ENDIAN 556 + 557 + config ARCH_USING_PATCHABLE_FUNCTION_ENTRY 558 + depends on FUNCTION_TRACER && (PPC32 || PPC64_ELF_ABI_V2) 559 + depends on $(cc-option,-fpatchable-function-entry=2) 560 + def_bool y if PPC32 561 + def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh $(CC) -mlittle-endian) if PPC64 && CPU_LITTLE_ENDIAN 562 + def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh $(CC) -mbig-endian) if PPC64 && CPU_BIG_ENDIAN 557 563 558 564 config HOTPLUG_CPU 559 565 bool "Support for enabling/disabling CPUs"
+5
arch/powerpc/Makefile
··· 143 143 CFLAGS-$(CONFIG_PPC32) += $(call cc-option,-mno-readonly-in-sdata) 144 144 145 145 ifdef CONFIG_FUNCTION_TRACER 146 + ifdef CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY 147 + KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY 148 + CC_FLAGS_FTRACE := -fpatchable-function-entry=2 149 + else 146 150 CC_FLAGS_FTRACE := -pg 147 151 ifdef CONFIG_MPROFILE_KERNEL 148 152 CC_FLAGS_FTRACE += -mprofile-kernel 153 + endif 149 154 endif 150 155 endif 151 156
+4 -2
arch/powerpc/include/asm/ftrace.h
··· 11 11 #define HAVE_FUNCTION_GRAPH_RET_ADDR_PTR 12 12 13 13 /* Ignore unused weak functions which will have larger offsets */ 14 - #ifdef CONFIG_MPROFILE_KERNEL 14 + #if defined(CONFIG_MPROFILE_KERNEL) || defined(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY) 15 15 #define FTRACE_MCOUNT_MAX_OFFSET 16 16 16 #elif defined(CONFIG_PPC32) 17 17 #define FTRACE_MCOUNT_MAX_OFFSET 8 ··· 22 22 23 23 static inline unsigned long ftrace_call_adjust(unsigned long addr) 24 24 { 25 - /* relocation of mcount call site is the same as the address */ 25 + if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) 26 + addr += MCOUNT_INSN_SIZE; 27 + 26 28 return addr; 27 29 } 28 30
+3 -1
arch/powerpc/include/asm/vermagic.h
··· 2 2 #ifndef _ASM_VERMAGIC_H 3 3 #define _ASM_VERMAGIC_H 4 4 5 - #ifdef CONFIG_MPROFILE_KERNEL 5 + #ifdef CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY 6 + #define MODULE_ARCH_VERMAGIC_FTRACE "patchable-function-entry " 7 + #elif defined(CONFIG_MPROFILE_KERNEL) 6 8 #define MODULE_ARCH_VERMAGIC_FTRACE "mprofile-kernel " 7 9 #else 8 10 #define MODULE_ARCH_VERMAGIC_FTRACE ""
+1 -1
arch/powerpc/kernel/module_64.c
··· 465 465 return 0; 466 466 } 467 467 468 - #ifdef CONFIG_MPROFILE_KERNEL 468 + #if defined(CONFIG_MPROFILE_KERNEL) || defined(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY) 469 469 470 470 static u32 stub_insns[] = { 471 471 #ifdef CONFIG_PPC_KERNEL_PCREL
+12 -2
arch/powerpc/kernel/trace/ftrace.c
··· 220 220 int ret = 0; 221 221 222 222 /* Verify instructions surrounding the ftrace location */ 223 - if (IS_ENABLED(CONFIG_PPC32)) { 223 + if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) { 224 + /* Expect nops */ 225 + ret = ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_NOP())); 226 + if (!ret) 227 + ret = ftrace_validate_inst(ip, ppc_inst(PPC_RAW_NOP())); 228 + } else if (IS_ENABLED(CONFIG_PPC32)) { 224 229 /* Expected sequence: 'mflr r0', 'stw r0,4(r1)', 'bl _mcount' */ 225 230 ret = ftrace_validate_inst(ip - 8, ppc_inst(PPC_RAW_MFLR(_R0))); 226 231 if (!ret) ··· 255 250 /* Nop-out the ftrace location */ 256 251 new = ppc_inst(PPC_RAW_NOP()); 257 252 addr = MCOUNT_ADDR; 258 - if (is_offset_in_branch_range(addr - ip)) { 253 + if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) { 254 + /* we instead patch-in the 'mflr r0' */ 255 + old = ppc_inst(PPC_RAW_NOP()); 256 + new = ppc_inst(PPC_RAW_MFLR(_R0)); 257 + ret = ftrace_modify_code(ip - 4, old, new); 258 + } else if (is_offset_in_branch_range(addr - ip)) { 259 259 /* Within range */ 260 260 old = ftrace_create_branch_inst(ip, addr, 1); 261 261 ret = ftrace_modify_code(ip, old, new);
+2
arch/powerpc/kernel/trace/ftrace_entry.S
··· 250 250 blr 251 251 #endif /* CONFIG_LIVEPATCH */ 252 252 253 + #ifndef CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY 253 254 _GLOBAL(mcount) 254 255 _GLOBAL(_mcount) 255 256 EXPORT_SYMBOL(_mcount) ··· 258 257 mtctr r12 259 258 mtlr r0 260 259 bctr 260 + #endif 261 261 262 262 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 263 263 _GLOBAL(return_to_handler)
+26
arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + set -e 5 + set -o pipefail 6 + 7 + # To debug, uncomment the following line 8 + # set -x 9 + 10 + # Output from -fpatchable-function-entry can only vary on ppc64 elfv2, so this 11 + # should not be invoked for other targets. Therefore we can pass in -m64 and 12 + # -mabi explicitly, to take care of toolchains defaulting to other targets. 13 + 14 + # Test whether the compile option -fpatchable-function-entry exists and 15 + # generates appropriate code 16 + echo "int func() { return 0; }" | \ 17 + $* -m64 -mabi=elfv2 -S -x c -O2 -fpatchable-function-entry=2 - -o - 2> /dev/null | \ 18 + grep -q "__patchable_function_entries" 19 + 20 + # Test whether nops are generated after the local entry point 21 + echo "int x; int func() { return x; }" | \ 22 + $* -m64 -mabi=elfv2 -S -x c -O2 -fpatchable-function-entry=2 - -o - 2> /dev/null | \ 23 + awk 'BEGIN { RS = ";" } /\.localentry.*nop.*\n[[:space:]]*nop/ { print $0 }' | \ 24 + grep -q "func:" 25 + 26 + exit 0