Skip to content

Commit 23ab18d

Browse files
committed
powerpc/ftrace: Add support for DYNAMIC_FTRACE_WITH_CALL_OPS
JIRA: https://issues.redhat.com/browse/RHEL-24555 commit e717754 Author: Naveen N Rao <naveen@kernel.org> Date: Wed Oct 30 12:38:47 2024 +0530 powerpc/ftrace: Add support for DYNAMIC_FTRACE_WITH_CALL_OPS Implement support for DYNAMIC_FTRACE_WITH_CALL_OPS similar to the arm64 implementation. This works by patching-in a pointer to an associated ftrace_ops structure before each traceable function. If multiple ftrace_ops are associated with a call site, then a special ftrace_list_ops is used to enable iterating over all the registered ftrace_ops. If no ftrace_ops are associated with a call site, then a special ftrace_nop_ops structure is used to render the ftrace call as a no-op. ftrace trampoline can then read the associated ftrace_ops for a call site by loading from an offset from the LR, and branch directly to the associated function. The primary advantage with this approach is that we don't have to iterate over all the registered ftrace_ops for call sites that have a single ftrace_ops registered. This is the equivalent of implementing support for dynamic ftrace trampolines, which set up a special ftrace trampoline for each registered ftrace_ops and have individual call sites branch into those directly. A secondary advantage is that this gives us a way to add support for direct ftrace callers without having to resort to using stubs. The address of the direct call trampoline can be loaded from the ftrace_ops structure. To support this, we reserve a nop before each function on 32-bit powerpc. For 64-bit powerpc, two nops are reserved before each out-of-line stub. During ftrace activation, we update this location with the associated ftrace_ops pointer. Then, on ftrace entry, we load from this location and call into ftrace_ops->func(). For 64-bit powerpc, we ensure that the out-of-line stub area is doubleword aligned so that ftrace_ops address can be updated atomically. Signed-off-by: Naveen N Rao <naveen@kernel.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://patch.msgid.link/20241030070850.1361304-15-hbathini@linux.ibm.com Signed-off-by: Viktor Malik <vmalik@redhat.com>
1 parent 997b2e2 commit 23ab18d

File tree

7 files changed

+102
-12
lines changed

7 files changed

+102
-12
lines changed

arch/powerpc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ config PPC
236236
select HAVE_DEBUG_STACKOVERFLOW
237237
select HAVE_DYNAMIC_FTRACE
238238
select HAVE_DYNAMIC_FTRACE_WITH_ARGS if ARCH_USING_PATCHABLE_FUNCTION_ENTRY || MPROFILE_KERNEL || PPC32
239+
select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if PPC_FTRACE_OUT_OF_LINE || (PPC32 && ARCH_USING_PATCHABLE_FUNCTION_ENTRY)
239240
select HAVE_DYNAMIC_FTRACE_WITH_REGS if ARCH_USING_PATCHABLE_FUNCTION_ENTRY || MPROFILE_KERNEL || PPC32
240241
select HAVE_EBPF_JIT
241242
select HAVE_EFFICIENT_UNALIGNED_ACCESS

arch/powerpc/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,12 @@ KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
158158
ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE
159159
CC_FLAGS_FTRACE := -fpatchable-function-entry=1
160160
else
161+
ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS # PPC32 only
162+
CC_FLAGS_FTRACE := -fpatchable-function-entry=3,1
163+
else
161164
CC_FLAGS_FTRACE := -fpatchable-function-entry=2
162165
endif
166+
endif
163167
else
164168
CC_FLAGS_FTRACE := -pg
165169
ifdef CONFIG_MPROFILE_KERNEL

arch/powerpc/include/asm/ftrace.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,11 @@ static inline u8 this_cpu_get_ftrace_enabled(void) { return 1; }
136136
extern unsigned int ftrace_tramp_text[], ftrace_tramp_init[];
137137
#ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE
138138
struct ftrace_ool_stub {
139+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
140+
struct ftrace_ops *ftrace_op;
141+
#endif
139142
u32 insn[4];
140-
};
143+
} __aligned(sizeof(unsigned long));
141144
extern struct ftrace_ool_stub ftrace_ool_stub_text_end[], ftrace_ool_stub_text[],
142145
ftrace_ool_stub_inittext[];
143146
extern unsigned int ftrace_ool_stub_text_end_count, ftrace_ool_stub_text_count,

arch/powerpc/kernel/asm-offsets.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,5 +681,9 @@ int main(void)
681681
DEFINE(FTRACE_OOL_STUB_SIZE, sizeof(struct ftrace_ool_stub));
682682
#endif
683683

