powerpc64/ftrace: Implement support for ftrace_regs_caller()
With -mprofile-kernel, we always save the full register state in ftrace_caller(). While this works, this is inefficient if we're not interested in the register state, such as when we're using the function tracer. Rename the existing ftrace_caller() as ftrace_regs_caller() and provide a simpler implementation for ftrace_caller() that is used when registers are not required to be saved. Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:

committed by
Michael Ellerman

parent
9ef4042364
commit
ae30cc05be
@@ -357,6 +357,8 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
{
|
||||
unsigned int op[2];
|
||||
void *ip = (void *)rec->ip;
|
||||
unsigned long entry, ptr, tramp;
|
||||
struct module *mod = rec->arch.mod;
|
||||
|
||||
/* read where this goes */
|
||||
if (probe_kernel_read(op, ip, sizeof(op)))
|
||||
@@ -368,19 +370,44 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If we never set up a trampoline to ftrace_caller, then bail */
|
||||
if (!rec->arch.mod->arch.tramp) {
|
||||
/* If we never set up ftrace trampoline(s), then bail */
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
|
||||
if (!mod->arch.tramp || !mod->arch.tramp_regs) {
|
||||
#else
|
||||
if (!mod->arch.tramp) {
|
||||
#endif
|
||||
pr_err("No ftrace trampoline\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
|
||||
if (rec->flags & FTRACE_FL_REGS)
|
||||
tramp = mod->arch.tramp_regs;
|
||||
else
|
||||
#endif
|
||||
tramp = mod->arch.tramp;
|
||||
|
||||
if (module_trampoline_target(mod, tramp, &ptr)) {
|
||||
pr_err("Failed to get trampoline target\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
pr_devel("trampoline target %lx", ptr);
|
||||
|
||||
entry = ppc_global_function_entry((void *)addr);
|
||||
/* This should match what was called */
|
||||
if (ptr != entry) {
|
||||
pr_err("addr %lx does not match expected %lx\n", ptr, entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Ensure branch is within 24 bits */
|
||||
if (!create_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) {
|
||||
if (!create_branch(ip, tramp, BRANCH_SET_LINK)) {
|
||||
pr_err("Branch out of range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (patch_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) {
|
||||
if (patch_branch(ip, tramp, BRANCH_SET_LINK)) {
|
||||
pr_err("REL24 out of range!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -388,14 +415,6 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
|
||||
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
||||
unsigned long addr)
|
||||
{
|
||||
return ftrace_make_call(rec, addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* !CONFIG_PPC64: */
|
||||
static int
|
||||
__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
@@ -472,6 +491,137 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
#endif /* CONFIG_MODULES */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
|
||||
#ifdef CONFIG_MODULES
|
||||
static int
|
||||
__ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned int op;
|
||||
unsigned long ip = rec->ip;
|
||||
unsigned long entry, ptr, tramp;
|
||||
struct module *mod = rec->arch.mod;
|
||||
|
||||
/* If we never set up ftrace trampolines, then bail */
|
||||
if (!mod->arch.tramp || !mod->arch.tramp_regs) {
|
||||
pr_err("No ftrace trampoline\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* read where this goes */
|
||||
if (probe_kernel_read(&op, (void *)ip, sizeof(int))) {
|
||||
pr_err("Fetching opcode failed.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Make sure that that this is still a 24bit jump */
|
||||
if (!is_bl_op(op)) {
|
||||
pr_err("Not expected bl: opcode is %x\n", op);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* lets find where the pointer goes */
|
||||
tramp = find_bl_target(ip, op);
|
||||
entry = ppc_global_function_entry((void *)old_addr);
|
||||
|
||||
pr_devel("ip:%lx jumps to %lx", ip, tramp);
|
||||
|
||||
if (tramp != entry) {
|
||||
/* old_addr is not within range, so we must have used a trampoline */
|
||||
if (module_trampoline_target(mod, tramp, &ptr)) {
|
||||
pr_err("Failed to get trampoline target\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
pr_devel("trampoline target %lx", ptr);
|
||||
|
||||
/* This should match what was called */
|
||||
if (ptr != entry) {
|
||||
pr_err("addr %lx does not match expected %lx\n", ptr, entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* The new target may be within range */
|
||||
if (test_24bit_addr(ip, addr)) {
|
||||
/* within range */
|
||||
if (patch_branch((unsigned int *)ip, addr, BRANCH_SET_LINK)) {
|
||||
pr_err("REL24 out of range!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rec->flags & FTRACE_FL_REGS)
|
||||
tramp = mod->arch.tramp_regs;
|
||||
else
|
||||
tramp = mod->arch.tramp;
|
||||
|
||||
if (module_trampoline_target(mod, tramp, &ptr)) {
|
||||
pr_err("Failed to get trampoline target\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
pr_devel("trampoline target %lx", ptr);
|
||||
|
||||
entry = ppc_global_function_entry((void *)addr);
|
||||
/* This should match what was called */
|
||||
if (ptr != entry) {
|
||||
pr_err("addr %lx does not match expected %lx\n", ptr, entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Ensure branch is within 24 bits */
|
||||
if (!create_branch((unsigned int *)ip, tramp, BRANCH_SET_LINK)) {
|
||||
pr_err("Branch out of range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (patch_branch((unsigned int *)ip, tramp, BRANCH_SET_LINK)) {
|
||||
pr_err("REL24 out of range!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned long ip = rec->ip;
|
||||
unsigned int old, new;
|
||||
|
||||
/*
|
||||
* If the calling address is more that 24 bits away,
|
||||
* then we had to use a trampoline to make the call.
|
||||
* Otherwise just update the call site.
|
||||
*/
|
||||
if (test_24bit_addr(ip, addr) && test_24bit_addr(ip, old_addr)) {
|
||||
/* within range */
|
||||
old = ftrace_call_replace(ip, old_addr, 1);
|
||||
new = ftrace_call_replace(ip, addr, 1);
|
||||
return ftrace_modify_code(ip, old, new);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
/*
|
||||
* Out of range jumps are called from modules.
|
||||
*/
|
||||
if (!rec->arch.mod) {
|
||||
pr_err("No module loaded\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return __ftrace_modify_call(rec, old_addr, addr);
|
||||
#else
|
||||
/* We should not get here without modules */
|
||||
return -EINVAL;
|
||||
#endif /* CONFIG_MODULES */
|
||||
}
|
||||
#endif
|
||||
|
||||
int ftrace_update_ftrace_func(ftrace_func_t func)
|
||||
{
|
||||
unsigned long ip = (unsigned long)(&ftrace_call);
|
||||
@@ -482,6 +632,16 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
|
||||
new = ftrace_call_replace(ip, (unsigned long)func, 1);
|
||||
ret = ftrace_modify_code(ip, old, new);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
|
||||
/* Also update the regs callback function */
|
||||
if (!ret) {
|
||||
ip = (unsigned long)(&ftrace_regs_call);
|
||||
old = *(unsigned int *)&ftrace_regs_call;
|
||||
new = ftrace_call_replace(ip, (unsigned long)func, 1);
|
||||
ret = ftrace_modify_code(ip, old, new);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user