Skip to content

Commit 41c7eca

Browse files
rnavNobody
authored andcommitted
powerpc/ftrace: Reserve instructions from function entry for ftrace
On some architectures, enabling function tracing results in multiple instructions being emitted at function entry. As an example, on powerpc64 with -mprofile-kernel, two instructions are emitted at function entry: mflr r0 bl _mcount It is desirable to nop out both these instructions when ftrace is not active. For that purpose, it is essential to mark both these instructions as belonging to ftrace so that other kernel subsystems (such as kprobes) do not modify these instructions. Add support for this by allowing architectures to override ftrace_cmp_recs() and to match against address ranges over and above a single MCOUNT_INSN_SIZE. For powerpc32, we mark the two instructions preceding the call to _mcount() as belonging to ftrace. For powerpc64, an additional aspect to consider is that functions can have a global entry point for setting up the TOC when invoked from other modules. If present, global entry point always involves two instructions (addis/lis and addi). To handle this, we provide a custom ftrace_init_nop() for powerpc64 where we identify functions having a global entry point and record this information in the LSB of dyn_ftrace->arch.mod. This information is used in ftrace_cmp_recs() to reserve instructions from the global entry point. Suggested-by: Steven Rostedt <[email protected]> Signed-off-by: Naveen N. Rao <[email protected]>
1 parent 31a23b2 commit 41c7eca

File tree

3 files changed

+117
-10
lines changed

3 files changed

+117
-10
lines changed

arch/powerpc/include/asm/ftrace.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,21 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
5959
struct dyn_arch_ftrace {
6060
struct module *mod;
6161
};
62+
63+
struct dyn_ftrace;
64+
struct module *ftrace_mod_addr_get(struct dyn_ftrace *rec);
65+
void ftrace_mod_addr_set(struct dyn_ftrace *rec, struct module *mod);
66+
67+
#ifdef CONFIG_MPROFILE_KERNEL
68+
int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
69+
#define ftrace_init_nop ftrace_init_nop
70+
#endif
71+
72+
#if defined(CONFIG_MPROFILE_KERNEL) || defined(CONFIG_PPC32)
73+
int ftrace_cmp_recs(const void *a, const void *b);
74+
#define ftrace_cmp_recs ftrace_cmp_recs
75+
#endif
76+
6277
#endif /* __ASSEMBLY__ */
6378

6479
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS

arch/powerpc/kernel/trace/ftrace.c

