aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNaveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>2018-04-19 03:04:09 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2018-05-03 08:32:29 -0400
commitae30cc05bed2fd7eb05e4fb53f412783f05ccb7b (patch)
treeb4293c28d14cb058d79585c44e5e60383df213a5
parent9ef404236438bf4934386dc2aa34ba7f0a9e1934 (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.h2
-rw-r--r--arch/powerpc/include/asm/module.h3
-rw-r--r--arch/powerpc/kernel/module_64.c28
-rw-r--r--arch/powerpc/kernel/trace/ftrace.c184
-rw-r--r--arch/powerpc/kernel/trace/ftrace_64_mprofile.S71
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 @@
49extern void _mcount(void); 49extern 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
54static inline unsigned long ftrace_call_adjust(unsigned long addr) 52static 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 */
768static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module *me) 772static 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
811static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module *me) 817static 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
817int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sechdrs) 824int 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
392int 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: */
400static int 419static 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
496static 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
590int 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
475int ftrace_update_ftrace_func(ftrace_func_t func) 625int 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
104ftrace_call: 104ftrace_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
165ftrace_caller_common:
165#ifdef CONFIG_FUNCTION_GRAPH_TRACER 166#ifdef CONFIG_FUNCTION_GRAPH_TRACER
166.globl ftrace_graph_call 167.globl ftrace_graph_call
167ftrace_graph_call: 168ftrace_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
223ftrace_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