lkml.org 
[lkml]   [2010]   [Feb]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Hi Rabin,

On Sun, Jan 31, 2010 at 11:03:16PM +0530, Rabin Vincent wrote:
> This adds mcount recording and updates dynamic ftrace for ARM to work
> with the new ftrace dyamic tracing implementation.
>=20
> With dynamic tracing, mcount() is implemented as a nop. Callsites ar=
e
> patched on startup with nops, and dynamically patched to call to the
> ftrace_caller() routine as needed.
>=20
> One oddity of the ARM implementation is that we need to handle two ki=
nds
> of mcounts. Newer GCC versions renamed the mcount routine (to
> __gnu_mcount_nc) and changed the semantics (lr is pushed on the stack
> before the call). We detect which one is used at run-time.
>=20
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Frederic Weisbecker <fweisbec@gmail.com>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Abhishek Sagar <sagar.abhishek@gmail.com>
> Cc: Uwe Kleine-K=F6nig <u.kleine-koenig@pengutronix.de>
> Signed-off-by: Rabin Vincent <rabin@rab.in>
> ---
> arch/arm/Kconfig | 2 +
> arch/arm/include/asm/ftrace.h | 14 +++++
> arch/arm/kernel/entry-common.S | 25 ++++++---
> arch/arm/kernel/ftrace.c | 122 ++++++++++++++++++++++++++----=
----------
> scripts/recordmcount.pl | 2 +
> 5 files changed, 115 insertions(+), 50 deletions(-)
>=20
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 4c33ca8..dc92691 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -17,6 +17,8 @@ config ARM
> select HAVE_KPROBES if (!XIP_KERNEL)
> select HAVE_KRETPROBES if (HAVE_KPROBES)
> select HAVE_FUNCTION_TRACER if (!XIP_KERNEL)
> + select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL)
> + select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL && !THUMB2_KERNEL)
> select HAVE_GENERIC_DMA_COHERENT
> select HAVE_KERNEL_GZIP
> select HAVE_KERNEL_LZO
> diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftr=
ace.h
> index 103f7ee..12f445d 100644
> --- a/arch/arm/include/asm/ftrace.h
> +++ b/arch/arm/include/asm/ftrace.h
> @@ -8,6 +8,20 @@
> #ifndef __ASSEMBLY__
> extern void mcount(void);
> extern void __gnu_mcount_nc(void);
> +
> +#ifdef CONFIG_DYNAMIC_FTRACE
> +struct dyn_arch_ftrace {
> + bool gnu_mcount;
> +};
> +
> +static inline unsigned long ftrace_call_adjust(unsigned long addr)
> +{
> + return addr;
> +}
> +
> +extern void ftrace_caller_gnu(void);
> +#endif
> +
> #endif
> =20
> #endif
> diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-c=
ommon.S
> index 2c1db77..2a7ab14 100644
> --- a/arch/arm/kernel/entry-common.S
> +++ b/arch/arm/kernel/entry-common.S
> @@ -93,16 +93,25 @@ ENDPROC(ret_from_fork)
> =20
> #ifdef CONFIG_FUNCTION_TRACER
> #ifdef CONFIG_DYNAMIC_FTRACE
> +ENTRY(__gnu_mcount_nc)
> + mov ip, lr
> + ldm sp!, {lr}
> + mov pc, ip
> +
> ENTRY(mcount)
> - stmdb sp!, {r0-r3, lr}
> - mov r0, lr
> - sub r0, r0, #MCOUNT_INSN_SIZE
> + stmdb sp!, {lr}
> + ldr lr, [fp, #-4]
> + ldmia sp!, {pc}
> =20
> - .globl mcount_call
> -mcount_call:
> - bl ftrace_stub
> - ldr lr, [fp, #-4] @ restore lr
> - ldmia sp!, {r0-r3, pc}
> +ENTRY(ftrace_caller_gnu)
> + add sp, sp, #4
> +
> + /*
> + * We take advantage of the fact that we build with frame pointers =
and
> + * -mapcs-frame to combine the ftrace_caller implementations for th=
e
> + * two mcounts with just the above adjustment. This will need to b=
e
> + * revisited if Thumb-2 support is added.
> + */
> =20
> ENTRY(ftrace_caller)
> stmdb sp!, {r0-r3, lr}
> diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
> index c638427..1d5d268 100644
> --- a/arch/arm/kernel/ftrace.c
> +++ b/arch/arm/kernel/ftrace.c
> @@ -7,11 +7,12 @@
> *
> * Defines low-level handling of mcount calls when the kernel
> * is compiled with the -pg flag. When using dynamic ftrace, the
> - * mcount call-sites get patched lazily with NOP till they are
> - * enabled. All code mutation routines here take effect atomically.
> + * mcount call-sites get patched with NOP till they are enabled.
> + * All code mutation routines here take effect atomically.
> */
> =20
> #include <linux/ftrace.h>
> +#include <linux/uaccess.h>
> =20
> #include <asm/cacheflush.h>
> #include <asm/ftrace.h>
> @@ -20,17 +21,26 @@
> #define BL_OPCODE 0xeb000000
> #define BL_OFFSET_MASK 0x00ffffff
> =20
> -static unsigned long bl_insn;
> -static const unsigned long NOP =3D 0xe1a00000; /* mov r0, r0 */
> +#define NOP 0xe1a00000 /* mov r0, r0 */
> +#define GNU_NOP 0xe8bd4000 /* pop {lr} */
> =20
> -unsigned char *ftrace_nop_replace(void)
> +#define GNU_MCOUNT_ADDR ((unsigned long) __gnu_mcount_nc)
> +#define GNU_FTRACE_ADDR ((unsigned long) ftrace_caller_gnu)
> +
> +static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
> +{
> + return rec->arch.gnu_mcount ? GNU_NOP : NOP;
> +}
> +
> +static unsigned long ftrace_caller_addr(struct dyn_ftrace *rec)
> {
> - return (char *)&NOP;
> + return rec->arch.gnu_mcount ? GNU_FTRACE_ADDR : FTRACE_ADDR;
> }
> =20
> /* construct a branch (BL) instruction to addr */
> -unsigned char *ftrace_call_replace(unsigned long pc, unsigned long a=
ddr)
> +static unsigned long ftrace_call_replace(unsigned long pc, unsigned =
long addr)
> {
> + unsigned long bl_insn;
> long offset;
> =20
> offset =3D (long)addr - (long)(pc + PC_OFFSET);
> @@ -39,65 +49,93 @@ unsigned char *ftrace_call_replace(unsigned long =
pc, unsigned long addr)
> * doesn't generate branches outside of kernel text.
> */
> WARN_ON_ONCE(1);
> - return NULL;
> + return 0;
> }
> offset =3D (offset >> 2) & BL_OFFSET_MASK;
> bl_insn =3D BL_OPCODE | offset;
> - return (unsigned char *)&bl_insn;
> + return bl_insn;
> }
> =20
> -int ftrace_modify_code(unsigned long pc, unsigned char *old_code,
> - unsigned char *new_code)
> +static int ftrace_modify_code(unsigned long pc, unsigned long old,
> + unsigned long new)
> {
> - unsigned long err =3D 0, replaced =3D 0, old, new;
> + unsigned long replaced;
> =20
> - old =3D *(unsigned long *)old_code;
> - new =3D *(unsigned long *)new_code;
> + if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
> + return -EFAULT;
> =20
> - __asm__ __volatile__ (
> - "1: ldr %1, [%2] \n"
> - " cmp %1, %4 \n"
> - "2: streq %3, [%2] \n"
> - " cmpne %1, %3 \n"
> - " movne %0, #2 \n"
> - "3:\n"
> + if (replaced !=3D old)
> + return -EINVAL;
> =20
> - ".section .fixup, \"ax\"\n"
> - "4: mov %0, #1 \n"
> - " b 3b \n"
> - ".previous\n"
> + if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE))
> + return -EPERM;
> =20
> - ".section __ex_table, \"a\"\n"
> - " .long 1b, 4b \n"
> - " .long 2b, 4b \n"
> - ".previous\n"
> + flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
> =20
> - : "=3Dr"(err), "=3Dr"(replaced)
> - : "r"(pc), "r"(new), "r"(old), "0"(err), "1"(replaced)
> - : "memory");
> -
> - if (!err && (replaced =3D=3D old))
> - flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
> -
> - return err;
> + return 0;
> }
> =20
> int ftrace_update_ftrace_func(ftrace_func_t func)
> {
> - int ret;
> unsigned long pc, old;
> - unsigned char *new;
> + unsigned long new;
> =20
> pc =3D (unsigned long)&ftrace_call;
> memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
> new =3D ftrace_call_replace(pc, (unsigned long)func);
> - ret =3D ftrace_modify_code(pc, (unsigned char *)&old, new);
> +
> + return ftrace_modify_code(pc, old, new);
> +}
> +
> +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
> +{
> + unsigned long new, old;
> + unsigned long ip =3D rec->ip;
> +
> + old =3D ftrace_nop_replace(rec);
> + new =3D ftrace_call_replace(ip, ftrace_caller_addr(rec));
> +
> + return ftrace_modify_code(rec->ip, old, new);
> +}
> +
> +static int ftrace_detect_make_nop(struct module *mod,
> + struct dyn_ftrace *rec, unsigned long addr)
> +{
> + unsigned long ip =3D rec->ip;
> + unsigned long call;
> + int ret;
> +
> + call =3D ftrace_call_replace(ip, GNU_MCOUNT_ADDR);
> + ret =3D ftrace_modify_code(ip, call, GNU_NOP);
> + if (!ret)
> + rec->arch.gnu_mcount =3D true;
> + else if (ret =3D=3D -EINVAL) {
> + call =3D ftrace_call_replace(ip, addr);
> + ret =3D ftrace_modify_code(ip, call, NOP);
> + }
> +
> return ret;
> }
Wouldn't it be nice to patch out the instruction pushing lr to the
stack (in the gnu case)? Should work, shouldn't it.

Thanks for looking into that.

Best regards
Uwe

--=20
Pengutronix e.K. | Uwe Kleine-K=F6nig =
|
Industrial Linux Solutions | http://www.pengutronix.=
de/ |
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel"=
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2010-02-01 11:30    [W:0.037 / U:1.800 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site