684+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
685+
OFFSET(FTRACE_OPS_FUNC, ftrace_ops, func);
686+
#endif
687+
684688
return 0;
685689
}

arch/powerpc/kernel/trace/ftrace.c

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ unsigned long ftrace_call_adjust(unsigned long addr)
3838
return 0;
3939

4040
if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY) &&
41-
!IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE))
41+
!IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE)) {
4242
addr += MCOUNT_INSN_SIZE;
43+
if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
44+
addr += MCOUNT_INSN_SIZE;
45+
}
4346

4447
return addr;
4548
}
@@ -264,6 +267,46 @@ static int ftrace_init_ool_stub(struct module *mod, struct dyn_ftrace *rec)
264267
#endif
265268
}
266269

270+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
271+
static const struct ftrace_ops *powerpc_rec_get_ops(struct dyn_ftrace *rec)
272+
{
273+
const struct ftrace_ops *ops = NULL;
274+
275+
if (rec->flags & FTRACE_FL_CALL_OPS_EN) {
276+
ops = ftrace_find_unique_ops(rec);
277+
WARN_ON_ONCE(!ops);
278+
}
279+
280+
if (!ops)
281+
ops = &ftrace_list_ops;
282+
283+
return ops;
284+
}
285+
286+
static int ftrace_rec_set_ops(struct dyn_ftrace *rec, const struct ftrace_ops *ops)
287+
{
288+
if (IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE))
289+
return patch_ulong((void *)(ftrace_get_ool_stub(rec) - sizeof(unsigned long)),
290+
(unsigned long)ops);
291+
else
292+
return patch_ulong((void *)(rec->ip - MCOUNT_INSN_SIZE - sizeof(unsigned long)),
293+
(unsigned long)ops);
294+
}
295+
296+
static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec)
297+
{
298+
return ftrace_rec_set_ops(rec, &ftrace_nop_ops);
299+
}
300+
301+
static int ftrace_rec_update_ops(struct dyn_ftrace *rec)
302+
{
303+
return ftrace_rec_set_ops(rec, powerpc_rec_get_ops(rec));
304+
}
305+
#else
306+
static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec) { return 0; }
307+
static int ftrace_rec_update_ops(struct dyn_ftrace *rec) { return 0; }
308+
#endif
309+
267310
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
268311
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr)
269312
{
@@ -294,6 +337,10 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
294337
if (!ret)
295338
ret = ftrace_modify_code(ip, old, new);
296339

340+
ret = ftrace_rec_update_ops(rec);
341+
if (ret)
342+
return ret;
343+
297344
if (!ret && IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE))
298345
ret = ftrace_modify_code(rec->ip, ppc_inst(PPC_RAW_NOP()),
299346
ppc_inst(PPC_RAW_BRANCH((long)ftrace_get_ool_stub(rec) - (long)rec->ip)));
@@ -345,16 +392,19 @@ void ftrace_replace_code(int enable)
345392
case FTRACE_UPDATE_MODIFY_CALL:
346393
ret = ftrace_get_call_inst(rec, new_addr, &new_call_inst);
347394
ret |= ftrace_get_call_inst(rec, addr, &call_inst);
395+
ret |= ftrace_rec_update_ops(rec);
348396
old = call_inst;
349397
new = new_call_inst;
350398
break;
351399
case FTRACE_UPDATE_MAKE_NOP:
352400
ret = ftrace_get_call_inst(rec, addr, &call_inst);
401+
ret |= ftrace_rec_set_nop_ops(rec);
353402
old = call_inst;
354403
new = nop_inst;
355404
break;
356405
case FTRACE_UPDATE_MAKE_CALL:
357406
ret = ftrace_get_call_inst(rec, new_addr, &call_inst);
407+
ret |= ftrace_rec_update_ops(rec);
358408
old = nop_inst;
359409
new = call_inst;
360410
break;
@@ -470,6 +520,13 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
470520
ppc_inst_t old, new;
471521
int ret;
472522

523+
/*
524+
* When using CALL_OPS, the function to call is associated with the
525+
* call site, and we don't have a global function pointer to update.
526+
*/
527+
if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))
528+
return 0;
529+
473530
old = ppc_inst_read((u32 *)&ftrace_call);
474531
new = ftrace_create_branch_inst(ip, ppc_function_entry(func), 1);
475532
ret = ftrace_modify_code(ip, old, new);

