From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:28:52
Currently we generate the module stub for ftrace_caller() at the bottom
of apply_relocate_add(). However apply_relocate_add() is potentially
called more than once per module, which means we will try to generate
the ftrace_caller() stub multiple times.
Although the current code deals with that correctly, ie. it only
generates a stub the first time, it would be clearer to only try to
generate the stub once.
Note also on first reading it may appear that we generate a different
stub for each section that requires relocation, but that is not the
case. The code in stub_for_addr() that searches for an existing stub
uses sechdrs[me->arch.stubs_section], ie. the single stub section for
this module.
A cleaner approach is to only generate the ftrace_caller() stub once,
from module_finalize(). An additional benefit is we can clean the ifdefs
up a little.
Finally we must propagate the const'ness of some of the pointers passed
to module_finalize(), but that is also an improvement.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/include/asm/module.h | 9 +++++++++
arch/powerpc/kernel/module.c | 5 +++++
arch/powerpc/kernel/module_32.c | 15 ++++++++++-----
arch/powerpc/kernel/module_64.c | 22 ++++++++++++++--------
4 files changed, 38 insertions(+), 13 deletions(-)
@@ -413,7 +413,7 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (thisgivesthevaluemaximumspaninaninstructionwhichusesasignedoffset)*/-staticinlineunsignedlongmy_r2(Elf64_Shdr*sechdrs,structmodule*me)+staticinlineunsignedlongmy_r2(constElf64_Shdr*sechdrs,structmodule*me){returnsechdrs[me->arch.toc_section].sh_addr+0x8000;}
@@ -426,7 +426,7 @@ static inline unsigned long my_r2(Elf64_Shdr *sechdrs, struct module *me)#define PPC_HA(v) PPC_HI ((v) + 0x8000)/* Patch stub to reference function and correct r2 value. */-staticinlineintcreate_stub(Elf64_Shdr*sechdrs,+staticinlineintcreate_stub(constElf64_Shdr*sechdrs,structppc64_stub_entry*entry,unsignedlongaddr,structmodule*me)
@@ -452,7 +452,7 @@ static inline int create_stub(Elf64_Shdr *sechdrs,/* Create stub to jump to function described in this OPD/ptr: we need thestubtosetuptheTOCptr(r2)forthefunction.*/-staticunsignedlongstub_for_addr(Elf64_Shdr*sechdrs,+staticunsignedlongstub_for_addr(constElf64_Shdr*sechdrs,unsignedlongaddr,structmodule*me){
@@ -693,12 +693,18 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,}}+return0;+}+#ifdef CONFIG_DYNAMIC_FTRACE-me->arch.toc=my_r2(sechdrs,me);-me->arch.tramp=stub_for_addr(sechdrs,-(unsignedlong)ftrace_caller,-me);-#endif+intmodule_finalize_ftrace(structmodule*mod,constElf_Shdr*sechdrs)+{+mod->arch.toc=my_r2(sechdrs,mod);+mod->arch.tramp=stub_for_addr(sechdrs,(unsignedlong)ftrace_caller,mod);++if(!mod->arch.tramp)+return-ENOENT;return0;}+#endif
@@ -1237,6 +1237,11 @@ _GLOBAL(ftrace_graph_caller)stdr5,64(r1)stdr4,56(r1)stdr3,48(r1)++/*Savecallee's TOC in the ABI compliant location */+stdr2,24(r1)+ldr2,PACATOC(r13)/*getkernelTOCinr2*/+mfctrr4/*ftrace_callerhasmovedlocaladdrhere*/stdr4,40(r1)mflrr3/*ftrace_callerhasrestoredLRfromstack*/
@@ -1237,6 +1237,11 @@ _GLOBAL(ftrace_graph_caller)stdr5,64(r1)stdr4,56(r1)stdr3,48(r1)++/*Savecallee's TOC in the ABI compliant location */+stdr2,24(r1)
R2_STACK_OFFSET for readability?
quoted hunk
+ ld r2, PACATOC(r13) /* get kernel TOC in r2 */
+
mfctr r4 /* ftrace_caller has moved local addr here */
std r4, 40(r1)
mflr r3 /* ftrace_caller has restored LR from stack */
@@ -1237,6 +1237,11 @@ _GLOBAL(ftrace_graph_caller)stdr5,64(r1)stdr4,56(r1)stdr3,48(r1)++/*Savecallee's TOC in the ABI compliant location */+stdr2,24(r1)
R2_STACK_OFFSET for readability?
Hmm, maybe. Personally when I see "24(r1)" what my brain reads is "stack TOC
save slot", but maybe I've been spending too much time with powerpc assembly.
R2_STACK_OFFSET is actually new, pulled out from the module code by Torsten.
Other code uses STK_GOT to mean the same thing. I don't really like either
name, so I'll probably leave do a clean up once this is in.
cheers
@@ -1237,6 +1237,11 @@ _GLOBAL(ftrace_graph_caller)stdr5,64(r1)stdr4,56(r1)stdr3,48(r1)++/*Savecallee's TOC in the ABI compliant location */+stdr2,24(r1)
R2_STACK_OFFSET for readability?
I have encountered LRSAVE vs. PPC_LR_STKOFF and
STK_GOT vs. R2_STACK_OFFSET, some usable in assembler source, some in C.
quoted
+ ld r2, PACATOC(r13) /* get kernel TOC in r2 */
+
mfctr r4 /* ftrace_caller has moved local addr here */
std r4, 40(r1)
mflr r3 /* ftrace_caller has restored LR from stack */
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:28:58
From: Torsten Duwe <redacted>
The gcc switch -mprofile-kernel, available for ppc64 on gcc > 4.8.5,
allows to call _mcount very early in the function, which low-level
ASM code and code patching functions need to consider.
Especially the link register and the parameter registers are still
alive and not yet saved into a new stack frame.
* arch/powerpc/kernel/entry_64.S:
- modify the default _mcount to be prepared for such call sites
- have the ftrace_graph_caller save function arguments before
calling its C helper prepare_ftrace_return
* arch/powerpc/include/asm/code-patching.h:
- define some common macros to make things readable.
- pull the R2 stack location definition from
arch/powerpc/kernel/module_64.c
* arch/powerpc/kernel/module_64.c:
- enhance binary code examination to handle the new patterns.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/include/asm/code-patching.h | 24 ++++++++++++++++
arch/powerpc/kernel/entry_64.S | 48 +++++++++++++++++++++++++++++++-
arch/powerpc/kernel/ftrace.c | 44 ++++++++++++++++++++++-------
arch/powerpc/kernel/module_64.c | 31 +++++++++++++++++++--
4 files changed, 133 insertions(+), 14 deletions(-)
@@ -99,4 +99,28 @@ static inline unsigned long ppc_global_function_entry(void *func)#endif}+#ifdef CONFIG_PPC64+/* Some instruction encodings commonly used in dynamic ftracing+*andfunctionlivepatching:+*/++/* This must match the definition of STK_GOT in <asm/ppc_asm.h> */+#if defined(_CALL_ELF) && _CALL_ELF == 2+#define R2_STACK_OFFSET 24+#else+#define R2_STACK_OFFSET 40+#endif++/* load / store the TOC from / into the stack frame */+#define PPC_INST_LD_TOC (PPC_INST_LD | ___PPC_RT(__REG_R2) | \+___PPC_RA(__REG_R1)|R2_STACK_OFFSET)+#define PPC_INST_STD_TOC (PPC_INST_STD | ___PPC_RS(__REG_R2) | \+___PPC_RA(__REG_R1)|R2_STACK_OFFSET)++/* usually preceded by a mflr r0 */+#define PPC_INST_STD_LR (PPC_INST_STD | ___PPC_RS(__REG_R0) | \+___PPC_RA(__REG_R1)|PPC_LR_STKOFF)++#endif /* CONFIG_PPC64 */+#endif /* _ASM_POWERPC_CODE_PATCHING_H */
@@ -281,16 +281,14 @@ int ftrace_make_nop(struct module *mod,#ifdef CONFIG_MODULES#ifdef CONFIG_PPC64+/* Examine the existing instructions for __ftrace_make_call.+*TheyshouldeffectivelybeaNOP,andfollowformalconstraints,+*dependingontheABI.Returnfalseiftheydon't.+*/+#ifndef CC_USING_MPROFILE_KERNELstaticint-__ftrace_make_call(structdyn_ftrace*rec,unsignedlongaddr)+expected_nop_sequence(void*ip,unsignedintop0,unsignedintop1){-unsignedintop[2];-void*ip=(void*)rec->ip;--/* read where this goes */-if(probe_kernel_read(op,ip,sizeof(op)))-return-EFAULT;-/**Weexpecttosee:*
@@ -300,8 +298,34 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)*TheloadoffsetisdifferentdependingontheABI.Forsimplicity*justmaskitoutwhendoingthecompare.*/-if((op[0]!=0x48000008)||((op[1]&0xffff0000)!=0xe8410000)){-pr_err("Unexpected call sequence: %x %x\n",op[0],op[1]);+if((op0!=0x48000008)||((op1&0xffff0000)!=0xe8410000))+return0;+return1;+}+#else+staticint+expected_nop_sequence(void*ip,unsignedintop0,unsignedintop1)+{+/* look for patched "NOP" on ppc64 with -mprofile-kernel */+if(op0!=PPC_INST_NOP)+return0;+return1;+}+#endif++staticint+__ftrace_make_call(structdyn_ftrace*rec,unsignedlongaddr)+{+unsignedintop[2];+void*ip=(void*)rec->ip;++/* read where this goes */+if(probe_kernel_read(op,ip,sizeof(op)))+return-EFAULT;++if(!expected_nop_sequence(ip,op[0],op[1])){+pr_err("Unexpected call sequence at %p: %x %x\n",+ip,op[0],op[1]);return-EINVAL;}
@@ -41,7 +41,6 @@--RR.*/#if defined(_CALL_ELF) && _CALL_ELF == 2-#define R2_STACK_OFFSET 24/* An address is simply the address of the function. */typedefunsignedlongfunc_desc_t;
@@ -73,7 +72,6 @@ static unsigned int local_entry_offset(const Elf64_Sym *sym)returnPPC64_LOCAL_ENTRY_OFFSET(sym->st_other);}#else-#define R2_STACK_OFFSET 40/* An address is address of the OPD entry, which contains address of fn. */typedefstructppc64_opd_entryfunc_desc_t;
@@ -450,17 +448,44 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,return(unsignedlong)&stubs[i];}+#ifdef CC_USING_MPROFILE_KERNEL+staticintis_early_mcount_callsite(u32*instruction)+{+/* -mprofile-kernel sequence starting with+*mflrr0andmaybestdr0,LRSAVE(r1).+*/+if((instruction[-3]==PPC_INST_MFLR&&+instruction[-2]==PPC_INST_STD_LR)||+instruction[-2]==PPC_INST_MFLR){+/* Nothing to be done here, it's an _mcount+*calllocationandr2willhavetobe+*restoredinthe_mcountfunction.+*/+return1;+}+return0;+}+#else+/* without -mprofile-kernel, mcount calls are never early */+staticintis_early_mcount_callsite(u32*instruction)+{+return0;+}+#endif+/* We expect a noop next: if it is, replace it with instruction torestorer2.*/staticintrestore_r2(u32*instruction,structmodule*me){if(*instruction!=PPC_INST_NOP){+if(is_early_mcount_callsite(instruction))+return1;pr_err("%s: Expect noop after relocate, got %08x\n",me->name,*instruction);return0;}/* ld r2,R2_STACK_OFFSET(r1) */-*instruction=0xe8410000|R2_STACK_OFFSET;+*instruction=PPC_INST_LD_TOC;return1;}
From: Torsten Duwe <redacted>
The gcc switch -mprofile-kernel, available for ppc64 on gcc > 4.8.5,
allows to call _mcount very early in the function, which low-level
ASM code and code patching functions need to consider.
Especially the link register and the parameter registers are still
alive and not yet saved into a new stack frame.
* arch/powerpc/kernel/entry_64.S:
- modify the default _mcount to be prepared for such call sites
- have the ftrace_graph_caller save function arguments before
calling its C helper prepare_ftrace_return
* arch/powerpc/include/asm/code-patching.h:
- define some common macros to make things readable.
- pull the R2 stack location definition from
arch/powerpc/kernel/module_64.c
* arch/powerpc/kernel/module_64.c:
- enhance binary code examination to handle the new patterns.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/include/asm/code-patching.h | 24 ++++++++++++++++
arch/powerpc/kernel/entry_64.S | 48 +++++++++++++++++++++++++++++++-
arch/powerpc/kernel/ftrace.c | 44 ++++++++++++++++++++++-------
arch/powerpc/kernel/module_64.c | 31 +++++++++++++++++++--
4 files changed, 133 insertions(+), 14 deletions(-)
@@ -99,4 +99,28 @@ static inline unsigned long ppc_global_function_entry(void *func)#endif}+#ifdef CONFIG_PPC64+/* Some instruction encodings commonly used in dynamic ftracing+*andfunctionlivepatching:+*/++/* This must match the definition of STK_GOT in <asm/ppc_asm.h> */+#if defined(_CALL_ELF) && _CALL_ELF == 2+#define R2_STACK_OFFSET 24+#else+#define R2_STACK_OFFSET 40+#endif++/* load / store the TOC from / into the stack frame */+#define PPC_INST_LD_TOC (PPC_INST_LD | ___PPC_RT(__REG_R2) | \+___PPC_RA(__REG_R1)|R2_STACK_OFFSET)+#define PPC_INST_STD_TOC (PPC_INST_STD | ___PPC_RS(__REG_R2) | \+___PPC_RA(__REG_R1)|R2_STACK_OFFSET)++/* usually preceded by a mflr r0 */+#define PPC_INST_STD_LR (PPC_INST_STD | ___PPC_RS(__REG_R0) | \+___PPC_RA(__REG_R1)|PPC_LR_STKOFF)++#endif /* CONFIG_PPC64 */+#endif /* _ASM_POWERPC_CODE_PATCHING_H */
@@ -281,16 +281,14 @@ int ftrace_make_nop(struct module *mod,#ifdef CONFIG_MODULES#ifdef CONFIG_PPC64+/* Examine the existing instructions for __ftrace_make_call.+*TheyshouldeffectivelybeaNOP,andfollowformalconstraints,+*dependingontheABI.Returnfalseiftheydon't.+*/+#ifndef CC_USING_MPROFILE_KERNELstaticint-__ftrace_make_call(structdyn_ftrace*rec,unsignedlongaddr)+expected_nop_sequence(void*ip,unsignedintop0,unsignedintop1){-unsignedintop[2];-void*ip=(void*)rec->ip;--/* read where this goes */-if(probe_kernel_read(op,ip,sizeof(op)))-return-EFAULT;-/**Weexpecttosee:*
@@ -300,8 +298,34 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)*TheloadoffsetisdifferentdependingontheABI.Forsimplicity*justmaskitoutwhendoingthecompare.*/-if((op[0]!=0x48000008)||((op[1]&0xffff0000)!=0xe8410000)){-pr_err("Unexpected call sequence: %x %x\n",op[0],op[1]);+if((op0!=0x48000008)||((op1&0xffff0000)!=0xe8410000))+return0;+return1;+}+#else+staticint+expected_nop_sequence(void*ip,unsignedintop0,unsignedintop1)+{+/* look for patched "NOP" on ppc64 with -mprofile-kernel */+if(op0!=PPC_INST_NOP)+return0;+return1;
With the magic changes, do we care for this? I think it's a bit of an overkill
quoted hunk
+}
+#endif
+
+static int
+__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned int op[2];
+ void *ip = (void *)rec->ip;
+
+ /* read where this goes */
+ if (probe_kernel_read(op, ip, sizeof(op)))
+ return -EFAULT;
+
+ if (!expected_nop_sequence(ip, op[0], op[1])) {
+ pr_err("Unexpected call sequence at %p: %x %x\n",
+ ip, op[0], op[1]);
return -EINVAL;
}
@@ -41,7 +41,6 @@--RR.*/#if defined(_CALL_ELF) && _CALL_ELF == 2-#define R2_STACK_OFFSET 24/* An address is simply the address of the function. */typedefunsignedlongfunc_desc_t;
@@ -73,7 +72,6 @@ static unsigned int local_entry_offset(const Elf64_Sym *sym)returnPPC64_LOCAL_ENTRY_OFFSET(sym->st_other);}#else-#define R2_STACK_OFFSET 40/* An address is address of the OPD entry, which contains address of fn. */typedefstructppc64_opd_entryfunc_desc_t;
@@ -450,17 +448,44 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,return(unsignedlong)&stubs[i];}+#ifdef CC_USING_MPROFILE_KERNEL+staticintis_early_mcount_callsite(u32*instruction)+{+/* -mprofile-kernel sequence starting with+*mflrr0andmaybestdr0,LRSAVE(r1).+*/+if((instruction[-3]==PPC_INST_MFLR&&+instruction[-2]==PPC_INST_STD_LR)||+instruction[-2]==PPC_INST_MFLR){+/* Nothing to be done here, it's an _mcount+*calllocationandr2willhavetobe+*restoredinthe_mcountfunction.+*/+return1;+}+return0;+}+#else+/* without -mprofile-kernel, mcount calls are never early */+staticintis_early_mcount_callsite(u32*instruction)+{+return0;+}+#endif+/* We expect a noop next: if it is, replace it with instruction torestorer2.*/staticintrestore_r2(u32*instruction,structmodule*me){if(*instruction!=PPC_INST_NOP){+if(is_early_mcount_callsite(instruction))+return1;
I don't think we need this either, since mcount callsites use a different stub now for ftrace
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-25 10:37:12
On Thu, 2016-02-25 at 11:28 +1100, Balbir Singh wrote:
On 25/02/16 01:28, Michael Ellerman wrote:
quoted
@@ -300,8 +298,34 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) * The load offset is different depending on the ABI. For simplicity * just mask it out when doing the compare. */- if ((op[0] != 0x48000008) || ((op[1] & 0xffff0000) != 0xe8410000)) {- pr_err("Unexpected call sequence: %x %x\n", op[0], op[1]);+ if ((op0 != 0x48000008) || ((op1 & 0xffff0000) != 0xe8410000))+ return 0;+ return 1;+}+#else+static int+expected_nop_sequence(void *ip, unsigned int op0, unsigned int op1)+{+ /* look for patched "NOP" on ppc64 with -mprofile-kernel */+ if (op0 != PPC_INST_NOP)+ return 0;+ return 1;
With the magic changes, do we care for this? I think it's a bit of an overkill
I don't particularly like it either. However this code doesn't actually use the
magic, it's the reverse case of turning a nop into a call to the stub. So the
magic in the stub doesn't actually make that any safer.
I think we do at least want to check there's a nop there. But without
mprofile-kernel it's not a nop, so we need some check and it does need to be
different between the profiling ABIs. So I think for now this is the
conservative approach.
cheers
On Thu, Feb 25, 2016 at 01:28:27AM +1100, Michael Ellerman wrote:
quoted hunk
@@ -450,17 +448,44 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs, return (unsigned long)&stubs[i]; }+#ifdef CC_USING_MPROFILE_KERNEL+static int is_early_mcount_callsite(u32 *instruction)+{+ /* -mprofile-kernel sequence starting with+ * mflr r0 and maybe std r0, LRSAVE(r1).+ */+ if ((instruction[-3] == PPC_INST_MFLR &&+ instruction[-2] == PPC_INST_STD_LR) ||+ instruction[-2] == PPC_INST_MFLR) {+ /* Nothing to be done here, it's an _mcount+ * call location and r2 will have to be+ * restored in the _mcount function.+ */+ return 1;+ }+ return 0;+}+#else
*You* said this might page fault :)
Did we agree yet whether we insist on a streamlined compiler?
(GCC commit e95d0248dace required)?
If not:
if (instruction[-2] == PPC_INST_STD_LR)
{
if (instruction[-3] == PPC_INST_MFLR)
return 1;
}
else if (instruction[-2] == PPC_INST_MFLR)
return 1;
return 0;
leaves less freedom for the compiler to "optimise".
Signed-off-by: Torsten Duwe <redacted>
Torsten
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:28:59
The main change is to just use paca->kernel_toc, rather than a branch to
+4 and mflr etc. That makes the code simpler and should also perform
better.
There was also a sequence after ftrace_call() where we load from
pt_regs->nip, move to LR, then a few instructions later load from LRSAVE
and move to LR. Instead I think we want to put pt_regs->nip into CTR and
branch to it later.
We also rework some of the SPR loads to hopefully speed them up a bit.
Also comment the asm much more, to hopefully make it clearer.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/entry_64.S | 94 ++++++++++++++++++++++++++++--------------
1 file changed, 62 insertions(+), 32 deletions(-)
Squash.
@@ -1171,65 +1171,98 @@ _GLOBAL(ftrace_graph_stub)mtlrr0addir1,r1,112#else+/*+*+*ftrace_caller()isthefunctionthatreplaces_mcount()whenftraceis+*active.+*+*WearrivehereafterafunctionAcallsfunctionB,andwearethetrace+*functionforB.Whenweenterr1pointstoA's stack frame, B has not yet+*hadachancetoallocateoneyet.+*+*Additionallyr2maypointeithertotheTOCforA,orB,dependingon+*whetherBdidaTOCsetupsequencebeforecallingus.+*+*OnentrytheLRpointsbacktothe_mcount()callsite,andr0holdsthe+*savedLRasitwasonentrytoB,ie.theoriginalreturnaddressatthe+*callsiteinA.+*+*Ourjobistosavetheregisterstateintoastructpt_regs (onthestack)+*andthenarrangefortheftracefunctiontobecalled.+*/ _GLOBAL(ftrace_caller)+/*SavetheoriginalreturnaddressinA's stack frame */stdr0,LRSAVE(r1)-#if defined(_CALL_ELF) && _CALL_ELF == 2-mflrr0-bl2f-2:mflrr12-mtlrr0-mrr0,r2/*savecallee's TOC */-addisr2,r12,(.TOC.-ftrace_caller-12)@ha-addir2,r2,(.TOC.-ftrace_caller-12)@l-#else-mrr0,r2-#endif-ldr12,LRSAVE(r1)/*getcaller's address */+/*Createourstackframe+pt_regs*/stdur1,-SWITCH_FRAME_SIZE(r1)-stdr12,_LINK(r1)+/*Saveallgprstopt_regs*/SAVE_8GPRS(0,r1)-stdr0,24(r1)/*saveTOC*/SAVE_8GPRS(8,r1)SAVE_8GPRS(16,r1)SAVE_8GPRS(24,r1)+/*Loadspecialregsforsavebelow*/+mfmsrr8+mfctrr9+mfxerr10+mfcrr11++/*Getthe_mcount()callsiteoutofLR*/+mflrr7+/*Saveitaspt_regs->nip&pt_regs->link*/+stdr7,_NIP(r1)+stdr7,_LINK(r1)++/*Savecallee's TOC in the ABI compliant location */+stdr2,24(r1)+ldr2,PACATOC(r13)/*getkernelTOCinr2*/+addisr3,r2,function_trace_op@toc@haaddir3,r3,function_trace_op@toc@lldr5,0(r3)-mflrr3-stdr3,_NIP(r1)-stdr3,16(r1)-subir3,r3,MCOUNT_INSN_SIZE-mfmsrr4-stdr4,_MSR(r1)-mfctrr4-stdr4,_CTR(r1)-mfxerr4-stdr4,_XER(r1)-mrr4,r12+/*Calculateipfromnip-4intor3forcallbelow*/+subir3,r7,MCOUNT_INSN_SIZE++/*Puttheoriginalreturnaddressinr4asparent_ip*/+mrr4,r0++/*Savespecialregs*/+stdr8,_MSR(r1)+stdr9,_CTR(r1)+stdr10,_XER(r1)+stdr11,_CCR(r1)++/*Load&pt_regsinr6forcallbelow*/addir6,r1,STACK_FRAME_OVERHEAD+/*ftrace_call(r3,r4,r5,r6)*/.globlftrace_callftrace_call:blftrace_stubnop+/*LoadctrwiththepossiblymodifiedNIP*/ldr3,_NIP(r1)-mtlrr3+mtctrr3+/*Restoregprs*/REST_8GPRS(0,r1)REST_8GPRS(8,r1)REST_8GPRS(16,r1)REST_8GPRS(24,r1)+/*Restorecallee's TOC */+ldr2,24(r1)++/*Popourstackframe*/addir1,r1,SWITCH_FRAME_SIZE-ldr12,LRSAVE(r1)/*getcaller's address */-mtlrr12-mrr2,r0/*restorecallee's TOC */+/*RestoreoriginalLRforreturntoB*/+ldr0,LRSAVE(r1)+mtlrr0#ifdef CONFIG_FUNCTION_GRAPH_TRACERstdur1,-112(r1)
@@ -1240,9 +1273,6 @@ _GLOBAL(ftrace_graph_stub)addir1,r1,112#endif-mflrr0/*movethisLRtoCTR*/-mtctrr0-ldr0,LRSAVE(r1)/*restorecallee's lr at _mcount site */mtlrr0bctr/*jumpafter_mcountsite*/
The main change is to just use paca->kernel_toc, rather than a branch to
+4 and mflr etc. That makes the code simpler and should also perform
better.
There was also a sequence after ftrace_call() where we load from
pt_regs->nip, move to LR, then a few instructions later load from LRSAVE
and move to LR. Instead I think we want to put pt_regs->nip into CTR and
branch to it later.
We also rework some of the SPR loads to hopefully speed them up a bit.
Also comment the asm much more, to hopefully make it clearer.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/entry_64.S | 94 ++++++++++++++++++++++++++++--------------
1 file changed, 62 insertions(+), 32 deletions(-)
Squash.
@@ -1171,65 +1171,98 @@ _GLOBAL(ftrace_graph_stub)mtlrr0addir1,r1,112#else+/*+*+*ftrace_caller()isthefunctionthatreplaces_mcount()whenftraceis+*active.+*+*WearrivehereafterafunctionAcallsfunctionB,andwearethetrace+*functionforB.Whenweenterr1pointstoA's stack frame, B has not yet+*hadachancetoallocateoneyet.+*+*Additionallyr2maypointeithertotheTOCforA,orB,dependingon+*whetherBdidaTOCsetupsequencebeforecallingus.+*+*OnentrytheLRpointsbacktothe_mcount()callsite,andr0holdsthe+*savedLRasitwasonentrytoB,ie.theoriginalreturnaddressatthe+*callsiteinA.+*+*Ourjobistosavetheregisterstateintoastructpt_regs (onthestack)+*andthenarrangefortheftracefunctiontobecalled.+*/ _GLOBAL(ftrace_caller)+/*SavetheoriginalreturnaddressinA's stack frame */stdr0,LRSAVE(r1)-#if defined(_CALL_ELF) && _CALL_ELF == 2-mflrr0-bl2f-2:mflrr12-mtlrr0-mrr0,r2/*savecallee's TOC */-addisr2,r12,(.TOC.-ftrace_caller-12)@ha-addir2,r2,(.TOC.-ftrace_caller-12)@l-#else-mrr0,r2-#endif-ldr12,LRSAVE(r1)/*getcaller's address */+/*Createourstackframe+pt_regs*/stdur1,-SWITCH_FRAME_SIZE(r1)-stdr12,_LINK(r1)+/*Saveallgprstopt_regs*/SAVE_8GPRS(0,r1)-stdr0,24(r1)/*saveTOC*/SAVE_8GPRS(8,r1)SAVE_8GPRS(16,r1)SAVE_8GPRS(24,r1)+/*Loadspecialregsforsavebelow*/+mfmsrr8+mfctrr9+mfxerr10+mfcrr11++/*Getthe_mcount()callsiteoutofLR*/+mflrr7+/*Saveitaspt_regs->nip&pt_regs->link*/+stdr7,_NIP(r1)+stdr7,_LINK(r1)++/*Savecallee's TOC in the ABI compliant location */+stdr2,24(r1)+ldr2,PACATOC(r13)/*getkernelTOCinr2*/+addisr3,r2,function_trace_op@toc@haaddir3,r3,function_trace_op@toc@lldr5,0(r3)-mflrr3-stdr3,_NIP(r1)-stdr3,16(r1)-subir3,r3,MCOUNT_INSN_SIZE-mfmsrr4-stdr4,_MSR(r1)-mfctrr4-stdr4,_CTR(r1)-mfxerr4-stdr4,_XER(r1)-mrr4,r12+/*Calculateipfromnip-4intor3forcallbelow*/+subir3,r7,MCOUNT_INSN_SIZE++/*Puttheoriginalreturnaddressinr4asparent_ip*/+mrr4,r0++/*Savespecialregs*/+stdr8,_MSR(r1)+stdr9,_CTR(r1)+stdr10,_XER(r1)+stdr11,_CCR(r1)++/*Load&pt_regsinr6forcallbelow*/addir6,r1,STACK_FRAME_OVERHEAD+/*ftrace_call(r3,r4,r5,r6)*/.globlftrace_callftrace_call:blftrace_stubnop+/*LoadctrwiththepossiblymodifiedNIP*/ldr3,_NIP(r1)-mtlrr3+mtctrr3+/*Restoregprs*/REST_8GPRS(0,r1)REST_8GPRS(8,r1)REST_8GPRS(16,r1)REST_8GPRS(24,r1)+/*Restorecallee's TOC */+ldr2,24(r1)++/*Popourstackframe*/addir1,r1,SWITCH_FRAME_SIZE-ldr12,LRSAVE(r1)/*getcaller's address */-mtlrr12-mrr2,r0/*restorecallee's TOC */+/*RestoreoriginalLRforreturntoB*/+ldr0,LRSAVE(r1)+mtlrr0#ifdef CONFIG_FUNCTION_GRAPH_TRACERstdur1,-112(r1)
@@ -1240,9 +1273,6 @@ _GLOBAL(ftrace_graph_stub)addir1,r1,112#endif-mflrr0/*movethisLRtoCTR*/-mtctrr0-ldr0,LRSAVE(r1)/*restorecallee's lr at _mcount site */mtlrr0bctr/*jumpafter_mcountsite*/
On Thu, Feb 25, 2016 at 01:28:31AM +1100, Michael Ellerman wrote:
The main change is to just use paca->kernel_toc, rather than a branch to
+4 and mflr etc. That makes the code simpler and should also perform
better.
Indeed.
There was also a sequence after ftrace_call() where we load from
pt_regs->nip, move to LR, then a few instructions later load from LRSAVE
and move to LR. Instead I think we want to put pt_regs->nip into CTR and
branch to it later.
Yes, I did some of this cleanup in the livepatch implementation.
We also rework some of the SPR loads to hopefully speed them up a bit.
Also comment the asm much more, to hopefully make it clearer.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:29:19
From: Torsten Duwe <redacted>
Using -mprofile-kernel on early boot code not only confuses the
checker but is also useless, as the infrastructure is not yet in
place. Proceed like with -pg (remove it from CFLAGS), equally with
time.o, ftrace and its helper files.
* arch/powerpc/kernel/Makefile,
arch/powerpc/lib/Makefile:
- remove -mprofile-kernel from low level, boot code and
code-patching objects' CFLAGS.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/Makefile | 12 ++++++------
arch/powerpc/lib/Makefile | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
@@ -16,14 +16,14 @@ endififdef CONFIG_FUNCTION_TRACER# Do not trace early boot code-CFLAGS_REMOVE_cputable.o=-pg-mno-sched-epilog-CFLAGS_REMOVE_prom_init.o=-pg-mno-sched-epilog-CFLAGS_REMOVE_btext.o=-pg-mno-sched-epilog-CFLAGS_REMOVE_prom.o=-pg-mno-sched-epilog+CFLAGS_REMOVE_cputable.o=-pg-mno-sched-epilog-mprofile-kernel+CFLAGS_REMOVE_prom_init.o=-pg-mno-sched-epilog-mprofile-kernel+CFLAGS_REMOVE_btext.o=-pg-mno-sched-epilog-mprofile-kernel+CFLAGS_REMOVE_prom.o=-pg-mno-sched-epilog-mprofile-kernel# do not trace tracer code-CFLAGS_REMOVE_ftrace.o=-pg-mno-sched-epilog+CFLAGS_REMOVE_ftrace.o=-pg-mno-sched-epilog-mprofile-kernel# timers used by tracing-CFLAGS_REMOVE_time.o=-pg-mno-sched-epilog+CFLAGS_REMOVE_time.o=-pg-mno-sched-epilog-mprofile-kernelendifobj-y:=cputable.optrace.osyscalls.o\
From: Torsten Duwe <redacted>
Using -mprofile-kernel on early boot code not only confuses the
checker but is also useless, as the infrastructure is not yet in
place. Proceed like with -pg (remove it from CFLAGS), equally with
time.o, ftrace and its helper files.
* arch/powerpc/kernel/Makefile,
arch/powerpc/lib/Makefile:
- remove -mprofile-kernel from low level, boot code and
code-patching objects' CFLAGS.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/Makefile | 12 ++++++------
arch/powerpc/lib/Makefile | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
@@ -16,14 +16,14 @@ endififdef CONFIG_FUNCTION_TRACER# Do not trace early boot code-CFLAGS_REMOVE_cputable.o=-pg-mno-sched-epilog-CFLAGS_REMOVE_prom_init.o=-pg-mno-sched-epilog-CFLAGS_REMOVE_btext.o=-pg-mno-sched-epilog-CFLAGS_REMOVE_prom.o=-pg-mno-sched-epilog+CFLAGS_REMOVE_cputable.o=-pg-mno-sched-epilog-mprofile-kernel+CFLAGS_REMOVE_prom_init.o=-pg-mno-sched-epilog-mprofile-kernel+CFLAGS_REMOVE_btext.o=-pg-mno-sched-epilog-mprofile-kernel+CFLAGS_REMOVE_prom.o=-pg-mno-sched-epilog-mprofile-kernel# do not trace tracer code-CFLAGS_REMOVE_ftrace.o=-pg-mno-sched-epilog+CFLAGS_REMOVE_ftrace.o=-pg-mno-sched-epilog-mprofile-kernel# timers used by tracing-CFLAGS_REMOVE_time.o=-pg-mno-sched-epilog+CFLAGS_REMOVE_time.o=-pg-mno-sched-epilog-mprofile-kernelendifobj-y:=cputable.optrace.osyscalls.o\
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:29:51
__ftrace_make_nop() needs to detect either the two instruction or
the three instruction versions of the _mcount() sequence.
But if we're running a kernel with the two instruction sequence, we need
to be careful not to read from ip - 8, or we'll fault and (possibly)
incorrectly declare the sequence doesn't match.
To keep the code simpler just look at ip - 4, and if it is either of the
expected instructions declare it good. We've already passed a lot of
other checks.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/ftrace.c | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
Squash.
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:30:11
From: Torsten Duwe <redacted>
* arch/powerpc/Makefile:
- globally use -mprofile-kernel in case it's configured,
available and bug-free.
* arch/powerpc/gcc-mprofile-kernel-notrace.sh:
- make sure -mprofile-kernel works and has none of the
known bugs.
* arch/powerpc/kernel/ftrace.c:
- error out on compile with HAVE_DYNAMIC_FTRACE_WITH_REGS
and a buggy compiler.
* arch/powerpc/Kconfig / kernel/trace/Kconfig:
- declare that ppc64le HAVE_MPROFILE_KERNEL and
HAVE_DYNAMIC_FTRACE_WITH_REGS, and use it.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/Kconfig | 2 ++
arch/powerpc/Makefile | 17 +++++++++++++++
arch/powerpc/gcc-mprofile-kernel-notrace.sh | 33 +++++++++++++++++++++++++++++
arch/powerpc/kernel/ftrace.c | 5 +++++
kernel/trace/Kconfig | 5 +++++
5 files changed, 62 insertions(+)
create mode 100755 arch/powerpc/gcc-mprofile-kernel-notrace.sh
FIXME, needs more work. We can't break the build for someone with an
unsupported toolchain.
@@ -0,0 +1,33 @@+#!/bin/sh+# Test whether the compile option -mprofile-kernel+# generates profiling code ( = a call to mcount), and+# whether a function without any global references sets+# the TOC pointer properly at the beginning, and+# whether the "notrace" function attribute successfully+# suppresses the _mcount call.++echo"int func() { return 0; }"|\+$*-S-xc-O2-p-mprofile-kernel--o-2>/dev/null|\+grep-q"mcount"++trace_result=$?++echo"int func() { return 0; }"|\+$*-S-xc-O2-p-mprofile-kernel--o-2>/dev/null|\+sed-n-e'/func:/,/bl _mcount/p'|grep-qTOC++leaf_toc_result=$?++/bin/echo-e"#include <linux/compiler.h>\nnotrace int func() { return 0; }"|\+$*-S-xc-O2-p-mprofile-kernel--o-2>/dev/null|\+grep-q"mcount"++notrace_result=$?++if["$trace_result"-eq"0"-a\+"$leaf_toc_result"-eq"0"-a\+"$notrace_result"-eq"1"];then+echoy+else+echon+fi
From: Torsten Duwe <redacted>
* arch/powerpc/Makefile:
- globally use -mprofile-kernel in case it's configured,
available and bug-free.
* arch/powerpc/gcc-mprofile-kernel-notrace.sh:
- make sure -mprofile-kernel works and has none of the
known bugs.
* arch/powerpc/kernel/ftrace.c:
- error out on compile with HAVE_DYNAMIC_FTRACE_WITH_REGS
and a buggy compiler.
* arch/powerpc/Kconfig / kernel/trace/Kconfig:
- declare that ppc64le HAVE_MPROFILE_KERNEL and
HAVE_DYNAMIC_FTRACE_WITH_REGS, and use it.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/Kconfig | 2 ++
arch/powerpc/Makefile | 17 +++++++++++++++
arch/powerpc/gcc-mprofile-kernel-notrace.sh | 33 +++++++++++++++++++++++++++++
arch/powerpc/kernel/ftrace.c | 5 +++++
kernel/trace/Kconfig | 5 +++++
5 files changed, 62 insertions(+)
create mode 100755 arch/powerpc/gcc-mprofile-kernel-notrace.sh
FIXME, needs more work. We can't break the build for someone with an
unsupported toolchain.
@@ -0,0 +1,33 @@+#!/bin/sh+# Test whether the compile option -mprofile-kernel+# generates profiling code ( = a call to mcount), and+# whether a function without any global references sets+# the TOC pointer properly at the beginning, and+# whether the "notrace" function attribute successfully+# suppresses the _mcount call.++echo"int func() { return 0; }"|\+$*-S-xc-O2-p-mprofile-kernel--o-2>/dev/null|\+grep-q"mcount"++trace_result=$?++echo"int func() { return 0; }"|\+$*-S-xc-O2-p-mprofile-kernel--o-2>/dev/null|\+sed-n-e'/func:/,/bl _mcount/p'|grep-qTOC++leaf_toc_result=$?+
We should remove this bit, we don't need a TOC for leaf procedures anymore
quoted hunk
+/bin/echo -e "#include <linux/compiler.h>\nnotrace int func() { return 0; }" | \
+ $* -S -x c -O2 -p -mprofile-kernel - -o - 2> /dev/null | \
+ grep -q "mcount"
+
+notrace_result=$?
+
+if [ "$trace_result" -eq "0" -a \
+ "$leaf_toc_result" -eq "0" -a \
+ "$notrace_result" -eq "1" ]; then
+ echo y
+else
+ echo n
+fi
@@ -0,0 +1,33 @@+#!/bin/sh+# Test whether the compile option -mprofile-kernel+# generates profiling code ( = a call to mcount), and+# whether a function without any global references sets+# the TOC pointer properly at the beginning, and
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:30:39
From: Torsten Duwe <redacted>
Convert powerpc's arch_ftrace_update_code() from its own version to use
the generic default functionality (without stop_machine -- our
instructions are properly aligned and the replacements atomic).
With this we gain error checking and the much-needed function_trace_op
handling.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/ftrace.c | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)
From: Torsten Duwe <redacted>
Convert powerpc's arch_ftrace_update_code() from its own version to use
the generic default functionality (without stop_machine -- our
instructions are properly aligned and the replacements atomic).
With this we gain error checking and the much-needed function_trace_op
handling.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/ftrace.c | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:30:42
From: Torsten Duwe <redacted>
Implement FTRACE_WITH_REGS for powerpc64, on ELF ABI v2.
Initial work started by Vojtech Pavlik, used with permission.
* arch/powerpc/kernel/entry_64.S:
- Implement an effective ftrace_caller that works from
within the kernel binary as well as from modules.
* arch/powerpc/kernel/ftrace.c:
- be prepared to deal with ppc64 ELF ABI v2, especially
calls to _mcount that result from gcc -mprofile-kernel
- a little more error verbosity
* arch/powerpc/kernel/module_64.c:
- do not save the TOC pointer on the trampoline when the
destination is ftrace_caller. This trampoline jump happens from
a function prologue before a new stack frame is set up, so bad
things may happen otherwise...
- relax is_module_trampoline() to recognise the modified
trampoline.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/include/asm/ftrace.h | 5 +++
arch/powerpc/kernel/entry_64.S | 78 +++++++++++++++++++++++++++++++++++++++
arch/powerpc/kernel/ftrace.c | 66 ++++++++++++++++++++++++++++++---
arch/powerpc/kernel/module_64.c | 16 ++++++++
4 files changed, 159 insertions(+), 6 deletions(-)
Probably squash.
@@ -46,6 +46,8 @@externvoid_mcount(void);#ifdef CONFIG_DYNAMIC_FTRACE+# define FTRACE_ADDR ((unsigned long)ftrace_caller)+# define FTRACE_REGS_ADDR FTRACE_ADDRstaticinlineunsignedlongftrace_call_adjust(unsignedlongaddr){/* reloction of mcount call site is the same as the address */
@@ -61,8 +61,11 @@ ftrace_modify_code(unsigned long ip, unsigned int old, unsigned int new)return-EFAULT;/* Make sure it is what we expect it to be */-if(replaced!=old)+if(replaced!=old){+pr_err("%p: replaced (%#x) != old (%#x)",+(void*)ip,replaced,old);return-EINVAL;+}/* replace the text with the new text */if(patch_instruction((unsignedint*)ip,new))
@@ -108,11 +111,13 @@ __ftrace_make_nop(struct module *mod,{unsignedlongentry,ptr,tramp;unsignedlongip=rec->ip;-unsignedintop;+unsignedintop,pop;/* read where this goes */-if(probe_kernel_read(&op,(void*)ip,sizeof(int)))+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)){
@@ -152,10 +157,51 @@ __ftrace_make_nop(struct module *mod,**Useab+8tojumpovertheload.*/-op=0x48000008;/* b +8 */-if(patch_instruction((unsignedint*)ip,op))+pop=PPC_INST_BRANCH|8;/* b +8 */++/*+*Checkwhatisinthenextinstruction.Wecanseeldr2,40(r1),but+*onfirstpassafterbootwewillseemflrr0.+*/+if(probe_kernel_read(&op,(void*)(ip+4),MCOUNT_INSN_SIZE)){+pr_err("Fetching op failed.\n");+return-EFAULT;+}++if(op!=PPC_INST_LD_TOC)+{+unsignedintop0,op1;++if(probe_kernel_read(&op0,(void*)(ip-8),MCOUNT_INSN_SIZE)){+pr_err("Fetching op0 failed.\n");+return-EFAULT;+}++if(probe_kernel_read(&op1,(void*)(ip-4),MCOUNT_INSN_SIZE)){+pr_err("Fetching op1 failed.\n");+return-EFAULT;+}++/* mflr r0 ; [ std r0,LRSAVE(r1) ]? */+if((op0!=PPC_INST_MFLR||+op1!=PPC_INST_STD_LR)+&&op1!=PPC_INST_MFLR)+{+pr_err("Unexpected instructions around bl _mcount\n"+"when enabling dynamic ftrace!\t"+"(%08x,%08x,bl,%08x)\n",op0,op1,op);+return-EINVAL;+}++/* When using -mkernel_profile there is no load to jump over */+pop=PPC_INST_NOP;+}++if(patch_instruction((unsignedint*)ip,pop)){+pr_err("Patching NOP failed.\n");return-EPERM;+}return0;}
@@ -281,6 +327,14 @@ int ftrace_make_nop(struct module *mod,#ifdef CONFIG_MODULES#ifdef CONFIG_PPC64+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS+intftrace_modify_call(structdyn_ftrace*rec,unsignedlongold_addr,+unsignedlongaddr)+{+returnftrace_make_call(rec,addr);+}+#endif+/* Examine the existing instructions for __ftrace_make_call.*TheyshouldeffectivelybeaNOP,andfollowformalconstraints,*dependingontheABI.Returnfalseiftheydon't.
@@ -139,6 +139,19 @@ static u32 ppc64_stub_insns[] = {0x4e800420/* bctr */};+#ifdef CC_USING_MPROFILE_KERNEL+/* In case of _mcount calls or dynamic ftracing, Do not save the+*currentcallee'sTOC(inR2)againintotheoriginalcaller'sstack+*frameduringthistrampolinehop.Thestackframealreadyholds+*thatoftheoriginalcaller._mcountandftrace_callerwilltake+*careofthisTOCvaluethemselves.+*/+#define SQUASH_TOC_SAVE_INSN(trampoline_addr) \+(((structppc64_stub_entry*)(trampoline_addr))->jump[2]=PPC_INST_NOP)+#else+#define SQUASH_TOC_SAVE_INSN(trampoline_addr)+#endif+#ifdef CONFIG_DYNAMIC_FTRACEintmodule_trampoline_target(structmodule*mod,unsignedlongaddr,unsignedlong*target)
@@ -608,6 +621,9 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,return-ENOENT;if(!restore_r2((u32*)location+1,me))return-ENOEXEC;+/* Squash the TOC saver for profiler calls */+if(!strcmp("_mcount",strtab+sym->st_name))+SQUASH_TOC_SAVE_INSN(value);}elsevalue+=local_entry_offset(sym);
From: Torsten Duwe <redacted>
Implement FTRACE_WITH_REGS for powerpc64, on ELF ABI v2.
Initial work started by Vojtech Pavlik, used with permission.
* arch/powerpc/kernel/entry_64.S:
- Implement an effective ftrace_caller that works from
within the kernel binary as well as from modules.
* arch/powerpc/kernel/ftrace.c:
- be prepared to deal with ppc64 ELF ABI v2, especially
calls to _mcount that result from gcc -mprofile-kernel
- a little more error verbosity
* arch/powerpc/kernel/module_64.c:
- do not save the TOC pointer on the trampoline when the
destination is ftrace_caller. This trampoline jump happens from
a function prologue before a new stack frame is set up, so bad
things may happen otherwise...
- relax is_module_trampoline() to recognise the modified
trampoline.
Signed-off-by: Torsten Duwe <redacted>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/include/asm/ftrace.h | 5 +++
arch/powerpc/kernel/entry_64.S | 78 +++++++++++++++++++++++++++++++++++++++
arch/powerpc/kernel/ftrace.c | 66 ++++++++++++++++++++++++++++++---
arch/powerpc/kernel/module_64.c | 16 ++++++++
4 files changed, 159 insertions(+), 6 deletions(-)
Probably squash.
@@ -46,6 +46,8 @@externvoid_mcount(void);#ifdef CONFIG_DYNAMIC_FTRACE+# define FTRACE_ADDR ((unsigned long)ftrace_caller)+# define FTRACE_REGS_ADDR FTRACE_ADDRstaticinlineunsignedlongftrace_call_adjust(unsignedlongaddr){/* reloction of mcount call site is the same as the address */
@@ -61,8 +61,11 @@ ftrace_modify_code(unsigned long ip, unsigned int old, unsigned int new)return-EFAULT;/* Make sure it is what we expect it to be */-if(replaced!=old)+if(replaced!=old){+pr_err("%p: replaced (%#x) != old (%#x)",+(void*)ip,replaced,old);return-EINVAL;+}/* replace the text with the new text */if(patch_instruction((unsignedint*)ip,new))
@@ -108,11 +111,13 @@ __ftrace_make_nop(struct module *mod,{unsignedlongentry,ptr,tramp;unsignedlongip=rec->ip;-unsignedintop;+unsignedintop,pop;/* read where this goes */-if(probe_kernel_read(&op,(void*)ip,sizeof(int)))+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)){
@@ -152,10 +157,51 @@ __ftrace_make_nop(struct module *mod,**Useab+8tojumpovertheload.*/-op=0x48000008;/* b +8 */-if(patch_instruction((unsignedint*)ip,op))+pop=PPC_INST_BRANCH|8;/* b +8 */+
Do we really need the bits below for safety? I would put then under DEBUG or DEBUG_FTRACE
+ /*
+ * Check what is in the next instruction. We can see ld r2,40(r1), but
+ * on first pass after boot we will see mflr r0.
+ */
+ if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE)) {
+ pr_err("Fetching op failed.\n");
+ return -EFAULT;
+ }
+
+ if (op != PPC_INST_LD_TOC)
+ {
+ unsigned int op0, op1;
+
+ if (probe_kernel_read(&op0, (void *)(ip-8), MCOUNT_INSN_SIZE)) {
+ pr_err("Fetching op0 failed.\n");
+ return -EFAULT;
+ }
+
+ if (probe_kernel_read(&op1, (void *)(ip-4), MCOUNT_INSN_SIZE)) {
+ pr_err("Fetching op1 failed.\n");
+ return -EFAULT;
+ }
+
+ /* mflr r0 ; [ std r0,LRSAVE(r1) ]? */
+ if ( (op0 != PPC_INST_MFLR ||
+ op1 != PPC_INST_STD_LR)
+ && op1 != PPC_INST_MFLR )
+ {
+ pr_err("Unexpected instructions around bl _mcount\n"
+ "when enabling dynamic ftrace!\t"
+ "(%08x,%08x,bl,%08x)\n", op0, op1, op);
+ return -EINVAL;
+ }
+
+ /* When using -mkernel_profile there is no load to jump over */
+ pop = PPC_INST_NOP;
+ }
+
The bits till here
quoted hunk
+ if (patch_instruction((unsigned int *)ip, pop)) {
+ pr_err("Patching NOP failed.\n");
return -EPERM;
+ }
return 0;
}
@@ -281,6 +327,14 @@ int ftrace_make_nop(struct module *mod, #ifdef CONFIG_MODULES #ifdef CONFIG_PPC64+#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+ /* Examine the existing instructions for __ftrace_make_call. * They should effectively be a NOP, and follow formal constraints, * depending on the ABI. Return false if they don't.
@@ -348,7 +402,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) return 0; }-#else+#else /* !CONFIG_PPC64: */ static int __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) {
@@ -139,6 +139,19 @@ static u32 ppc64_stub_insns[] = {0x4e800420/* bctr */};+#ifdef CC_USING_MPROFILE_KERNEL+/* In case of _mcount calls or dynamic ftracing, Do not save the+*currentcallee'sTOC(inR2)againintotheoriginalcaller'sstack+*frameduringthistrampolinehop.Thestackframealreadyholds+*thatoftheoriginalcaller._mcountandftrace_callerwilltake+*careofthisTOCvaluethemselves.+*/+#define SQUASH_TOC_SAVE_INSN(trampoline_addr) \+(((structppc64_stub_entry*)(trampoline_addr))->jump[2]=PPC_INST_NOP)+#else+#define SQUASH_TOC_SAVE_INSN(trampoline_addr)+#endif+#ifdef CONFIG_DYNAMIC_FTRACEintmodule_trampoline_target(structmodule*mod,unsignedlongaddr,unsignedlong*target)
@@ -608,6 +621,9 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,return-ENOENT;if(!restore_r2((u32*)location+1,me))return-ENOEXEC;+/* Squash the TOC saver for profiler calls */+if(!strcmp("_mcount",strtab+sym->st_name))+SQUASH_TOC_SAVE_INSN(value);
On Thu, Feb 25, 2016 at 11:48:59AM +1100, Balbir Singh wrote:
quoted
@@ -608,6 +621,9 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, return -ENOENT; if (!restore_r2((u32 *)location + 1, me)) return -ENOEXEC;+ /* Squash the TOC saver for profiler calls */+ if (!strcmp("_mcount", strtab+sym->st_name))+ SQUASH_TOC_SAVE_INSN(value);
I don't think we need this anymore, do we?
I'm not sure. Once a module is loaded, are all the "bl _mcount"s NOPed out
before any of its functions are run? If not, the _mcount trampoline will
be used, and it must not save R2!
Torsten
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-26 10:14:14
On Thu, 2016-02-25 at 16:11 +0100, Torsten Duwe wrote:
On Thu, Feb 25, 2016 at 11:48:59AM +1100, Balbir Singh wrote:
quoted
quoted
@@ -608,6 +621,9 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, return -ENOENT; if (!restore_r2((u32 *)location + 1, me)) return -ENOEXEC;+ /* Squash the TOC saver for profiler calls */+ if (!strcmp("_mcount", strtab+sym->st_name))+ SQUASH_TOC_SAVE_INSN(value);
I don't think we need this anymore, do we?
I'm not sure. Once a module is loaded, are all the "bl _mcount"s NOPed out
before any of its functions are run? If not, the _mcount trampoline will
be used, and it must not save R2!
With dynamic ftrace, yes they are all nop'ed out before the module runs. See
ftrace_module_init() called from load_module().
But with static ftrace they are just left as-is.
As this series is currently written you can't enable mprofile-kernel with
static ftrace. But that's a bit fragile, someone could easily send a patch to
enable it for static ftrace and we'd probably merge it without thinking about
this code. So I'll leave this as is for now, and we will clean it up once the
series is in.
cheers
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:31:28
is_early_mcount_callsite() needs to detect either the two instruction or
the three instruction versions of the _mcount() sequence.
But if we're running a kernel with the two instruction sequence, we need
to be careful not to read instruction - 2, otherwise we might fall off
the front of a page and cause an oops.
While we're here convert to bool to make the return semantics clear.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/module_64.c | 30 ++++++++++++++----------------
1 file changed, 14 insertions(+), 16 deletions(-)
Squash.
@@ -449,27 +449,25 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,}#ifdef CC_USING_MPROFILE_KERNEL-staticintis_early_mcount_callsite(u32*instruction)+staticboolis_early_mcount_callsite(u32*instruction){-/* -mprofile-kernel sequence starting with-*mflrr0andmaybestdr0,LRSAVE(r1).+/*+*Checkifthisisoneofthe-mprofile-kernelsequences.*/-if((instruction[-3]==PPC_INST_MFLR&&-instruction[-2]==PPC_INST_STD_LR)||-instruction[-2]==PPC_INST_MFLR){-/* Nothing to be done here, it's an _mcount-*calllocationandr2willhavetobe-*restoredinthe_mcountfunction.-*/-return1;-}-return0;+if(instruction[-1]==PPC_INST_STD_LR&&+instruction[-2]==PPC_INST_MFLR)+returntrue;++if(instruction[-1]==PPC_INST_MFLR)+returntrue;++returnfalse;}#else/* without -mprofile-kernel, mcount calls are never early */-staticintis_early_mcount_callsite(u32*instruction)+staticboolis_early_mcount_callsite(u32*instruction){-return0;+returnfalse;}#endif
@@ -478,7 +476,7 @@ static int is_early_mcount_callsite(u32 *instruction)staticintrestore_r2(u32*instruction,structmodule*me){if(*instruction!=PPC_INST_NOP){-if(is_early_mcount_callsite(instruction))+if(is_early_mcount_callsite(instruction-1))return1;pr_err("%s: Expect noop after relocate, got %08x\n",me->name,*instruction);
is_early_mcount_callsite() needs to detect either the two instruction or
the three instruction versions of the _mcount() sequence.
But if we're running a kernel with the two instruction sequence, we need
to be careful not to read instruction - 2, otherwise we might fall off
the front of a page and cause an oops.
While we're here convert to bool to make the return semantics clear.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/module_64.c | 30 ++++++++++++++----------------
1 file changed, 14 insertions(+), 16 deletions(-)
Squash.
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-25 10:28:37
On Thu, 2016-02-25 at 10:39 +1100, Balbir Singh wrote:
On 25/02/16 01:28, Michael Ellerman wrote:
quoted
is_early_mcount_callsite() needs to detect either the two instruction or
the three instruction versions of the _mcount() sequence.
But if we're running a kernel with the two instruction sequence, we need
to be careful not to read instruction - 2, otherwise we might fall off
the front of a page and cause an oops.
While we're here convert to bool to make the return semantics clear.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Do we even need to do this anymore?
Yes. Otherwise the code in apply_relocate_add() will see a far call with no nop
slot after it to do the toc restore, and it considers that a bug (which it
usually is, except mcount is special).
As we discussed today I'm hoping we can clean this code up a bit more in the
medium term, but this works for now.
cheers
On Thu, Feb 25, 2016 at 09:28:32PM +1100, Michael Ellerman wrote:
On Thu, 2016-02-25 at 10:39 +1100, Balbir Singh wrote:
quoted
On 25/02/16 01:28, Michael Ellerman wrote:
quoted
is_early_mcount_callsite() needs to detect either the two instruction or
the three instruction versions of the _mcount() sequence.
But if we're running a kernel with the two instruction sequence, we need
to be careful not to read instruction - 2, otherwise we might fall off
the front of a page and cause an oops.
While we're here convert to bool to make the return semantics clear.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
I wouldn't mind if you had folded this into the previous patch, see comments there.
quoted
quoted
Do we even need to do this anymore?
Yes. Otherwise the code in apply_relocate_add() will see a far call with no nop
slot after it to do the toc restore, and it considers that a bug (which it
usually is, except mcount is special).
As we discussed today I'm hoping we can clean this code up a bit more in the
medium term, but this works for now.
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:31:47
In order to support the new -mprofile-kernel ABI, we need to be able to
call from the module back to ftrace_caller() (in the kernel) without
using the module's r2. That is because the function in this module which
is calling ftrace_caller() may not have setup r2, if it doesn't
otherwise need it (ie. it accesses no globals).
To make that work we add a new stub which is used for calling
ftrace_caller(), which uses the kernel toc instead of the module toc.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/module_64.c | 48 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)
@@ -671,10 +671,56 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,}#ifdef CONFIG_DYNAMIC_FTRACE++#define PACATOC offsetof(struct paca_struct, kernel_toc)++staticunsignedlongcreate_ftrace_stub(constElf64_Shdr*sechdrs,structmodule*me)+{+structppc64_stub_entry*entry;+unsignedinti,num_stubs;+staticu32stub_insns[]={+0xe98d0000|PACATOC,/* ld r12,PACATOC(r13) */+0x3d8c0000,/* addis r12,r12,<high> */+0x398c0000,/* addi r12,r12,<low> */+0x7d8903a6,/* mtctr r12 */+0x4e800420,/* bctr */+};+longreladdr;++num_stubs=sechdrs[me->arch.stubs_section].sh_size/sizeof(*entry);++/* Find the next available stub entry */+entry=(void*)sechdrs[me->arch.stubs_section].sh_addr;+for(i=0;i<num_stubs&&stub_func_addr(entry->funcdata);i++,entry++);++if(i>=num_stubs){+pr_err("%s: Unable to find a free slot for ftrace stub.\n",me->name);+return0;+}++memcpy(entry->jump,stub_insns,sizeof(stub_insns));++/* Stub uses address relative to kernel_toc */+reladdr=(unsignedlong)ftrace_caller-get_paca()->kernel_toc;+if(reladdr>0x7FFFFFFF||reladdr<-(0x80000000L)){+pr_err("%s: Address of ftrace_caller out of range of kernel_toc.\n",me->name);+return0;+}++entry->jump[1]|=PPC_HA(reladdr);+entry->jump[2]|=PPC_LO(reladdr);++/* Eventhough we don't use funcdata in the stub, it's needed elsewhere. */+entry->funcdata=func_desc((unsignedlong)ftrace_caller);+entry->magic=STUB_MAGIC;++return(unsignedlong)entry;+}+intmodule_finalize_ftrace(structmodule*mod,constElf_Shdr*sechdrs){mod->arch.toc=my_r2(sechdrs,mod);-mod->arch.tramp=stub_for_addr(sechdrs,(unsignedlong)ftrace_caller,mod);+mod->arch.tramp=create_ftrace_stub(sechdrs,mod);if(!mod->arch.tramp)return-ENOENT;
In order to support the new -mprofile-kernel ABI, we need to be able to
call from the module back to ftrace_caller() (in the kernel) without
using the module's r2. That is because the function in this module which
is calling ftrace_caller() may not have setup r2, if it doesn't
otherwise need it (ie. it accesses no globals).
To make that work we add a new stub which is used for calling
ftrace_caller(), which uses the kernel toc instead of the module toc.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/kernel/module_64.c | 48 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)
How about some comments on r2
r2 is still pointing to the module's toc, will be saved by ftrace_caller and restored by the instruction following bl ftrace_caller (after patching _mcount/nop)
+ static u32 stub_insns[] = {
+ 0xe98d0000 | PACATOC, /* ld r12,PACATOC(r13) */
+ 0x3d8c0000, /* addis r12,r12,<high> */
+ 0x398c0000, /* addi r12,r12,<low> */
+ 0x7d8903a6, /* mtctr r12 */
+ 0x4e800420, /* bctr */
+ };
+ long reladdr;
+
+ num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*entry);
+
+ /* Find the next available stub entry */
+ entry = (void *)sechdrs[me->arch.stubs_section].sh_addr;
+ for (i = 0; i < num_stubs && stub_func_addr(entry->funcdata); i++, entry++);
+
+ if (i >= num_stubs) {
+ pr_err("%s: Unable to find a free slot for ftrace stub.\n", me->name);
+ return 0;
+ }
+
+ memcpy(entry->jump, stub_insns, sizeof(stub_insns));
+
+ /* Stub uses address relative to kernel_toc */
+ reladdr = (unsigned long)ftrace_caller - get_paca()->kernel_toc;
+ if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
+ pr_err("%s: Address of ftrace_caller out of range of kernel_toc.\n", me->name);
+ return 0;
+ }
+
+ entry->jump[1] |= PPC_HA(reladdr);
+ entry->jump[2] |= PPC_LO(reladdr);
+
+ /* Eventhough we don't use funcdata in the stub, it's needed elsewhere. */
+ entry->funcdata = func_desc((unsigned long)ftrace_caller);
+ entry->magic = STUB_MAGIC;
+
+ return (unsigned long)entry;
+}
+
int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sechdrs)
{
mod->arch.toc = my_r2(sechdrs, mod);
- mod->arch.tramp = stub_for_addr(sechdrs, (unsigned long)ftrace_caller, mod);
+ mod->arch.tramp = create_ftrace_stub(sechdrs, mod);
if (!mod->arch.tramp)
return -ENOENT;
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-25 10:48:22
On Thu, 2016-02-25 at 11:08 +1100, Balbir Singh wrote:
On 25/02/16 01:28, Michael Ellerman wrote:
quoted
In order to support the new -mprofile-kernel ABI, we need to be able to
call from the module back to ftrace_caller() (in the kernel) without
using the module's r2. That is because the function in this module which
is calling ftrace_caller() may not have setup r2, if it doesn't
otherwise need it (ie. it accesses no globals).
To make that work we add a new stub which is used for calling
ftrace_caller(), which uses the kernel toc instead of the module toc.
How about some comments on r2
r2 is still pointing to the module's toc, will be saved by ftrace_caller and
restored by the instruction following bl ftrace_caller (after patching
_mcount/nop)
Yeah I'll add some commentary.
I think the change log describes it fairly well but a comment is also good.
cheers
On Thu, Feb 25, 2016 at 11:08:54AM +1100, Balbir Singh wrote:
How about some comments on r2
r2 is still pointing to the module's toc, will be saved by ftrace_caller and restored by the instruction following bl ftrace_caller (after patching _mcount/nop)
To be precise: ftrace_caller needs to save _and_ restore r2 in case of -mprofile-kernel.
kernel_toc is a compile time constant; do you really want to look it up in
memory at runtime each time? It's a bit tricky to get the +- 0x8000 right
OTOH...
I wrote:
extern unsigned long __toc_start;
reladdr = addr - ((unsigned long)(&__toc_start) + 0x8000UL);
looks a bit odd, but evaluates to a constant for ftrace_caller.
Either way is fine with me:
Signed-off-by: Torsten Duwe <redacted>
Reviewed-by: Torsten Duwe <redacted>
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-26 10:35:54
On Thu, 2016-02-25 at 14:31 +0100, Torsten Duwe wrote:
On Thu, Feb 25, 2016 at 11:08:54AM +1100, Balbir Singh wrote:
quoted
How about some comments on r2
r2 is still pointing to the module's toc, will be saved by ftrace_caller and restored by the instruction following bl ftrace_caller (after patching _mcount/nop)
To be precise: ftrace_caller needs to save _and_ restore r2 in case of -mprofile-kernel.
Yeah true. I originally wrote it with the address of ftrace_caller passed as an
argument, so it had to be computed at runtime, and getting it from the paca is
~= to getting it out of the GOT.
kernel_toc is a compile time constant; do you really want to look it up in
memory at runtime each time? It's a bit tricky to get the +- 0x8000 right
OTOH...
I wrote:
extern unsigned long __toc_start;
reladdr = addr - ((unsigned long)(&__toc_start) + 0x8000UL);
looks a bit odd, but evaluates to a constant for ftrace_caller.
Yeah that makes sense. I'll add a helper to do the + 32k.
cheers
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-24 14:32:02
When a module is loaded, calls out to the kernel go via a stub which is
generated at runtime. One of these stubs is used to call _mcount(),
which is the default target of tracing calls generated by the compiler
with -pg.
If dynamic ftrace is enabled (which it typicall is), another stub is
used to call ftrace_caller(), which is the target of tracing calls when
ftrace is actually active.
ftrace then wants to disable the calls to _mcount() at module startup,
and enable/disable the calls to ftrace_caller() when enabling/disablig
tracing - all of these it does by patching the code.
As part of that code patching, the ftrace code wants to confirm that the
branch it is about to modify, is in fact a call to a module stub which
calls _mcount() or ftrace_caller().
Currently it does that by inspecting the instructions and confirming
they are what it expects. Although that works, the code to do it is
pretty intricate because it requires lots of knowledge about the exact
format of the stub.
We can make that process easier by marking the generated stubs with a
magic value, and then looking for that magic value. Altough this is not
as rigorous as the current method, I believe it is sufficient in
practice.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/include/asm/module.h | 3 +-
arch/powerpc/kernel/ftrace.c | 14 ++-----
arch/powerpc/kernel/module_64.c | 78 +++++++++++++--------------------------
3 files changed, 31 insertions(+), 64 deletions(-)
@@ -106,10 +106,9 @@ static int__ftrace_make_nop(structmodule*mod,structdyn_ftrace*rec,unsignedlongaddr){-unsignedintop;-unsignedlongentry,ptr;+unsignedlongentry,ptr,tramp;unsignedlongip=rec->ip;-void*tramp;+unsignedintop;/* read where this goes */if(probe_kernel_read(&op,(void*)ip,sizeof(int)))
@@ -122,14 +121,9 @@ __ftrace_make_nop(struct module *mod,}/* lets find where the pointer goes */-tramp=(void*)find_bl_target(ip,op);--pr_devel("ip:%lx jumps to %p",ip,tramp);+tramp=find_bl_target(ip,op);-if(!is_module_trampoline(tramp)){-pr_err("Not a trampoline\n");-return-EINVAL;-}+pr_devel("ip:%lx jumps to %lx",ip,tramp);if(module_trampoline_target(mod,tramp,&ptr)){pr_err("Failed to get trampoline target\n");
@@ -96,6 +96,8 @@ static unsigned int local_entry_offset(const Elf64_Sym *sym)}#endif+#define STUB_MAGIC 0x73747562 /* stub */+/* Like PPC32, we need little trampolines to do > 24-bit jumps (intothekernelitself).ButonPPC64,theseneedtobeusedforeveryjump,actually,toresetr2(TOC+0x8000).*/
@@ -105,7 +107,8 @@ struct ppc64_stub_entry*need6instructionsonABIv2butwealwaysallocate7so*sowedon'thavetomodifythetrampolineloadinstruction.*/u32jump[7];-u32unused;+/* Used by ftrace to identify stubs */+u32magic;/* Data for the above code */func_desc_tfuncdata;};
@@ -139,70 +142,39 @@ static u32 ppc64_stub_insns[] = {};#ifdef CONFIG_DYNAMIC_FTRACE--staticu32ppc64_stub_mask[]={-0xffff0000,-0xffff0000,-0xffffffff,-0xffffffff,-#if !defined(_CALL_ELF) || _CALL_ELF != 2-0xffffffff,-#endif-0xffffffff,-0xffffffff-};--boolis_module_trampoline(u32*p)+intmodule_trampoline_target(structmodule*mod,unsignedlongaddr,+unsignedlong*target){-unsignedinti;-u32insns[ARRAY_SIZE(ppc64_stub_insns)];--BUILD_BUG_ON(sizeof(ppc64_stub_insns)!=sizeof(ppc64_stub_mask));+structppc64_stub_entry*stub;+func_desc_tfuncdata;+u32magic;-if(probe_kernel_read(insns,p,sizeof(insns)))+if(!within_module_core(addr,mod)){+pr_err("%s: stub %lx not in module %s\n",__func__,addr,mod->name);return-EFAULT;--for(i=0;i<ARRAY_SIZE(ppc64_stub_insns);i++){-u32insna=insns[i];-u32insnb=ppc64_stub_insns[i];-u32mask=ppc64_stub_mask[i];--if((insna&mask)!=(insnb&mask))-returnfalse;}-returntrue;-}+stub=(structppc64_stub_entry*)addr;-intmodule_trampoline_target(structmodule*mod,u32*trampoline,-unsignedlong*target)-{-u32buf[2];-u16upper,lower;-longoffset;-void*toc_entry;--if(probe_kernel_read(buf,trampoline,sizeof(buf)))+if(probe_kernel_read(&magic,&stub->magic,sizeof(magic))){+pr_err("%s: fault reading magic for stub %lx for %s\n",__func__,addr,mod->name);return-EFAULT;+}-upper=buf[0]&0xffff;-lower=buf[1]&0xffff;--/* perform the addis/addi, both signed */-offset=((short)upper<<16)+(short)lower;+if(magic!=STUB_MAGIC){+pr_err("%s: bad magic for stub %lx for %s\n",__func__,addr,mod->name);+return-EFAULT;+}-/*-*Nowgettheaddressthistrampolinejumpsto.This-*isalways32bytesintoourtrampolinestub.-*/-toc_entry=(void*)mod->arch.toc+offset+32;+if(probe_kernel_read(&funcdata,&stub->funcdata,sizeof(funcdata))){+pr_err("%s: fault reading funcdata for stub %lx for %s\n",__func__,addr,mod->name);+return-EFAULT;+}-if(probe_kernel_read(target,toc_entry,sizeof(*target)))-return-EFAULT;+*target=stub_func_addr(funcdata);return0;}-#endif/* Count how many different 24-bit relocations (different symbol,
@@ -447,6 +419,8 @@ static inline int create_stub(const Elf64_Shdr *sechdrs,entry->jump[0]|=PPC_HA(reladdr);entry->jump[1]|=PPC_LO(reladdr);entry->funcdata=func_desc(addr);+entry->magic=STUB_MAGIC;+return1;}
When a module is loaded, calls out to the kernel go via a stub which is
generated at runtime. One of these stubs is used to call _mcount(),
which is the default target of tracing calls generated by the compiler
with -pg.
If dynamic ftrace is enabled (which it typicall is), another stub is
used to call ftrace_caller(), which is the target of tracing calls when
ftrace is actually active.
ftrace then wants to disable the calls to _mcount() at module startup,
and enable/disable the calls to ftrace_caller() when enabling/disablig
tracing - all of these it does by patching the code.
As part of that code patching, the ftrace code wants to confirm that the
branch it is about to modify, is in fact a call to a module stub which
calls _mcount() or ftrace_caller().
Currently it does that by inspecting the instructions and confirming
they are what it expects. Although that works, the code to do it is
pretty intricate because it requires lots of knowledge about the exact
format of the stub.
We can make that process easier by marking the generated stubs with a
magic value, and then looking for that magic value. Altough this is not
as rigorous as the current method, I believe it is sufficient in
practice.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
---
arch/powerpc/include/asm/module.h | 3 +-
arch/powerpc/kernel/ftrace.c | 14 ++-----
arch/powerpc/kernel/module_64.c | 78 +++++++++++++--------------------------
3 files changed, 31 insertions(+), 64 deletions(-)
@@ -106,10 +106,9 @@ static int__ftrace_make_nop(structmodule*mod,structdyn_ftrace*rec,unsignedlongaddr){-unsignedintop;-unsignedlongentry,ptr;+unsignedlongentry,ptr,tramp;unsignedlongip=rec->ip;-void*tramp;+unsignedintop;/* read where this goes */if(probe_kernel_read(&op,(void*)ip,sizeof(int)))
@@ -122,14 +121,9 @@ __ftrace_make_nop(struct module *mod,}/* lets find where the pointer goes */-tramp=(void*)find_bl_target(ip,op);--pr_devel("ip:%lx jumps to %p",ip,tramp);+tramp=find_bl_target(ip,op);-if(!is_module_trampoline(tramp)){-pr_err("Not a trampoline\n");-return-EINVAL;-}+pr_devel("ip:%lx jumps to %lx",ip,tramp);if(module_trampoline_target(mod,tramp,&ptr)){pr_err("Failed to get trampoline target\n");
@@ -96,6 +96,8 @@ static unsigned int local_entry_offset(const Elf64_Sym *sym)}#endif+#define STUB_MAGIC 0x73747562 /* stub */+/* Like PPC32, we need little trampolines to do > 24-bit jumps (intothekernelitself).ButonPPC64,theseneedtobeusedforeveryjump,actually,toresetr2(TOC+0x8000).*/
@@ -105,7 +107,8 @@ struct ppc64_stub_entry*need6instructionsonABIv2butwealwaysallocate7so*sowedon'thavetomodifythetrampolineloadinstruction.*/u32jump[7];-u32unused;+/* Used by ftrace to identify stubs */+u32magic;
quoted hunk
/* Data for the above code */
func_desc_t funcdata;
};
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-25 06:44:02
On Thu, 2016-02-25 at 11:04 +1100, Balbir Singh wrote:
On 25/02/16 01:28, Michael Ellerman wrote:
quoted
-bool is_module_trampoline(u32 *p)
+int module_trampoline_target(struct module *mod, unsigned long addr,
+ unsigned long *target)
{
- unsigned int i;
- u32 insns[ARRAY_SIZE(ppc64_stub_insns)];
-
- BUILD_BUG_ON(sizeof(ppc64_stub_insns) != sizeof(ppc64_stub_mask));
+ struct ppc64_stub_entry *stub;
+ func_desc_t funcdata;
+ u32 magic;
- if (probe_kernel_read(insns, p, sizeof(insns)))
+ if (!within_module_core(addr, mod)) {
+ pr_err("%s: stub %lx not in module %s\n", __func__, addr, mod->name);
return -EFAULT;
-EFAULT or -EINVAL?
I think we want EFAULT. Otherwise ftrace_bug() will try and print the actual
instruction, which would then fault. (though I haven't confirmed that by
testing)
I wonder if we can recover from a bad trampoline address.
We can't recover at the moment. Do you mean is there some way we could recover?
cheers
On Thu, Feb 25, 2016 at 01:28:25AM +1100, Michael Ellerman wrote:
We can make that process easier by marking the generated stubs with a
magic value, and then looking for that magic value. Altough this is not
as rigorous as the current method, I believe it is sufficient in
practice.
The actual magic value is sort of debatable; it should be "improbable"
enough. But this can be changed easily, for each kernel compile, even.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-02-26 10:37:58
On Thu, 2016-02-25 at 14:17 +0100, Torsten Duwe wrote:
On Thu, Feb 25, 2016 at 01:28:25AM +1100, Michael Ellerman wrote:
quoted
We can make that process easier by marking the generated stubs with a
magic value, and then looking for that magic value. Altough this is not
as rigorous as the current method, I believe it is sufficient in
practice.
The actual magic value is sort of debatable; it should be "improbable"
enough. But this can be changed easily, for each kernel compile, even.
Yeah. Given the locations we're trying to patch are computed in the first place
from the mcount call sites, I feel like we don't need to be super paranoid
here. The only time I've heard of this code (the current version) tripping up
is when folks are hacking on ftrace.
quoted
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Which is one of:
ori r21,r19,29811
andi. r20,r27,30050
Both of which are pretty improbable. They don't appear in any kernel I have
around here.
I have more plans for this code, which would hopefully mean we can get rid of
the magic checking entirely. But I think this is OK for now.
cheers
Currently we generate the module stub for ftrace_caller() at the bottom
of apply_relocate_add(). However apply_relocate_add() is potentially
called more than once per module, which means we will try to generate
the ftrace_caller() stub multiple times.
This makes sense
Reviewed-by: Balbir Singh <bsingharora@gmail.com>
Currently we generate the module stub for ftrace_caller() at the bottom
of apply_relocate_add(). However apply_relocate_add() is potentially
called more than once per module, which means we will try to generate
the ftrace_caller() stub multiple times.
This makes sense
Reviewed-by: Balbir Singh <bsingharora@gmail.com>
For the entire series also
Tested-by: Balbir Singh <bsingharora@gmail.com>
On Thu, Feb 25, 2016 at 01:28:24AM +1100, Michael Ellerman wrote:
Currently we generate the module stub for ftrace_caller() at the bottom
of apply_relocate_add(). However apply_relocate_add() is potentially
called more than once per module, which means we will try to generate
the ftrace_caller() stub multiple times.
Although the current code deals with that correctly, ie. it only
generates a stub the first time, it would be clearer to only try to
generate the stub once.
Note also on first reading it may appear that we generate a different
stub for each section that requires relocation, but that is not the
case. The code in stub_for_addr() that searches for an existing stub
uses sechdrs[me->arch.stubs_section], ie. the single stub section for
this module.
A cleaner approach is to only generate the ftrace_caller() stub once,
from module_finalize(). An additional benefit is we can clean the ifdefs
up a little.
Finally we must propagate the const'ness of some of the pointers passed
to module_finalize(), but that is also an improvement.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
* Michael Ellerman [off-list ref] [2016-02-25 01:28:24]:
Currently we generate the module stub for ftrace_caller() at the bottom
of apply_relocate_add(). However apply_relocate_add() is potentially
called more than once per module, which means we will try to generate
the ftrace_caller() stub multiple times.
Although the current code deals with that correctly, ie. it only
generates a stub the first time, it would be clearer to only try to
generate the stub once.
Note also on first reading it may appear that we generate a different
stub for each section that requires relocation, but that is not the
case. The code in stub_for_addr() that searches for an existing stub
uses sechdrs[me->arch.stubs_section], ie. the single stub section for
this module.
A cleaner approach is to only generate the ftrace_caller() stub once,
from module_finalize(). An additional benefit is we can clean the ifdefs
up a little.
Finally we must propagate the const'ness of some of the pointers passed
to module_finalize(), but that is also an improvement.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
For all of the patches in the series.
Tested-by: Kamalesh Babulal <redacted>
Regards,
Kamalesh.