From: Ralf Baechle <ralf@linux-mips.org>
To: "Frédéric Weisbecker" <fweisbec@gmail.com>
Cc: linux-mips@linux-mips.org
Subject: Re: Support for ftrace in MIPS
Date: Thu, 23 Oct 2008 10:07:46 +0100 [thread overview]
Message-ID: <20081023090746.GC10625@linux-mips.org> (raw)
In-Reply-To: <c62985530810201800r3ddfbb99s1a0809f2eba45cdf@mail.gmail.com>
Frédéric,
> I saw that there is no implementation of ftrace in mips currently and
> I would like to know if someone is currently working on it. If not I
> would be glad
> to work on patches for that.
I do have an ftrace implementation in the queue. Static ftrace works as
far as I've tested but due to a restriction of the generic ftrace code the
MIPS code for dynamic ftrace can't work on most SMP systems - basically
only the R10000 and single core 34K SMP kernel variants have a chance to
work. For others cache flushing requires a call to smp_call_function but
that can't work because other CPUs are stopped at the time of the flush.
So that still needs some sorting.
Patch below.
Ralf
From: Ralf Baechle <ralf@linux-mips.org>
Date: Wed, 10 Sep 2008 18:04:04 +0200
Subject: [PATCH] [MIPS] Implement fcount support for MIPS.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
---
arch/mips/Kconfig | 5 ++
arch/mips/Makefile | 2 +
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/ftrace.c | 118 +++++++++++++++++++++++++++++++++++++
arch/mips/kernel/mips_ksyms.c | 6 ++
arch/mips/lib/Makefile | 2 +
arch/mips/lib/mcount.S | 128 +++++++++++++++++++++++++++++++++++++++++
include/asm-mips/ftrace.h | 14 +++++
8 files changed, 277 insertions(+), 0 deletions(-)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 5f149b0..43202db 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1,6 +1,7 @@
config MIPS
bool
default y
+ select HAVE_FTRACE
select HAVE_IDE
select HAVE_OPROFILE
select HAVE_ARCH_KGDB
@@ -1503,6 +1504,10 @@ config MIPS_APSP_KSPD
"exit" syscall notifying other kernel modules the SP program is
exiting. You probably want to say yes here.
+config SYS_SUPPORTS_DYNAMIC_FTRACE
+ def_bool y
+ select HAVE_DYNAMIC_FTRACE if !SMP || CPU_R10000
+
config MIPS_CMP
bool "MIPS CMP framework support"
depends on SYS_SUPPORTS_MIPS_CMP
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 7f39fd8..ae9099b 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -14,7 +14,9 @@
KBUILD_DEFCONFIG := ip22_defconfig
+ifndef CONFIG_FTRACE
cflags-y := -ffunction-sections
+endif
#
# Select the object file format to substitute into the linker script.
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index d9da711..ea730e0 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -43,6 +43,8 @@ obj-$(CONFIG_CPU_TX39XX) += r2300_fpu.o r2300_switch.o
obj-$(CONFIG_CPU_TX49XX) += r4k_fpu.o r4k_switch.o
obj-$(CONFIG_CPU_VR41XX) += r4k_fpu.o r4k_switch.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SMP_UP) += smp-up.o
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c
new file mode 100644
index 0000000..e24ae47
--- /dev/null
+++ b/arch/mips/kernel/ftrace.c
@@ -0,0 +1,118 @@
+#include <linux/ftrace.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+
+#include <asm/cacheflush.h>
+#include <asm/sgidefs.h>
+#include <asm/inst.h>
+#include <asm/ftrace.h>
+
+/*
+ * On O32 the gcc generated code sequence to invoke _mcount looks like
+ *
+ * move $1, $31 # save current return address
+ * subu $sp, $sp, 8 # _mcount pops 2 words from stack
+ * jal _mcount
+ * j $31
+ *
+ * If the _mcount call is nop'ed out we still need to pop the stack frame.
+ * So we use an addiu $sp, $sp, 8 instead. NABI doesn't allocate frames in
+ * such a nonsenical way so a normal nop does the magic.
+ */
+static const union mips_instruction ftrace_nop = {
+#if _MIPS_SIM == _MIPS_SIM_ABI32
+ .i_format.opcode = addiu_op, /* addiu $sp, $sp, 8 */
+ .i_format.rs = 29,
+ .i_format.rt = 29,
+ .i_format.simmediate = 8
+#else
+ .word = 0 /* normal nop */
+#endif
+};
+
+notrace unsigned char *ftrace_nop_replace(void)
+{
+ return (unsigned char *) ftrace_nop.byte;
+}
+
+notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+ static union mips_instruction inst;
+
+ if (unlikely(ip ^ addr) >> 26) {
+ WARN_ON_ONCE(1);
+ return NULL;
+ }
+
+ inst.j_format.opcode = jal_op;
+ inst.j_format.target = addr >> 2;
+
+ return (unsigned char *) &inst;
+}
+
+notrace int
+ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ unsigned int old = *(unsigned int *) old_code;
+ unsigned int new = *(unsigned int *) new_code;
+ unsigned int replaced;
+ int err;
+
+ err = __get_user(replaced, (unsigned int *) ip);
+ if (unlikely(err))
+ return 1;
+
+ err = __put_user(new, (unsigned int *) ip);
+ if (unlikely(err))
+ return 1;
+
+ if (unlikely(replaced != old && replaced != new))
+ return 2;
+
+ /*
+ * This is broken. We need to flush the icache on all processors
+ * but can't because ftrace_modify_code is executed with interrupts
+ * disabled ...
+ */
+ local_flush_icache_range(ip, ip + MCOUNT_INSN_SIZE);
+
+ return 0;
+}
+
+notrace int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long) &ftrace_call;
+ unsigned char old[MCOUNT_INSN_SIZE], *new;
+ int ret;
+
+ memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(ip, (unsigned long)func);
+ ret = ftrace_modify_code(ip, old, new);
+
+ return ret;
+}
+
+notrace int ftrace_mcount_set(unsigned long *data)
+{
+ unsigned long ip = (unsigned long) &mcount_call;
+ unsigned long *addr = data;
+ unsigned char old[MCOUNT_INSN_SIZE], *new;
+
+ /*
+ * Replace the mcount stub with a pointer to the
+ * ip recorder function.
+ */
+ memcpy(old, &mcount_call, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(ip, *addr);
+ *addr = ftrace_modify_code(ip, old, new);
+
+ return 0;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ ftrace_mcount_set(data);
+
+ return 0;
+}
diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c
index 225755d..18f5556 100644
--- a/arch/mips/kernel/mips_ksyms.c
+++ b/arch/mips/kernel/mips_ksyms.c
@@ -11,6 +11,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <asm/checksum.h>
+#include <asm/ftrace.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
@@ -51,3 +52,8 @@ EXPORT_SYMBOL(csum_partial_copy_nocheck);
EXPORT_SYMBOL(__csum_partial_copy_user);
EXPORT_SYMBOL(invalid_pte_table);
+
+#ifdef CONFIG_FTRACE
+/* _mcount is defined in assembly */
+EXPORT_SYMBOL(_mcount);
+#endif
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 8810dfb..c9ec0c3 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -27,5 +27,7 @@ obj-$(CONFIG_CPU_TX39XX) += r3k_dump_tlb.o
obj-$(CONFIG_CPU_TX49XX) += dump_tlb.o
obj-$(CONFIG_CPU_VR41XX) += dump_tlb.o
+obj-$(CONFIG_FTRACE) += mcount.o
+
# libgcc-style stuff needed in the kernel
obj-y += ashldi3.o ashrdi3.o cmpdi2.o lshrdi3.o ucmpdi2.o
diff --git a/arch/mips/lib/mcount.S b/arch/mips/lib/mcount.S
new file mode 100644
index 0000000..9817ce6
--- /dev/null
+++ b/arch/mips/lib/mcount.S
@@ -0,0 +1,128 @@
+#include <asm/regdef.h>
+#include <asm/ftrace.h>
+#include <asm/asm.h>
+
+/*
+ * The problem here is that _mcount() is not a normal function. It does
+ * not obey the calling conventions of a normal function. It takes its
+ * arguments in $at and $ra, and it is required to preserve $ra - and most
+ * other registers, in fact, if I remember correctly. The arguments have
+ * not yet been saved. You need to do this. Another bizarreness is that
+ * the caller needs to pop an extra 8 bytes for the O32.
+ */
+
+#if _MIPS_SIM == _ABIO32
+#define POP_EXTRA 8
+#define ARG_SAVE 16
+#define N_ARG_REGS 4
+#else
+#define POP_EXTRA 0
+#define ARG_SAVE 0
+#define N_ARG_REGS 8
+#endif
+
+ .macro save_mcount_frame
+ PTR_SUBU sp, sp, ARG_SAVE + (N_ARG_REGS + 4) * LONGSIZE
+ LONG_S AT, ARG_SAVE + 0 * LONGSIZE(sp)
+ LONG_S ra, ARG_SAVE + 1 * LONGSIZE(sp)
+ LONG_S a0, ARG_SAVE + 2 * LONGSIZE(sp)
+ LONG_S a1, ARG_SAVE + 3 * LONGSIZE(sp)
+ LONG_S a2, ARG_SAVE + 4 * LONGSIZE(sp)
+ LONG_S a3, ARG_SAVE + 5 * LONGSIZE(sp)
+#if _MIPS_SIM == _ABI64
+ LONG_S a4, ARG_SAVE + 6 * LONGSIZE(sp)
+ LONG_S a5, ARG_SAVE + 7 * LONGSIZE(sp)
+ LONG_S a6, ARG_SAVE + 8 * LONGSIZE(sp)
+ LONG_S a7, ARG_SAVE + 9 * LONGSIZE(sp)
+#endif
+ LONG_S v0, ARG_SAVE + (2 + N_ARG_REGS) * LONGSIZE(sp)
+ .endm
+
+ .macro restore_mcount_frame
+ LONG_L AT, ARG_SAVE + 0 * LONGSIZE(sp)
+ LONG_L ra, ARG_SAVE + 1 * LONGSIZE(sp)
+ LONG_L a0, ARG_SAVE + 2 * LONGSIZE(sp)
+ LONG_L a1, ARG_SAVE + 3 * LONGSIZE(sp)
+ LONG_L a2, ARG_SAVE + 4 * LONGSIZE(sp)
+ LONG_L a3, ARG_SAVE + 5 * LONGSIZE(sp)
+#if _MIPS_SIM == _ABI64
+ LONG_L a4, ARG_SAVE + 6 * LONGSIZE(sp)
+ LONG_L a5, ARG_SAVE + 7 * LONGSIZE(sp)
+ LONG_L a6, ARG_SAVE + 8 * LONGSIZE(sp)
+ LONG_L a7, ARG_SAVE + 9 * LONGSIZE(sp)
+#endif
+ LONG_L v0, ARG_SAVE + (2 + N_ARG_REGS) * LONGSIZE(sp)
+ PTR_ADDU sp, sp, ARG_SAVE + (N_ARG_REGS + 4) * LONGSIZE + POP_EXTRA
+ .endm
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+LEAF(_mcount)
+ .set push
+ .set noreorder
+ .set noat
+ save_mcount_frame
+ PTR_SUBU a0, ra, 2 * MCOUNT_INSN_SIZE
+
+FEXPORT(mcount_call)
+ jal ftrace_stub
+ nop
+
+ restore_mcount_frame
+ jr ra
+ move ra, AT
+ .set pop
+ END(_mcount)
+
+LEAF(ftrace_caller)
+ .set push
+ .set noreorder
+ .set noat
+ save_mcount_frame
+ PTR_SUBU a0, ra, 2 * MCOUNT_INSN_SIZE
+ move a1, AT
+
+FEXPORT(ftrace_call)
+ jal ftrace_stub
+ nop
+
+ restore_mcount_frame
+ jr ra
+ move ra, AT
+ .set pop
+ END(ftrace_caller)
+
+#else
+
+LEAF(_mcount)
+ .set push
+ .set noreorder
+ .set noat
+ PTR_L t0, ftrace_trace_function
+ PTR_LA t1, ftrace_stub
+ bne t0, t1, trace
+ nop
+
+#if POP_EXTRA
+ PTR_ADDIU sp, sp, 8
+#endif
+ jr ra
+ move ra, AT
+
+trace:
+ save_mcount_frame
+ PTR_SUBU a0, ra, 2 * MCOUNT_INSN_SIZE
+ move a1, AT
+ jalr t0
+ nop
+ restore_mcount_frame
+ jr ra
+ move ra, AT
+ .set pop
+ END(_mcount)
+
+#endif /* !CONFIG_DYNAMIC_FTRACE */
+
+LEAF(ftrace_stub)
+ jr ra
+ END(ftrace_stub)
diff --git a/include/asm-mips/ftrace.h b/include/asm-mips/ftrace.h
new file mode 100644
index 0000000..f0479ce
--- /dev/null
+++ b/include/asm-mips/ftrace.h
@@ -0,0 +1,14 @@
+#ifndef __ASM_FTRACE_H
+#define __ASM_FTRACE_H
+
+#ifdef CONFIG_FTRACE
+#define MCOUNT_ADDR ((long)(_mcount))
+#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
+
+#ifndef __ASSEMBLY__
+extern void _mcount(void);
+#endif
+
+#endif /* CONFIG_FTRACE */
+
+#endif /* __ASM_FTRACE_H */
next prev parent reply other threads:[~2008-10-23 9:07 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-10-21 1:00 Support for ftrace in MIPS Frédéric Weisbecker
2008-10-23 9:07 ` Ralf Baechle [this message]
2008-10-23 12:31 ` Frédéric Weisbecker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20081023090746.GC10625@linux-mips.org \
--to=ralf@linux-mips.org \
--cc=fweisbec@gmail.com \
--cc=linux-mips@linux-mips.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.