Lines changed: 100 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -428,21 +428,21 @@ int ftrace_make_nop(struct module *mod,
428428
* We should either already have a pointer to the module
429429
* or it has been passed in.
430430
*/
431-
if (!rec->arch.mod) {
431+
if (!ftrace_mod_addr_get(rec)) {
432432
if (!mod) {
433433
pr_err("No module loaded addr=%lx\n", addr);
434434
return -EFAULT;
435435
}
436-
rec->arch.mod = mod;
436+
ftrace_mod_addr_set(rec, mod);
437437
} else if (mod) {
438-
if (mod != rec->arch.mod) {
438+
if (mod != ftrace_mod_addr_get(rec)) {
439439
pr_err("Record mod %p not equal to passed in mod %p\n",
440-
rec->arch.mod, mod);
440+
ftrace_mod_addr_get(rec), mod);
441441
return -EINVAL;
442442
}
443443
/* nothing to do if mod == rec->arch.mod */
444444
} else
445-
mod = rec->arch.mod;
445+
mod = ftrace_mod_addr_get(rec);
446446

447447
return __ftrace_make_nop(mod, rec, addr);
448448
#else
@@ -451,6 +451,96 @@ int ftrace_make_nop(struct module *mod,
451451
#endif /* CONFIG_MODULES */
452452
}
453453

454+
#define FUNC_MCOUNT_OFFSET_PPC32 8
455+
#define FUNC_MCOUNT_OFFSET_PPC64_LEP 4
456+
#define FUNC_MCOUNT_OFFSET_PPC64_GEP 12
457+
458+
#ifdef CONFIG_MPROFILE_KERNEL
459+
struct module *ftrace_mod_addr_get(struct dyn_ftrace *rec)
460+
{
461+
return (struct module *)((unsigned long)rec->arch.mod & ~0x1);
462+
}
463+
464+
void ftrace_mod_addr_set(struct dyn_ftrace *rec, struct module *mod)
465+
{
466+
rec->arch.mod = (struct module *)(((unsigned long)rec->arch.mod & 0x1) | (unsigned long)mod);
467+
}
468+
469+
int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
470+
{
471+
unsigned long offset, ip = rec->ip;
472+
ppc_inst_t op1, op2;
473+
int ret;
474+
475+
if (!kallsyms_lookup_size_offset(rec->ip, NULL, &offset) ||
476+
(offset != FUNC_MCOUNT_OFFSET_PPC64_GEP && offset != FUNC_MCOUNT_OFFSET_PPC64_LEP)) {
477+
ip -= FUNC_MCOUNT_OFFSET_PPC64_GEP;
478+
ret = copy_inst_from_kernel_nofault(&op1, (void *)ip);
479+
ret |= copy_inst_from_kernel_nofault(&op2, (void *)(ip + MCOUNT_INSN_SIZE));
480+
if (!ret &&
481+
((ppc_inst_val(op1) & 0xffff0000) == PPC_RAW_LIS(_R2, 0) ||
482+
(ppc_inst_val(op1) & 0xffff0000) == PPC_RAW_ADDIS(_R2, _R12, 0)) &&
483+
(ppc_inst_val(op2) & 0xffff0000) == PPC_RAW_ADDI(_R2, _R2, 0))
484+
ftrace_mod_addr_set(rec, (struct module *)1);
485+
} else if (offset == FUNC_MCOUNT_OFFSET_PPC64_GEP) {
486+
ftrace_mod_addr_set(rec, (struct module *)1);
487+
}
488+
489+
return ftrace_make_nop(mod, rec, MCOUNT_ADDR);
490+
}
491+
#else
492+
struct module *ftrace_mod_addr_get(struct dyn_ftrace *rec)
493+
{
494+
return rec->arch.mod;
495+
}
496+
497+
void ftrace_mod_addr_set(struct dyn_ftrace *rec, struct module *mod)
498+
{
499+
rec->arch.mod = mod;
500+
}
501+
#endif /* CONFIG_MPROFILE_KERNEL */
502+
503+
#if defined(CONFIG_MPROFILE_KERNEL) || defined(CONFIG_PPC32)
504+
int ftrace_location_get_offset(const struct dyn_ftrace *rec)
505+
{
506+
if (IS_ENABLED(CONFIG_MPROFILE_KERNEL))
507+
/*
508+
* On ppc64le with -mprofile-kernel, function entry can have:
509+
* addis r2, r12, M
510+
* addi r2, r2, N
511+
* mflr r0
512+
* bl _mcount
513+
*
514+
* The first two instructions are for TOC setup and represent the global entry
515+
* point for cross-module calls, and may be missing if the function is never called
516+
* from other modules.
517+
*/
518+
return ((unsigned long)rec->arch.mod & 0x1) ? FUNC_MCOUNT_OFFSET_PPC64_GEP :
519+
FUNC_MCOUNT_OFFSET_PPC64_LEP;
520+
else
521+
/*
522+
* On ppc32, function entry always has:
523+
* mflr r0
524+
* stw r0, 4(r1)
525+
* bl _mcount
526+
*/
527+
return FUNC_MCOUNT_OFFSET_PPC32;
528+
}
529+
530+
int ftrace_cmp_recs(const void *a, const void *b)
531+
{
532+
const struct dyn_ftrace *key = a;
533+
const struct dyn_ftrace *rec = b;
534+
int offset = ftrace_location_get_offset(rec);
535+
536+
if (key->flags < rec->ip - offset)
537+
return -1;
538+
if (key->ip >= rec->ip + MCOUNT_INSN_SIZE)
539+
return 1;
540+
return 0;
541+
}
542+
#endif
543+
454544
#ifdef CONFIG_MODULES
455545
#ifdef CONFIG_PPC64
456546
/*
@@ -494,7 +584,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
494584
ppc_inst_t instr;
495585
void *ip = (void *)rec->ip;
496586
unsigned long entry, ptr, tramp;
497-
struct module *mod = rec->arch.mod;
587+
struct module *mod = ftrace_mod_addr_get(rec);
498588

499589
/* read where this goes */
500590
if (copy_inst_from_kernel_nofault(op, ip))
@@ -561,7 +651,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
561651
int err;
562652
ppc_inst_t op;
563653
u32 *ip = (u32 *)rec->ip;
564-
struct module *mod = rec->arch.mod;
654+
struct module *mod = ftrace_mod_addr_get(rec);
565655
unsigned long tramp;
566656

567657
/* read where this goes */
@@ -678,7 +768,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
678768
* Being that we are converting from nop, it had better
679769
* already have a module defined.
680770
*/
681-
if (!rec->arch.mod) {
771+
if (!ftrace_mod_addr_get(rec)) {
682772
pr_err("No module loaded\n");
683773
return -EINVAL;
684774
}
@@ -699,7 +789,7 @@ __ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
699789
ppc_inst_t op;
700790
unsigned long ip = rec->ip;
701791
unsigned long entry, ptr, tramp;
702-
struct module *mod = rec->arch.mod;
792+
struct module *mod = ftrace_mod_addr_get(rec);
703793

704794
/* If we never set up ftrace trampolines, then bail */
705795
if (!mod->arch.tramp || !mod->arch.tramp_regs) {
@@ -814,7 +904,7 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
814904
/*
815905
* Out of range jumps are called from modules.
816906
*/
817-
if (!rec->arch.mod) {
907+
if (!ftrace_mod_addr_get(rec)) {
818908
pr_err("No module loaded\n");
819909
return -EINVAL;
820910
}

kernel/trace/ftrace.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,7 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs)
15101510
}
15111511

15121512

1513+
#ifndef ftrace_cmp_recs
15131514
static int ftrace_cmp_recs(const void *a, const void *b)
15141515
{
15151516
const struct dyn_ftrace *key = a;
@@ -1521,6 +1522,7 @@ static int ftrace_cmp_recs(const void *a, const void *b)
15211522
return 1;
15221523
return 0;
15231524
}
1525+
#endif
15241526

15251527
static struct dyn_ftrace *lookup_rec(unsigned long start, unsigned long end)
15261528
{

0 commit comments

Comments
 (0)