diff options
author | Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> | 2018-04-19 03:04:09 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2018-05-03 08:32:29 -0400 |
commit | ae30cc05bed2fd7eb05e4fb53f412783f05ccb7b (patch) | |
tree | b4293c28d14cb058d79585c44e5e60383df213a5 | |
parent | 9ef404236438bf4934386dc2aa34ba7f0a9e1934 (diff) |
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>
-rw-r--r-- | arch/powerpc/include/asm/ftrace.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/module.h | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/module_64.c | 28 | ||||
-rw-r--r-- | arch/powerpc/kernel/trace/ftrace.c | 184 | ||||
-rw-r--r-- | arch/powerpc/kernel/trace/ftrace_64_mprofile.S | 71 |
5 files changed, 262 insertions, 26 deletions
diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h index 3b5e85a72e10..f0806a2fd451 100644 --- a/arch/powerpc/include/asm/ftrace.h +++ b/arch/powerpc/include/asm/ftrace.h | |||
@@ -49,8 +49,6 @@ | |||
49 | extern void _mcount(void); | 49 | extern void _mcount(void); |
50 | 50 | ||
51 | #ifdef CONFIG_DYNAMIC_FTRACE | 51 | #ifdef CONFIG_DYNAMIC_FTRACE |
52 | # define FTRACE_ADDR ((unsigned long)ftrace_caller) | ||
53 | # define FTRACE_REGS_ADDR FTRACE_ADDR | ||
54 | static inline unsigned long ftrace_call_adjust(unsigned long addr) | 52 | static inline unsigned long ftrace_call_adjust(unsigned long addr) |
55 | { | 53 | { |
56 | /* reloction of mcount call site is the same as the address */ | 54 | /* reloction of mcount call site is the same as the address */ |
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h index 4f6573934792..18f7214d68b7 100644 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h | |||
@@ -53,6 +53,9 @@ struct mod_arch_specific { | |||
53 | #ifdef CONFIG_DYNAMIC_FTRACE | 53 | #ifdef CONFIG_DYNAMIC_FTRACE |
54 | unsigned long toc; | 54 | unsigned long toc; |
55 | unsigned long tramp; | 55 | unsigned long tramp; |
56 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
57 | unsigned long tramp_regs; | ||
58 | #endif | ||
56 | #endif | 59 | #endif |
57 | 60 | ||
58 | /* For module function descriptor dereference */ | 61 | /* For module function descriptor dereference */ |
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index 8413be31d6a4..f7667e2ebfcb 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c | |||
@@ -280,6 +280,10 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, | |||
280 | #ifdef CONFIG_DYNAMIC_FTRACE | 280 | #ifdef CONFIG_DYNAMIC_FTRACE |
281 | /* make the trampoline to the ftrace_caller */ | 281 | /* make the trampoline to the ftrace_caller */ |
282 | relocs++; | 282 | relocs++; |
283 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
284 | /* an additional one for ftrace_regs_caller */ | ||
285 | relocs++; | ||
286 | #endif | ||
283 | #endif | 287 | #endif |
284 | 288 | ||
285 | pr_debug("Looks like a total of %lu stubs, max\n", relocs); | 289 | pr_debug("Looks like a total of %lu stubs, max\n", relocs); |
@@ -765,7 +769,8 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, | |||
765 | * via the paca (in r13). The target (ftrace_caller()) is responsible for | 769 | * via the paca (in r13). The target (ftrace_caller()) is responsible for |
766 | * saving and restoring the toc before returning. | 770 | * saving and restoring the toc before returning. |
767 | */ | 771 | */ |
768 | static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module *me) | 772 | static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, |
773 | struct module *me, unsigned long addr) | ||
769 | { | 774 | { |
770 | struct ppc64_stub_entry *entry; | 775 | struct ppc64_stub_entry *entry; |
771 | unsigned int i, num_stubs; | 776 | unsigned int i, num_stubs; |
@@ -792,9 +797,10 @@ static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module | |||
792 | memcpy(entry->jump, stub_insns, sizeof(stub_insns)); | 797 | memcpy(entry->jump, stub_insns, sizeof(stub_insns)); |
793 | 798 | ||
794 | /* Stub uses address relative to kernel toc (from the paca) */ | 799 | /* Stub uses address relative to kernel toc (from the paca) */ |
795 | reladdr = (unsigned long)ftrace_caller - kernel_toc_addr(); | 800 | reladdr = addr - kernel_toc_addr(); |
796 | if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { | 801 | if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { |
797 | pr_err("%s: Address of ftrace_caller out of range of kernel_toc.\n", me->name); | 802 | pr_err("%s: Address of %ps out of range of kernel_toc.\n", |
803 | me->name, (void *)addr); | ||
798 | return 0; | 804 | return 0; |
799 | } | 805 | } |
800 | 806 | ||
@@ -802,22 +808,30 @@ static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module | |||
802 | entry->jump[2] |= PPC_LO(reladdr); | 808 | entry->jump[2] |= PPC_LO(reladdr); |
803 | 809 | ||
804 | /* Eventhough we don't use funcdata in the stub, it's needed elsewhere. */ | 810 | /* Eventhough we don't use funcdata in the stub, it's needed elsewhere. */ |
805 | entry->funcdata = func_desc((unsigned long)ftrace_caller); | 811 | entry->funcdata = func_desc(addr); |
806 | entry->magic = STUB_MAGIC; | 812 | entry->magic = STUB_MAGIC; |
807 | 813 | ||
808 | return (unsigned long)entry; | 814 | return (unsigned long)entry; |
809 | } | 815 | } |
810 | #else | 816 | #else |
811 | static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module *me) | 817 | static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, |
818 | struct module *me, unsigned long addr) | ||
812 | { | 819 | { |
813 | return stub_for_addr(sechdrs, (unsigned long)ftrace_caller, me); | 820 | return stub_for_addr(sechdrs, addr, me); |
814 | } | 821 | } |
815 | #endif | 822 | #endif |
816 | 823 | ||
817 | int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sechdrs) | 824 | int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sechdrs) |
818 | { | 825 | { |
819 | mod->arch.toc = my_r2(sechdrs, mod); | 826 | mod->arch.toc = my_r2(sechdrs, mod); |
820 | mod->arch.tramp = create_ftrace_stub(sechdrs, mod); | 827 | mod->arch.tramp = create_ftrace_stub(sechdrs, mod, |
828 | (unsigned long)ftrace_caller); | ||
829 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
830 | mod->arch.tramp_regs = create_ftrace_stub(sechdrs, mod, | ||
831 | (unsigned long)ftrace_regs_caller); | ||
832 | if (!mod->arch.tramp_regs) | ||
833 | return -ENOENT; | ||
834 | #endif | ||
821 | 835 | ||
822 | if (!mod->arch.tramp) | 836 | if (!mod->arch.tramp) |
823 | return -ENOENT; | 837 | return -ENOENT; |
diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c index 80667128db3d..79d2924e75d5 100644 --- a/arch/powerpc/kernel/trace/ftrace.c +++ b/arch/powerpc/kernel/trace/ftrace.c | |||
@@ -357,6 +357,8 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
357 | { | 357 | { |
358 | unsigned int op[2]; | 358 | unsigned int op[2]; |
359 | void *ip = (void *)rec->ip; | 359 | void *ip = (void *)rec->ip; |
360 | unsigned long entry, ptr, tramp; | ||
361 | struct module *mod = rec->arch.mod; | ||
360 | 362 | ||
361 | /* read where this goes */ | 363 | /* read where this goes */ |
362 | if (probe_kernel_read(op, ip, sizeof(op))) | 364 | if (probe_kernel_read(op, ip, sizeof(op))) |
@@ -368,19 +370,44 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
368 | return -EINVAL; | 370 | return -EINVAL; |
369 | } | 371 | } |
370 | 372 | ||
371 | /* If we never set up a trampoline to ftrace_caller, then bail */ | 373 | /* If we never set up ftrace trampoline(s), then bail */ |
372 | if (!rec->arch.mod->arch.tramp) { | 374 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS |
375 | if (!mod->arch.tramp || !mod->arch.tramp_regs) { | ||
376 | #else | ||
377 | if (!mod->arch.tramp) { | ||
378 | #endif | ||
373 | pr_err("No ftrace trampoline\n"); | 379 | pr_err("No ftrace trampoline\n"); |
374 | return -EINVAL; | 380 | return -EINVAL; |
375 | } | 381 | } |
376 | 382 | ||
383 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
384 | if (rec->flags & FTRACE_FL_REGS) | ||
385 | tramp = mod->arch.tramp_regs; | ||
386 | else | ||
387 | #endif | ||
388 | tramp = mod->arch.tramp; | ||
389 | |||
390 | if (module_trampoline_target(mod, tramp, &ptr)) { | ||
391 | pr_err("Failed to get trampoline target\n"); | ||
392 | return -EFAULT; | ||
393 | } | ||
394 | |||
395 | pr_devel("trampoline target %lx", ptr); | ||
396 | |||
397 | entry = ppc_global_function_entry((void *)addr); | ||
398 | /* This should match what was called */ | ||
399 | if (ptr != entry) { | ||
400 | pr_err("addr %lx does not match expected %lx\n", ptr, entry); | ||
401 | return -EINVAL; | ||
402 | } | ||
403 | |||
377 | /* Ensure branch is within 24 bits */ | 404 | /* Ensure branch is within 24 bits */ |
378 | if (!create_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { | 405 | if (!create_branch(ip, tramp, BRANCH_SET_LINK)) { |
379 | pr_err("Branch out of range\n"); | 406 | pr_err("Branch out of range\n"); |
380 | return -EINVAL; | 407 | return -EINVAL; |
381 | } | 408 | } |
382 | 409 | ||
383 | if (patch_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { | 410 | if (patch_branch(ip, tramp, BRANCH_SET_LINK)) { |
384 | pr_err("REL24 out of range!\n"); | 411 | pr_err("REL24 out of range!\n"); |
385 | return -EINVAL; | 412 | return -EINVAL; |
386 | } | 413 | } |
@@ -388,14 +415,6 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
388 | return 0; | 415 | return 0; |
389 | } | 416 | } |
390 | 417 | ||
391 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
392 | int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, | ||
393 | unsigned long addr) | ||
394 | { | ||
395 | return ftrace_make_call(rec, addr); | ||
396 | } | ||
397 | #endif | ||
398 | |||
399 | #else /* !CONFIG_PPC64: */ | 418 | #else /* !CONFIG_PPC64: */ |
400 | static int | 419 | static int |
401 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 420 | __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) | |||
472 | #endif /* CONFIG_MODULES */ | 491 | #endif /* CONFIG_MODULES */ |
473 | } | 492 | } |
474 | 493 | ||
494 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
495 | #ifdef CONFIG_MODULES | ||
496 | static int | ||
497 | __ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, | ||
498 | unsigned long addr) | ||
499 | { | ||
500 | unsigned int op; | ||
501 | unsigned long ip = rec->ip; | ||
502 | unsigned long entry, ptr, tramp; | ||
503 | struct module *mod = rec->arch.mod; | ||
504 | |||
505 | /* If we never set up ftrace trampolines, then bail */ | ||
506 | if (!mod->arch.tramp || !mod->arch.tramp_regs) { | ||
507 | pr_err("No ftrace trampoline\n"); | ||
508 | return -EINVAL; | ||
509 | } | ||
510 | |||
511 | /* read where this goes */ | ||
512 | if (probe_kernel_read(&op, (void *)ip, sizeof(int))) { | ||
513 | pr_err("Fetching opcode failed.\n"); | ||
514 | return -EFAULT; | ||
515 | } | ||
516 | |||
517 | /* Make sure that that this is still a 24bit jump */ | ||
518 | if (!is_bl_op(op)) { | ||
519 | pr_err("Not expected bl: opcode is %x\n", op); | ||
520 | return -EINVAL; | ||
521 | } | ||
522 | |||
523 | /* lets find where the pointer goes */ | ||
524 | tramp = find_bl_target(ip, op); | ||
525 | entry = ppc_global_function_entry((void *)old_addr); | ||
526 | |||
527 | pr_devel("ip:%lx jumps to %lx", ip, tramp); | ||
528 | |||
529 | if (tramp != entry) { | ||
530 | /* old_addr is not within range, so we must have used a trampoline */ | ||
531 | if (module_trampoline_target(mod, tramp, &ptr)) { | ||
532 | pr_err("Failed to get trampoline target\n"); | ||
533 | return -EFAULT; | ||
534 | } | ||
535 | |||
536 | pr_devel("trampoline target %lx", ptr); | ||
537 | |||
538 | /* This should match what was called */ | ||
539 | if (ptr != entry) { | ||
540 | pr_err("addr %lx does not match expected %lx\n", ptr, entry); | ||
541 | return -EINVAL; | ||
542 | } | ||
543 | } | ||
544 | |||
545 | /* The new target may be within range */ | ||
546 | if (test_24bit_addr(ip, addr)) { | ||
547 | /* within range */ | ||
548 | if (patch_branch((unsigned int *)ip, addr, BRANCH_SET_LINK)) { | ||
549 | pr_err("REL24 out of range!\n"); | ||
550 | return -EINVAL; | ||
551 | } | ||
552 | |||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | if (rec->flags & FTRACE_FL_REGS) | ||
557 | tramp = mod->arch.tramp_regs; | ||
558 | else | ||
559 | tramp = mod->arch.tramp; | ||
560 | |||
561 | if (module_trampoline_target(mod, tramp, &ptr)) { | ||
562 | pr_err("Failed to get trampoline target\n"); | ||
563 | return -EFAULT; | ||
564 | } | ||
565 | |||
566 | pr_devel("trampoline target %lx", ptr); | ||
567 | |||
568 | entry = ppc_global_function_entry((void *)addr); | ||
569 | /* This should match what was called */ | ||
570 | if (ptr != entry) { | ||
571 | pr_err("addr %lx does not match expected %lx\n", ptr, entry); | ||
572 | return -EINVAL; | ||
573 | } | ||
574 | |||
575 | /* Ensure branch is within 24 bits */ | ||
576 | if (!create_branch((unsigned int *)ip, tramp, BRANCH_SET_LINK)) { | ||
577 | pr_err("Branch out of range\n"); | ||
578 | return -EINVAL; | ||
579 | } | ||
580 | |||
581 | if (patch_branch((unsigned int *)ip, tramp, BRANCH_SET_LINK)) { | ||
582 | pr_err("REL24 out of range!\n"); | ||
583 | return -EINVAL; | ||
584 | } | ||
585 | |||
586 | return 0; | ||
587 | } | ||
588 | #endif | ||
589 | |||
590 | int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, | ||
591 | unsigned long addr) | ||
592 | { | ||
593 | unsigned long ip = rec->ip; | ||
594 | unsigned int old, new; | ||
595 | |||
596 | /* | ||
597 | * If the calling address is more that 24 bits away, | ||
598 | * then we had to use a trampoline to make the call. | ||
599 | * Otherwise just update the call site. | ||
600 | */ | ||
601 | if (test_24bit_addr(ip, addr) && test_24bit_addr(ip, old_addr)) { | ||
602 | /* within range */ | ||
603 | old = ftrace_call_replace(ip, old_addr, 1); | ||
604 | new = ftrace_call_replace(ip, addr, 1); | ||
605 | return ftrace_modify_code(ip, old, new); | ||
606 | } | ||
607 | |||
608 | #ifdef CONFIG_MODULES | ||
609 | /* | ||
610 | * Out of range jumps are called from modules. | ||
611 | */ | ||
612 | if (!rec->arch.mod) { | ||
613 | pr_err("No module loaded\n"); | ||
614 | return -EINVAL; | ||
615 | } | ||
616 | |||
617 | return __ftrace_modify_call(rec, old_addr, addr); | ||
618 | #else | ||
619 | /* We should not get here without modules */ | ||
620 | return -EINVAL; | ||
621 | #endif /* CONFIG_MODULES */ | ||
622 | } | ||
623 | #endif | ||
624 | |||
475 | int ftrace_update_ftrace_func(ftrace_func_t func) | 625 | int ftrace_update_ftrace_func(ftrace_func_t func) |
476 | { | 626 | { |
477 | unsigned long ip = (unsigned long)(&ftrace_call); | 627 | unsigned long ip = (unsigned long)(&ftrace_call); |
@@ -482,6 +632,16 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
482 | new = ftrace_call_replace(ip, (unsigned long)func, 1); | 632 | new = ftrace_call_replace(ip, (unsigned long)func, 1); |
483 | ret = ftrace_modify_code(ip, old, new); | 633 | ret = ftrace_modify_code(ip, old, new); |
484 | 634 | ||
635 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS | ||
636 | /* Also update the regs callback function */ | ||
637 | if (!ret) { | ||
638 | ip = (unsigned long)(&ftrace_regs_call); | ||
639 | old = *(unsigned int *)&ftrace_regs_call; | ||
640 | new = ftrace_call_replace(ip, (unsigned long)func, 1); | ||
641 | ret = ftrace_modify_code(ip, old, new); | ||
642 | } | ||
643 | #endif | ||
644 | |||
485 | return ret; | 645 | return ret; |
486 | } | 646 | } |
487 | 647 | ||
diff --git a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S index ae1cbe783ab6..ed9d7a46c3af 100644 --- a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S +++ b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S | |||
@@ -20,8 +20,8 @@ | |||
20 | #ifdef CONFIG_DYNAMIC_FTRACE | 20 | #ifdef CONFIG_DYNAMIC_FTRACE |
21 | /* | 21 | /* |
22 | * | 22 | * |
23 | * ftrace_caller() is the function that replaces _mcount() when ftrace is | 23 | * ftrace_caller()/ftrace_regs_caller() is the function that replaces _mcount() |
24 | * active. | 24 | * when ftrace is active. |
25 | * | 25 | * |
26 | * We arrive here after a function A calls function B, and we are the trace | 26 | * We arrive here after a function A calls function B, and we are the trace |
27 | * function for B. When we enter r1 points to A's stack frame, B has not yet | 27 | * function for B. When we enter r1 points to A's stack frame, B has not yet |
@@ -37,7 +37,7 @@ | |||
37 | * Our job is to save the register state into a struct pt_regs (on the stack) | 37 | * Our job is to save the register state into a struct pt_regs (on the stack) |
38 | * and then arrange for the ftrace function to be called. | 38 | * and then arrange for the ftrace function to be called. |
39 | */ | 39 | */ |
40 | _GLOBAL(ftrace_caller) | 40 | _GLOBAL(ftrace_regs_caller) |
41 | /* Save the original return address in A's stack frame */ | 41 | /* Save the original return address in A's stack frame */ |
42 | std r0,LRSAVE(r1) | 42 | std r0,LRSAVE(r1) |
43 | 43 | ||
@@ -100,8 +100,8 @@ _GLOBAL(ftrace_caller) | |||
100 | addi r6, r1 ,STACK_FRAME_OVERHEAD | 100 | addi r6, r1 ,STACK_FRAME_OVERHEAD |
101 | 101 | ||
102 | /* ftrace_call(r3, r4, r5, r6) */ | 102 | /* ftrace_call(r3, r4, r5, r6) */ |
103 | .globl ftrace_call | 103 | .globl ftrace_regs_call |
104 | ftrace_call: | 104 | ftrace_regs_call: |
105 | bl ftrace_stub | 105 | bl ftrace_stub |
106 | nop | 106 | nop |
107 | 107 | ||
@@ -162,6 +162,7 @@ ftrace_call: | |||
162 | bne- livepatch_handler | 162 | bne- livepatch_handler |
163 | #endif | 163 | #endif |
164 | 164 | ||
165 | ftrace_caller_common: | ||
165 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 166 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
166 | .globl ftrace_graph_call | 167 | .globl ftrace_graph_call |
167 | ftrace_graph_call: | 168 | ftrace_graph_call: |
@@ -182,6 +183,66 @@ ftrace_no_trace: | |||
182 | mtlr r0 | 183 | mtlr r0 |
183 | bctr | 184 | bctr |
184 | 185 | ||
186 | _GLOBAL(ftrace_caller) | ||
187 | /* Save the original return address in A's stack frame */ | ||
188 | std r0, LRSAVE(r1) | ||
189 | |||
190 | /* Create our stack frame + pt_regs */ | ||
191 | stdu r1, -SWITCH_FRAME_SIZE(r1) | ||
192 | |||
193 | /* Save all gprs to pt_regs */ | ||
194 | SAVE_8GPRS(3, r1) | ||
195 | |||
196 | lbz r3, PACA_FTRACE_ENABLED(r13) | ||
197 | cmpdi r3, 0 | ||
198 | beq ftrace_no_trace | ||
199 | |||
200 | /* Get the _mcount() call site out of LR */ | ||
201 | mflr r7 | ||
202 | std r7, _NIP(r1) | ||
203 | |||
204 | /* Save callee's TOC in the ABI compliant location */ | ||
205 | std r2, 24(r1) | ||
206 | ld r2, PACATOC(r13) /* get kernel TOC in r2 */ | ||
207 | |||
208 | addis r3, r2, function_trace_op@toc@ha | ||
209 | addi r3, r3, function_trace_op@toc@l | ||
210 | ld r5, 0(r3) | ||
211 | |||
212 | /* Calculate ip from nip-4 into r3 for call below */ | ||
213 | subi r3, r7, MCOUNT_INSN_SIZE | ||
214 | |||
215 | /* Put the original return address in r4 as parent_ip */ | ||
216 | mr r4, r0 | ||
217 | |||
218 | /* Set pt_regs to NULL */ | ||
219 | li r6, 0 | ||
220 | |||
221 | /* ftrace_call(r3, r4, r5, r6) */ | ||
222 | .globl ftrace_call | ||
223 | ftrace_call: | ||
224 | bl ftrace_stub | ||
225 | nop | ||
226 | |||
227 | ld r3, _NIP(r1) | ||
228 | mtctr r3 | ||
229 | |||
230 | /* Restore gprs */ | ||
231 | REST_8GPRS(3,r1) | ||
232 | |||
233 | /* Restore callee's TOC */ | ||
234 | ld r2, 24(r1) | ||
235 | |||
236 | /* Pop our stack frame */ | ||
237 | addi r1, r1, SWITCH_FRAME_SIZE | ||
238 | |||
239 | /* Reload original LR */ | ||
240 | ld r0, LRSAVE(r1) | ||
241 | mtlr r0 | ||
242 | |||
243 | /* Handle function_graph or go back */ | ||
244 | b ftrace_caller_common | ||
245 | |||
185 | #ifdef CONFIG_LIVEPATCH | 246 | #ifdef CONFIG_LIVEPATCH |
186 | /* | 247 | /* |
187 | * This function runs in the mcount context, between two functions. As | 248 | * This function runs in the mcount context, between two functions. As |