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

powerpc64/ftrace: Move ftrace sequence out of line

Function profile sequence on powerpc includes two instructions at the
beginning of each function:
mflr r0
bl ftrace_caller

The call to ftrace_caller() gets nop'ed out during kernel boot and is
patched in when ftrace is enabled.

Given the sequence, we cannot return from ftrace_caller with 'blr' as we
need to keep LR and r0 intact. This results in link stack (return
address predictor) imbalance when ftrace is enabled. To address that, we
would like to use a three instruction sequence:
mflr r0
bl ftrace_caller
mtlr r0

Further more, to support DYNAMIC_FTRACE_WITH_CALL_OPS, we need to
reserve two instruction slots before the function. This results in a
total of five instruction slots to be reserved for ftrace use on each
function that is traced.

Move the function profile sequence out-of-line to minimize its impact.
To do this, we reserve a single nop at function entry using
-fpatchable-function-entry=1 and add a pass on vmlinux.o to determine
the total number of functions that can be traced. This is then used to
generate a .S file reserving the appropriate amount of space for use as
ftrace stubs, which is built and linked into vmlinux.

On bootup, the stub space is split into separate stubs per function and
populated with the proper instruction sequence. A pointer to the
associated stub is maintained in dyn_arch_ftrace.

For modules, space for ftrace stubs is reserved from the generic module
stub space.

This is restricted to and enabled by default only on 64-bit powerpc,
though there are some changes to accommodate 32-bit powerpc. This is
done so that 32-bit powerpc could choose to opt into this based on
further tests and benchmarks.

As an example, after this patch, kernel functions will have a single nop
at function entry:
<kernel_clone>:
addis r2,r12,467
addi r2,r2,-16028
nop
mfocrf r11,8
...

When ftrace is enabled, the nop is converted to an unconditional branch
to the stub associated with that function:
<kernel_clone>:
addis r2,r12,467
addi r2,r2,-16028
b ftrace_ool_stub_text_end+0x11b28
mfocrf r11,8
...

The associated stub:
<ftrace_ool_stub_text_end+0x11b28>:
mflr r0
bl ftrace_caller
mtlr r0
b kernel_clone+0xc
...

This change showed an improvement of ~10% in null_syscall benchmark on a
Power 10 system with ftrace enabled.

Signed-off-by: Naveen N Rao <naveen@kernel.org>
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://patch.msgid.link/20241030070850.1361304-13-hbathini@linux.ibm.com

authored by

Naveen N Rao and committed by
Michael Ellerman
eec37961 1198c9c6