arch/powerpc/kernel/trace/ftrace_entry.S

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,21 @@
8585
/* Save callee's TOC in the ABI compliant location */
8686
std r2, STK_GOT(r1)
8787
LOAD_PACA_TOC() /* get kernel TOC in r2 */
88+
#endif
89+
90+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
91+
/* r7 points to the instruction following the call to ftrace */
92+
PPC_LL r5, -(MCOUNT_INSN_SIZE*2 + SZL)(r7)
93+
PPC_LL r12, FTRACE_OPS_FUNC(r5)
94+
mtctr r12
95+
#else /* !CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS */
96+
#ifdef CONFIG_PPC64
8897
LOAD_REG_ADDR(r3, function_trace_op)
8998
ld r5,0(r3)
9099
#else
91100
lis r3,function_trace_op@ha
92101
lwz r5,function_trace_op@l(r3)
102+
#endif
93103
#endif
94104

95105
/* Save special regs */
@@ -205,20 +215,30 @@
205215
#endif
206216
.endm
207217

208-
_GLOBAL(ftrace_regs_caller)
209-
ftrace_regs_entry 1
210-
/* ftrace_call(r3, r4, r5, r6) */
218+
.macro ftrace_regs_func allregs
219+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
220+
bctrl
221+
#else
222+
.if \allregs == 1
211223
.globl ftrace_regs_call
212224
ftrace_regs_call:
225+
.else
226+
.globl ftrace_call
227+
ftrace_call:
228+
.endif
229+
/* ftrace_call(r3, r4, r5, r6) */
213230
bl ftrace_stub
231+
#endif
232+
.endm
233+
234+
_GLOBAL(ftrace_regs_caller)
235+
ftrace_regs_entry 1
236+
ftrace_regs_func 1
214237
ftrace_regs_exit 1
215238

216239
_GLOBAL(ftrace_caller)
217240
ftrace_regs_entry 0
218-
/* ftrace_call(r3, r4, r5, r6) */
219-
.globl ftrace_call
220-
ftrace_call:
221-
bl ftrace_stub
241+
ftrace_regs_func 0
222242
ftrace_regs_exit 0
223243

224244
_GLOBAL(ftrace_stub)
@@ -377,7 +397,7 @@ _GLOBAL(return_to_handler)
377397
#ifdef CONFIG_PPC_FTRACE_OUT_OF_LINE
378398
SYM_DATA(ftrace_ool_stub_text_count, .long CONFIG_PPC_FTRACE_OUT_OF_LINE_NUM_RESERVE)
379399

380-
SYM_CODE_START(ftrace_ool_stub_text)
400+
SYM_START(ftrace_ool_stub_text, SYM_L_GLOBAL, .balign SZL)
381401
.space CONFIG_PPC_FTRACE_OUT_OF_LINE_NUM_RESERVE * FTRACE_OOL_STUB_SIZE
382402
SYM_CODE_END(ftrace_ool_stub_text)
383403
#endif

arch/powerpc/tools/ftrace-gen-ool-stubs.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ fi
2828

2929
cat > "$arch_vmlinux_S" <<EOF
3030
#include <asm/asm-offsets.h>
31+
#include <asm/ppc_asm.h>
3132
#include <linux/linkage.h>
3233
3334
.pushsection .tramp.ftrace.text,"aw"
3435
SYM_DATA(ftrace_ool_stub_text_end_count, .long $num_ool_stubs_text_end)
3536
36-
SYM_CODE_START(ftrace_ool_stub_text_end)
37+
SYM_START(ftrace_ool_stub_text_end, SYM_L_GLOBAL, .balign SZL)
3738
#if $num_ool_stubs_text_end
3839
.space $num_ool_stubs_text_end * FTRACE_OOL_STUB_SIZE
3940
#endif
@@ -43,7 +44,7 @@ SYM_CODE_END(ftrace_ool_stub_text_end)
4344
.pushsection .tramp.ftrace.init,"aw"
4445
SYM_DATA(ftrace_ool_stub_inittext_count, .long $num_ool_stubs_inittext)
4546
46-
SYM_CODE_START(ftrace_ool_stub_inittext)
47+
SYM_START(ftrace_ool_stub_inittext, SYM_L_GLOBAL, .balign SZL)
4748
.space $num_ool_stubs_inittext * FTRACE_OOL_STUB_SIZE
4849
SYM_CODE_END(ftrace_ool_stub_inittext)
4950
.popsection

0 commit comments

Comments
 (0)