+380 -38
+1 -1
arch/powerpc/Kbuild
··· 19 19 obj-$(CONFIG_KEXEC_FILE) += purgatory/ 20 20 21 21 # for cleaning 22 - subdir- += boot 22 + subdir- += boot tools
+4
arch/powerpc/Kconfig
··· 569 569 def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh $(CC) -mlittle-endian) if PPC64 && CPU_LITTLE_ENDIAN 570 570 def_bool $(success,$(srctree)/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh $(CC) -mbig-endian) if PPC64 && CPU_BIG_ENDIAN 571 571 572 + config PPC_FTRACE_OUT_OF_LINE 573 + def_bool PPC64 && ARCH_USING_PATCHABLE_FUNCTION_ENTRY 574 + select ARCH_WANTS_PRE_LINK_VMLINUX 575 + 572 576 config HOTPLUG_CPU 573 577 bool "Support for enabling/disabling CPUs" 574 578 depends on SMP && (PPC_PSERIES || \
+4
arch/powerpc/Makefile
··· 148 148 ifdef CONFIG_FUNCTION_TRACER 149 149 ifdef CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY 150 150 KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY 151 + ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 152 + CC_FLAGS_FTRACE := -fpatchable-function-entry=1 153 + else 151 154 CC_FLAGS_FTRACE := -fpatchable-function-entry=2 155 + endif 152 156 else 153 157 CC_FLAGS_FTRACE := -pg 154 158 ifdef CONFIG_MPROFILE_KERNEL
+11
arch/powerpc/include/asm/ftrace.h
··· 24 24 struct module; 25 25 struct dyn_ftrace; 26 26 struct dyn_arch_ftrace { 27 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 28 + /* pointer to the associated out-of-line stub */ 29 + unsigned long ool_stub; 30 + #endif 27 31 }; 28 32 29 33 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS ··· 134 130 135 131 #ifdef CONFIG_FUNCTION_TRACER 136 132 extern unsigned int ftrace_tramp_text[], ftrace_tramp_init[]; 133 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 134 + struct ftrace_ool_stub { 135 + u32 insn[4]; 136 + }; 137 + extern struct ftrace_ool_stub ftrace_ool_stub_text_end[], ftrace_ool_stub_inittext[]; 138 + extern unsigned int ftrace_ool_stub_text_end_count, ftrace_ool_stub_inittext_count; 139 + #endif 137 140 void ftrace_free_init_tramp(void); 138 141 unsigned long ftrace_call_adjust(unsigned long addr); 139 142 #else
+5
arch/powerpc/include/asm/module.h
··· 47 47 #ifdef CONFIG_DYNAMIC_FTRACE 48 48 unsigned long tramp; 49 49 unsigned long tramp_regs; 50 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 51 + struct ftrace_ool_stub *ool_stubs; 52 + unsigned int ool_stub_count; 53 + unsigned int ool_stub_index; 54 + #endif 50 55 #endif 51 56 }; 52 57
+4
arch/powerpc/kernel/asm-offsets.c
··· 675 675 DEFINE(BPT_SIZE, BPT_SIZE); 676 676 #endif 677 677 678 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 679 + DEFINE(FTRACE_OOL_STUB_SIZE, sizeof(struct ftrace_ool_stub)); 680 + #endif 681 + 678 682 return 0; 679 683 }
+56 -2
arch/powerpc/kernel/module_64.c
··· 205 205 206 206 /* Get size of potential trampolines required. */ 207 207 static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, 208 - const Elf64_Shdr *sechdrs) 208 + const Elf64_Shdr *sechdrs, 209 + char *secstrings, 210 + struct module *me) 209 211 { 210 212 /* One extra reloc so it's always 0-addr terminated */ 211 213 unsigned long relocs = 1; ··· 245 243 246 244 /* stubs for ftrace_caller and ftrace_regs_caller */ 247 245 relocs += IS_ENABLED(CONFIG_DYNAMIC_FTRACE) + IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS); 246 + 247 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 248 + /* stubs for the function tracer */ 249 + for (i = 1; i < hdr->e_shnum; i++) { 250 + if (!strcmp(secstrings + sechdrs[i].sh_name, "__patchable_function_entries")) { 251 + me->arch.ool_stub_count = sechdrs[i].sh_size / sizeof(unsigned long); 252 + me->arch.ool_stub_index = 0; 253 + relocs += roundup(me->arch.ool_stub_count * sizeof(struct ftrace_ool_stub), 254 + sizeof(struct ppc64_stub_entry)) / 255 + sizeof(struct ppc64_stub_entry); 256 + break; 257 + } 258 + } 259 + if (i == hdr->e_shnum) { 260 + pr_err("%s: doesn't contain __patchable_function_entries.\n", me->name); 261 + return -ENOEXEC; 262 + } 263 + #endif 248 264 249 265 pr_debug("Looks like a total of %lu stubs, max\n", relocs); 250 266 return relocs * sizeof(struct ppc64_stub_entry); ··· 474 454 #endif 475 455 476 456 /* Override the stubs size */ 477 - sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs); 457 + sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs, secstrings, me); 478 458 479 459 return 0; 480 460 } ··· 1099 1079 return 0; 1100 1080 } 1101 1081 1082 + static int setup_ftrace_ool_stubs(const Elf64_Shdr *sechdrs, unsigned long addr, struct module *me) 1083 + { 1084 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 1085 + unsigned int i, total_stubs, num_stubs; 1086 + struct ppc64_stub_entry *stub; 1087 + 1088 + total_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stub); 1089 + num_stubs = roundup(me->arch.ool_stub_count * sizeof(struct ftrace_ool_stub), 1090 + sizeof(struct ppc64_stub_entry)) / sizeof(struct ppc64_stub_entry); 1091 + 1092 + /* Find the next available entry */ 1093 + stub = (void *)sechdrs[me->arch.stubs_section].sh_addr; 1094 + for (i = 0; stub_func_addr(stub[i].funcdata); i++) 1095 + if (WARN_ON(i >= total_stubs)) 1096 + return -1; 1097 + 1098 + if (WARN_ON(i + num_stubs > total_stubs)) 1099 + return -1; 1100 + 1101 + stub += i; 1102 + me->arch.ool_stubs = (struct ftrace_ool_stub *)stub; 1103 + 1104 + /* reserve stubs */ 1105 + for (i = 0; i < num_stubs; i++) 1106 + if (patch_u32((void *)&stub->funcdata, PPC_RAW_NOP())) 1107 + return -1; 1108 + #endif 1109 + 1110 + return 0; 1111 + } 1112 + 1102 1113 int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sechdrs) 1103 1114 { 1104 1115 mod->arch.tramp = stub_for_addr(sechdrs, ··· 1146 1095 #endif 1147 1096 1148 1097 if (!mod->arch.tramp) 1098 + return -ENOENT; 1099 + 1100 + if (setup_ftrace_ool_stubs(sechdrs, mod->arch.tramp, mod)) 1149 1101 return -ENOENT; 1150 1102 1151 1103 return 0;
+152 -10
arch/powerpc/kernel/trace/ftrace.c
··· 37 37 if (addr >= (unsigned long)__exittext_begin && addr < (unsigned long)__exittext_end) 38 38 return 0; 39 39 40 - if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) 40 + if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY) && 41 + !IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) 41 42 addr += MCOUNT_INSN_SIZE; 42 43 43 44 return addr; ··· 128 127 } 129 128 #endif 130 129 130 + static unsigned long ftrace_get_ool_stub(struct dyn_ftrace *rec) 131 + { 132 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 133 + return rec->arch.ool_stub; 134 + #else 135 + BUILD_BUG(); 136 + #endif 137 + } 138 + 131 139 static int ftrace_get_call_inst(struct dyn_ftrace *rec, unsigned long addr, ppc_inst_t *call_inst) 132 140 { 133 - unsigned long ip = rec->ip; 141 + unsigned long ip; 134 142 unsigned long stub; 143 + 144 + if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) 145 + ip = ftrace_get_ool_stub(rec) + MCOUNT_INSN_SIZE; /* second instruction in stub */ 146 + else 147 + ip = rec->ip; 135 148 136 149 if (is_offset_in_branch_range(addr - ip)) 137 150 /* Within range */ ··· 157 142 stub = ftrace_lookup_module_stub(ip, addr); 158 143 159 144 if (!stub) { 160 - pr_err("0x%lx: No ftrace stubs reachable\n", ip); 145 + pr_err("0x%lx (0x%lx): No ftrace stubs reachable\n", ip, rec->ip); 161 146 return -EINVAL; 162 147 } 163 148 164 149 *call_inst = ftrace_create_branch_inst(ip, stub, 1); 165 150 return 0; 151 + } 152 + 153 + static int ftrace_init_ool_stub(struct module *mod, struct dyn_ftrace *rec) 154 + { 155 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 156 + static int ool_stub_text_end_index, ool_stub_inittext_index; 157 + int ret = 0, ool_stub_count, *ool_stub_index; 158 + ppc_inst_t inst; 159 + /* 160 + * See ftrace_entry.S if changing the below instruction sequence, as we rely on 161 + * decoding the last branch instruction here to recover the correct function ip. 162 + */ 163 + struct ftrace_ool_stub *ool_stub, ool_stub_template = { 164 + .insn = { 165 + PPC_RAW_MFLR(_R0), 166 + PPC_RAW_NOP(), /* bl ftrace_caller */ 167 + PPC_RAW_MTLR(_R0), 168 + PPC_RAW_NOP() /* b rec->ip + 4 */ 169 + } 170 + }; 171 + 172 + WARN_ON(rec->arch.ool_stub); 173 + 174 + if (is_kernel_inittext(rec->ip)) { 175 + ool_stub = ftrace_ool_stub_inittext; 176 + ool_stub_index = &ool_stub_inittext_index; 177 + ool_stub_count = ftrace_ool_stub_inittext_count; 178 + } else if (is_kernel_text(rec->ip)) { 179 + ool_stub = ftrace_ool_stub_text_end; 180 + ool_stub_index = &ool_stub_text_end_index; 181 + ool_stub_count = ftrace_ool_stub_text_end_count; 182 + #ifdef CONFIG_MODULES 183 + } else if (mod) { 184 + ool_stub = mod->arch.ool_stubs; 185 + ool_stub_index = &mod->arch.ool_stub_index; 186 + ool_stub_count = mod->arch.ool_stub_count; 187 + #endif 188 + } else { 189 + return -EINVAL; 190 + } 191 + 192 + ool_stub += (*ool_stub_index)++; 193 + 194 + if (WARN_ON(*ool_stub_index > ool_stub_count)) 195 + return -EINVAL; 196 + 197 + if (!is_offset_in_branch_range((long)rec->ip - (long)&ool_stub->insn[0]) || 198 + !is_offset_in_branch_range((long)(rec->ip + MCOUNT_INSN_SIZE) - 199 + (long)&ool_stub->insn[3])) { 200 + pr_err("%s: ftrace ool stub out of range (%p -> %p).\n", 201 + __func__, (void *)rec->ip, (void *)&ool_stub->insn[0]); 202 + return -EINVAL; 203 + } 204 + 205 + rec->arch.ool_stub = (unsigned long)&ool_stub->insn[0]; 206 + 207 + /* bl ftrace_caller */ 208 + if (!mod) 209 + ret = ftrace_get_call_inst(rec, (unsigned long)ftrace_caller, &inst); 210 + #ifdef CONFIG_MODULES 211 + else 212 + /* 213 + * We can't use ftrace_get_call_inst() since that uses 214 + * __module_text_address(rec->ip) to look up the module. 215 + * But, since the module is not fully formed at this stage, 216 + * the lookup fails. We know the target though, so generate 217 + * the branch inst directly. 218 + */ 219 + inst = ftrace_create_branch_inst(ftrace_get_ool_stub(rec) + MCOUNT_INSN_SIZE, 220 + mod->arch.tramp, 1); 221 + #endif 222 + ool_stub_template.insn[1] = ppc_inst_val(inst); 223 + 224 + /* b rec->ip + 4 */ 225 + if (!ret && create_branch(&inst, &ool_stub->insn[3], rec->ip + MCOUNT_INSN_SIZE, 0)) 226 + return -EINVAL; 227 + ool_stub_template.insn[3] = ppc_inst_val(inst); 228 + 229 + if (!ret) 230 + ret = patch_instructions((u32 *)ool_stub, (u32 *)&ool_stub_template, 231 + sizeof(ool_stub_template), false); 232 + 233 + return ret; 234 + #else /* !CONFIG_PPC_FTRACE_OUT_OF_LINE */ 235 + BUILD_BUG(); 236 + #endif 166 237 } 167 238 168 239 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS ··· 263 162 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) 264 163 { 265 164 ppc_inst_t old, new; 266 - int ret; 165 + unsigned long ip = rec->ip; 166 + int ret = 0; 267 167 268 168 /* This can only ever be called during module load */ 269 - if (WARN_ON(!IS_ENABLED(CONFIG_MODULES) || core_kernel_text(rec->ip))) 169 + if (WARN_ON(!IS_ENABLED(CONFIG_MODULES) || core_kernel_text(ip))) 270 170 return -EINVAL; 271 171 272 172 old = ppc_inst(PPC_RAW_NOP()); 273 - ret = ftrace_get_call_inst(rec, addr, &new); 274 - if (ret) 275 - return ret; 173 + if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) { 174 + ip = ftrace_get_ool_stub(rec) + MCOUNT_INSN_SIZE; /* second instruction in stub */ 175 + ret = ftrace_get_call_inst(rec, (unsigned long)ftrace_caller, &old); 176 + } 276 177 277 - return ftrace_modify_code(rec->ip, old, new); 178 + ret |= ftrace_get_call_inst(rec, addr, &new); 179 + 180 + if (!ret) 181 + ret = ftrace_modify_code(ip, old, new); 182 + 183 + if (!ret && IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) 184 + ret = ftrace_modify_code(rec->ip, ppc_inst(PPC_RAW_NOP()), 185 + ppc_inst(PPC_RAW_BRANCH((long)ftrace_get_ool_stub(rec) - (long)rec->ip))); 186 + 187 + return ret; 278 188 } 279 189 280 190 int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) ··· 318 206 new_addr = ftrace_get_addr_new(rec); 319 207 update = ftrace_update_record(rec, enable); 320 208 209 + if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) && update != FTRACE_UPDATE_IGNORE) { 210 + ip = ftrace_get_ool_stub(rec) + MCOUNT_INSN_SIZE; 211 + ret = ftrace_get_call_inst(rec, (unsigned long)ftrace_caller, &nop_inst); 212 + if (ret) 213 + goto out; 214 + } 215 + 321 216 switch (update) { 322 217 case FTRACE_UPDATE_IGNORE: 323 218 default: ··· 349 230 350 231 if (!ret) 351 232 ret = ftrace_modify_code(ip, old, new); 233 + 234 + if (!ret && IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) && 235 + (update == FTRACE_UPDATE_MAKE_NOP || update == FTRACE_UPDATE_MAKE_CALL)) { 236 + /* Update the actual ftrace location */ 237 + call_inst = ppc_inst(PPC_RAW_BRANCH((long)ftrace_get_ool_stub(rec) - 238 + (long)rec->ip)); 239 + nop_inst = ppc_inst(PPC_RAW_NOP()); 240 + ip = rec->ip; 241 + 242 + if (update == FTRACE_UPDATE_MAKE_NOP) 243 + ret = ftrace_modify_code(ip, call_inst, nop_inst); 244 + else 245 + ret = ftrace_modify_code(ip, nop_inst, call_inst); 246 + 247 + if (ret) 248 + goto out; 249 + } 250 + 352 251 if (ret) 353 252 goto out; 354 253 } ··· 386 249 /* Verify instructions surrounding the ftrace location */ 387 250 if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) { 388 251 /* Expect nops */ 389 - ret = ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_NOP())); 252 + if (!IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) 253 + ret = ftrace_validate_inst(ip - 4, ppc_inst(PPC_RAW_NOP())); 390 254 if (!ret) 391 255 ret = ftrace_validate_inst(ip, ppc_inst(PPC_RAW_NOP())); 392 256 } else if (IS_ENABLED(CONFIG_PPC32)) { ··· 414 276 415 277 if (ret) 416 278 return ret; 279 + 280 + /* Set up out-of-line stub */ 281 + if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) 282 + return ftrace_init_ool_stub(mod, rec); 417 283 418 284 /* Nop-out the ftrace location */ 419 285 new = ppc_inst(PPC_RAW_NOP());
+91 -25
arch/powerpc/kernel/trace/ftrace_entry.S
··· 56 56 SAVE_GPR(2, r1) 57 57 SAVE_GPRS(11, 31, r1) 58 58 .else 59 - #ifdef CONFIG_LIVEPATCH_64 59 + #if defined(CONFIG_LIVEPATCH_64) || defined(CONFIG_PPC_FTRACE_OUT_OF_LINE) 60 60 SAVE_GPR(14, r1) 61 61 #endif 62 62 .endif ··· 78 78 79 79 /* Get the _mcount() call site out of LR */ 80 80 mflr r7 81 - /* Save it as pt_regs->nip */ 82 - PPC_STL r7, _NIP(r1) 83 - /* Also save it in B's stackframe header for proper unwind */ 84 - PPC_STL r7, LRSAVE+SWITCH_FRAME_SIZE(r1) 85 81 /* Save the read LR in pt_regs->link */ 86 82 PPC_STL r0, _LINK(r1) 87 83 ··· 92 96 lwz r5,function_trace_op@l(r3) 93 97 #endif 94 98 95 - #ifdef CONFIG_LIVEPATCH_64 96 - mr r14, r7 /* remember old NIP */ 97 - #endif 98 - 99 - /* Calculate ip from nip-4 into r3 for call below */ 100 - subi r3, r7, MCOUNT_INSN_SIZE 101 - 102 - /* Put the original return address in r4 as parent_ip */ 103 - mr r4, r0 104 - 105 99 /* Save special regs */ 106 100 PPC_STL r8, _MSR(r1) 107 101 .if \allregs == 1 ··· 100 114 PPC_STL r11, _CCR(r1) 101 115 .endif 102 116 117 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 118 + /* Save our real return address in nvr for return */ 119 + .if \allregs == 0 120 + SAVE_GPR(15, r1) 121 + .endif 122 + mr r15, r7 123 + /* 124 + * We want the ftrace location in the function, but our lr (in r7) 125 + * points at the 'mtlr r0' instruction in the out of line stub. To 126 + * recover the ftrace location, we read the branch instruction in the 127 + * stub, and adjust our lr by the branch offset. 128 + * 129 + * See ftrace_init_ool_stub() for the profile sequence. 130 + */ 131 + lwz r8, MCOUNT_INSN_SIZE(r7) 132 + slwi r8, r8, 6 133 + srawi r8, r8, 6 134 + add r3, r7, r8 135 + /* 136 + * Override our nip to point past the branch in the original function. 137 + * This allows reliable stack trace and the ftrace stack tracer to work as-is. 138 + */ 139 + addi r7, r3, MCOUNT_INSN_SIZE 140 + #else 141 + /* Calculate ip from nip-4 into r3 for call below */ 142 + subi r3, r7, MCOUNT_INSN_SIZE 143 + #endif 144 + 145 + /* Save NIP as pt_regs->nip */ 146 + PPC_STL r7, _NIP(r1) 147 + /* Also save it in B's stackframe header for proper unwind */ 148 + PPC_STL r7, LRSAVE+SWITCH_FRAME_SIZE(r1) 149 + #if defined(CONFIG_LIVEPATCH_64) || defined(CONFIG_PPC_FTRACE_OUT_OF_LINE) 150 + mr r14, r7 /* remember old NIP */ 151 + #endif 152 + 153 + /* Put the original return address in r4 as parent_ip */ 154 + mr r4, r0 155 + 103 156 /* Load &pt_regs in r6 for call below */ 104 157 addi r6, r1, STACK_INT_FRAME_REGS 105 158 .endm 106 159 107 160 .macro ftrace_regs_exit allregs 161 + #ifndef CONFIG_PPC_FTRACE_OUT_OF_LINE 108 162 /* Load ctr with the possibly modified NIP */ 109 163 PPC_LL r3, _NIP(r1) 110 164 mtctr r3 ··· 152 126 #ifdef CONFIG_LIVEPATCH_64 153 127 cmpd r14, r3 /* has NIP been altered? */ 154 128 #endif 129 + #else /* !CONFIG_PPC_FTRACE_OUT_OF_LINE */ 130 + /* Load LR with the possibly modified NIP */ 131 + PPC_LL r3, _NIP(r1) 132 + cmpd r14, r3 /* has NIP been altered? */ 133 + bne- 1f 134 + 135 + mr r3, r15 136 + .if \allregs == 0 137 + REST_GPR(15, r1) 138 + .endif 139 + 1: mtlr r3 140 + #endif 155 141 156 142 /* Restore gprs */ 157 143 .if \allregs == 1 158 144 REST_GPRS(2, 31, r1) 159 145 .else 160 146 REST_GPRS(3, 10, r1) 161 - #ifdef CONFIG_LIVEPATCH_64 147 + #if defined(CONFIG_LIVEPATCH_64) || defined(CONFIG_PPC_FTRACE_OUT_OF_LINE) 162 148 REST_GPR(14, r1) 163 149 #endif 164 150 .endif 165 151 166 152 /* Restore possibly modified LR */ 167 153 PPC_LL r0, _LINK(r1) 154 + #ifndef CONFIG_PPC_FTRACE_OUT_OF_LINE 168 155 mtlr r0 156 + #endif 169 157 170 158 #ifdef CONFIG_PPC64 171 159 /* Restore callee's TOC */ ··· 193 153 /* Based on the cmpd above, if the NIP was altered handle livepatch */ 194 154 bne- livepatch_handler 195 155 #endif 196 - bctr /* jump after _mcount site */ 156 + /* jump after _mcount site */ 157 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 158 + /* 159 + * Return with blr to keep the link stack balanced. The function profiling sequence 160 + * uses 'mtlr r0' to restore LR. 161 + */ 162 + blr 163 + #else 164 + bctr 165 + #endif 197 166 .endm 198 167 199 168 _GLOBAL(ftrace_regs_caller) ··· 226 177 227 178 #ifdef CONFIG_PPC64 228 179 ftrace_no_trace: 180 + #ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE 181 + REST_GPR(3, r1) 182 + addi r1, r1, SWITCH_FRAME_SIZE+STACK_FRAME_MIN_SIZE 183 + blr 184 + #else 229 185 mflr r3 230 186 mtctr r3 231 187 REST_GPR(3, r1) 232 188 addi r1, r1, SWITCH_FRAME_SIZE+STACK_FRAME_MIN_SIZE 233 189 mtlr r0 234 190 bctr 191 + #endif 235 192 #endif 236 193 237 194 #ifdef CONFIG_LIVEPATCH_64 ··· 249 194 * We get here when a function A, calls another function B, but B has 250 195 * been live patched with a new function C. 251 196 * 252 - * On entry: 253 - * - we have no stack frame and can not allocate one 197 + * On entry, we have no stack frame and can not allocate one. 198 + * 199 + * With PPC_FTRACE_OUT_OF_LINE=n, on entry: 254 200 * - LR points back to the original caller (in A) 255 201 * - CTR holds the new NIP in C 256 202 * - r0, r11 & r12 are free 203 + * 204 + * With PPC_FTRACE_OUT_OF_LINE=y, on entry: 205 + * - r0 points back to the original caller (in A) 206 + * - LR holds the new NIP in C 207 + * - r11 & r12 are free 257 208 */ 258 209 livepatch_handler: 259 210 ld r12, PACA_THREAD_INFO(r13) ··· 269 208 addi r11, r11, 24 270 209 std r11, TI_livepatch_sp(r12) 271 210 272 - /* Save toc & real LR on livepatch stack */ 273 - std r2, -24(r11) 274 - mflr r12 275 - std r12, -16(r11) 276 - 277 211 /* Store stack end marker */ 278 212 lis r12, STACK_END_MAGIC@h 279 213 ori r12, r12, STACK_END_MAGIC@l 280 214 std r12, -8(r11) 281 215 282 - /* Put ctr in r12 for global entry and branch there */ 216 + /* Save toc & real LR on livepatch stack */ 217 + std r2, -24(r11) 218 + #ifndef CONFIG_PPC_FTRACE_OUT_OF_LINE 219 + mflr r12 220 + std r12, -16(r11) 283 221 mfctr r12 222 + #else 223 + std r0, -16(r11) 224 + mflr r12 225 + /* Put ctr in r12 for global entry and branch there */ 226 + mtctr r12 227 + #endif 284 228 bctrl 285 229 286 230 /*
+2
arch/powerpc/tools/.gitignore
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + /vmlinux.arch.S
+9
arch/powerpc/tools/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + quiet_cmd_gen_ftrace_ool_stubs = GEN $@ 4 + cmd_gen_ftrace_ool_stubs = $< "$(CONFIG_64BIT)" "$(OBJDUMP)" vmlinux.o $@ 5 + 6 + $(obj)/vmlinux.arch.S: $(src)/ftrace-gen-ool-stubs.sh vmlinux.o FORCE 7 + $(call if_changed,gen_ftrace_ool_stubs) 8 + 9 + targets += vmlinux.arch.S
+41
arch/powerpc/tools/ftrace-gen-ool-stubs.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0-or-later 3 + 4 + # Error out on error 5 + set -e 6 + 7 + is_64bit="$1" 8 + objdump="$2" 9 + vmlinux_o="$3" 10 + arch_vmlinux_S="$4" 11 + 12 + RELOCATION=R_PPC64_ADDR64 13 + if [ -z "$is_64bit" ]; then 14 + RELOCATION=R_PPC_ADDR32 15 + fi 16 + 17 + num_ool_stubs_text=$($objdump -r -j __patchable_function_entries "$vmlinux_o" | 18 + grep -v ".init.text" | grep -c "$RELOCATION") 19 + num_ool_stubs_inittext=$($objdump -r -j __patchable_function_entries "$vmlinux_o" | 20 + grep ".init.text" | grep -c "$RELOCATION") 21 + 22 + cat > "$arch_vmlinux_S" <<EOF 23 + #include <asm/asm-offsets.h> 24 + #include <linux/linkage.h> 25 + 26 + .pushsection .tramp.ftrace.text,"aw" 27 + SYM_DATA(ftrace_ool_stub_text_end_count, .long $num_ool_stubs_text) 28 + 29 + SYM_CODE_START(ftrace_ool_stub_text_end) 30 + .space $num_ool_stubs_text * FTRACE_OOL_STUB_SIZE 31 + SYM_CODE_END(ftrace_ool_stub_text_end) 32 + .popsection 33 + 34 + .pushsection .tramp.ftrace.init,"aw" 35 + SYM_DATA(ftrace_ool_stub_inittext_count, .long $num_ool_stubs_inittext) 36 + 37 + SYM_CODE_START(ftrace_ool_stub_inittext) 38 + .space $num_ool_stubs_inittext * FTRACE_OOL_STUB_SIZE 39 + SYM_CODE_END(ftrace_ool_stub_inittext) 40 + .popsection 41 + EOF