ftrace: support for PowerPC
authorSteven Rostedt <rostedt@goodmis.org>
Thu, 15 May 2008 03:49:44 +0000 (23:49 -0400)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 23 May 2008 20:43:11 +0000 (22:43 +0200)
This patch adds full support for ftrace for PowerPC (both 64 and 32 bit).
This includes dynamic tracing and function filtering.

Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/powerpc/Kconfig
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/entry_32.S
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/ftrace.c [new file with mode: 0644]
arch/powerpc/kernel/io.c
arch/powerpc/kernel/irq.c
arch/powerpc/kernel/setup_32.c
arch/powerpc/kernel/setup_64.c
arch/powerpc/platforms/powermac/Makefile
kernel/trace/trace_selftest.c

index 3934e26..62d034a 100644 (file)
@@ -105,11 +105,12 @@ config ARCH_NO_VIRT_TO_BUS
 config PPC
        bool
        default y
+       select HAVE_FTRACE
        select HAVE_IDE
-       select HAVE_OPROFILE
        select HAVE_KPROBES
        select HAVE_KRETPROBES
        select HAVE_LMB
+       select HAVE_OPROFILE
 
 config EARLY_PRINTK
        bool
index 2346d27..f3f5e26 100644 (file)
@@ -12,6 +12,18 @@ CFLAGS_prom_init.o      += -fPIC
 CFLAGS_btext.o         += -fPIC
 endif
 
+ifdef CONFIG_FTRACE
+# Do not trace early boot code
+CFLAGS_REMOVE_cputable.o = -pg
+CFLAGS_REMOVE_prom_init.o = -pg
+
+ifdef CONFIG_DYNAMIC_FTRACE
+# dynamic ftrace setup.
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
+endif
+
 obj-y                          := cputable.o ptrace.o syscalls.o \
                                   irq.o align.o signal_32.o pmc.o vdso.o \
                                   init_task.o process.o systbl.o idle.o \
@@ -78,6 +90,8 @@ obj-$(CONFIG_KEXEC)           += machine_kexec.o crash.o \
 obj-$(CONFIG_AUDIT)            += audit.o
 obj64-$(CONFIG_AUDIT)          += compat_audit.o
 
+obj-$(CONFIG_DYNAMIC_FTRACE)   += ftrace.o
+
 obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o
 
 ifneq ($(CONFIG_PPC_INDIRECT_IO),y)
index 0c8614d..0e62218 100644 (file)
@@ -1035,3 +1035,133 @@ machine_check_in_rtas:
        /* XXX load up BATs and panic */
 
 #endif /* CONFIG_PPC_RTAS */
+
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+_GLOBAL(mcount)
+_GLOBAL(_mcount)
+       stwu    r1,-48(r1)
+       stw     r3, 12(r1)
+       stw     r4, 16(r1)
+       stw     r5, 20(r1)
+       stw     r6, 24(r1)
+       mflr    r3
+       stw     r7, 28(r1)
+       mfcr    r5
+       stw     r8, 32(r1)
+       stw     r9, 36(r1)
+       stw     r10,40(r1)
+       stw     r3, 44(r1)
+       stw     r5, 8(r1)
+       .globl mcount_call
+mcount_call:
+       bl      ftrace_stub
+       nop
+       lwz     r6, 8(r1)
+       lwz     r0, 44(r1)
+       lwz     r3, 12(r1)
+       mtctr   r0
+       lwz     r4, 16(r1)
+       mtcr    r6
+       lwz     r5, 20(r1)
+       lwz     r6, 24(r1)
+       lwz     r0, 52(r1)
+       lwz     r7, 28(r1)
+       lwz     r8, 32(r1)
+       mtlr    r0
+       lwz     r9, 36(r1)
+       lwz     r10,40(r1)
+       addi    r1, r1, 48
+       bctr
+
+_GLOBAL(ftrace_caller)
+       /* Based off of objdump optput from glibc */
+       stwu    r1,-48(r1)
+       stw     r3, 12(r1)
+       stw     r4, 16(r1)
+       stw     r5, 20(r1)
+       stw     r6, 24(r1)
+       mflr    r3
+       lwz     r4, 52(r1)
+       mfcr    r5
+       stw     r7, 28(r1)
+       stw     r8, 32(r1)
+       stw     r9, 36(r1)
+       stw     r10,40(r1)
+       stw     r3, 44(r1)
+       stw     r5, 8(r1)
+.globl ftrace_call
+ftrace_call:
+       bl      ftrace_stub
+       nop
+       lwz     r6, 8(r1)
+       lwz     r0, 44(r1)
+       lwz     r3, 12(r1)
+       mtctr   r0
+       lwz     r4, 16(r1)
+       mtcr    r6
+       lwz     r5, 20(r1)
+       lwz     r6, 24(r1)
+       lwz     r0, 52(r1)
+       lwz     r7, 28(r1)
+       lwz     r8, 32(r1)
+       mtlr    r0
+       lwz     r9, 36(r1)
+       lwz     r10,40(r1)
+       addi    r1, r1, 48
+       bctr
+#else
+_GLOBAL(mcount)
+_GLOBAL(_mcount)
+       stwu    r1,-48(r1)
+       stw     r3, 12(r1)
+       stw     r4, 16(r1)
+       stw     r5, 20(r1)
+       stw     r6, 24(r1)
+       mflr    r3
+       lwz     r4, 52(r1)
+       mfcr    r5
+       stw     r7, 28(r1)
+       stw     r8, 32(r1)
+       stw     r9, 36(r1)
+       stw     r10,40(r1)
+       stw     r3, 44(r1)
+       stw     r5, 8(r1)
+
+       LOAD_REG_ADDR(r5, ftrace_trace_function)
+#if 0
+       mtctr   r3
+       mr      r1, r5
+       bctrl
+#endif
+       lwz     r5,0(r5)
+#if 1
+       mtctr   r5
+       bctrl
+#else
+       bl      ftrace_stub
+#endif
+       nop
+
+       lwz     r6, 8(r1)
+       lwz     r0, 44(r1)
+       lwz     r3, 12(r1)
+       mtctr   r0
+       lwz     r4, 16(r1)
+       mtcr    r6
+       lwz     r5, 20(r1)
+       lwz     r6, 24(r1)
+       lwz     r0, 52(r1)
+       lwz     r7, 28(r1)
+       lwz     r8, 32(r1)
+       mtlr    r0
+       lwz     r9, 36(r1)
+       lwz     r10,40(r1)
+       addi    r1, r1, 48
+       bctr
+#endif
+
+_GLOBAL(ftrace_stub)
+       blr
+
+#endif /* CONFIG_MCOUNT */
index c0db5b7..2c4d9e0 100644 (file)
@@ -870,3 +870,65 @@ _GLOBAL(enter_prom)
        ld      r0,16(r1)
        mtlr    r0
         blr
+
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+_GLOBAL(mcount)
+_GLOBAL(_mcount)
+       /* Taken from output of objdump from lib64/glibc */
+       mflr    r3
+       stdu    r1, -112(r1)
+       std     r3, 128(r1)
+       .globl mcount_call
+mcount_call:
+       bl      ftrace_stub
+       nop
+       ld      r0, 128(r1)
+       mtlr    r0
+       addi    r1, r1, 112
+       blr
+
+_GLOBAL(ftrace_caller)
+       /* Taken from output of objdump from lib64/glibc */
+       mflr    r3
+       ld      r11, 0(r1)
+       stdu    r1, -112(r1)
+       std     r3, 128(r1)
+       ld      r4, 16(r11)
+.globl ftrace_call
+ftrace_call:
+       bl      ftrace_stub
+       nop
+       ld      r0, 128(r1)
+       mtlr    r0
+       addi    r1, r1, 112
+_GLOBAL(ftrace_stub)
+       blr
+#else
+_GLOBAL(mcount)
+       blr
+
+_GLOBAL(_mcount)
+       /* Taken from output of objdump from lib64/glibc */
+       mflr    r3
+       ld      r11, 0(r1)
+       stdu    r1, -112(r1)
+       std     r3, 128(r1)
+       ld      r4, 16(r11)
+
+
+       LOAD_REG_ADDR(r5,ftrace_trace_function)
+       ld      r5,0(r5)
+       ld      r5,0(r5)
+       mtctr   r5
+       bctrl
+
+       nop
+       ld      r0, 128(r1)
+       mtlr    r0
+       addi    r1, r1, 112
+_GLOBAL(ftrace_stub)
+       blr
+
+#endif
+#endif
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
new file mode 100644 (file)
index 0000000..5a4993f
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Code for replacing ftrace calls with jumps.
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/hardirq.h>
+#include <linux/ftrace.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/list.h>
+
+#include <asm/cacheflush.h>
+
+#define CALL_BACK              4
+
+static unsigned int ftrace_nop = 0x60000000;
+
+#ifdef CONFIG_PPC32
+# define GET_ADDR(addr) addr
+#else
+/* PowerPC64's functions are data that points to the functions */
+# define GET_ADDR(addr) *(unsigned long *)addr
+#endif
+
+notrace int ftrace_ip_converted(unsigned long ip)
+{
+       unsigned int save;
+
+       ip -= CALL_BACK;
+       save = *(unsigned int *)ip;
+
+       return save == ftrace_nop;
+}
+
+static unsigned int notrace ftrace_calc_offset(long ip, long addr)
+{
+       return (int)((addr + CALL_BACK) - ip);
+}
+
+notrace unsigned char *ftrace_nop_replace(void)
+{
+       return (char *)&ftrace_nop;
+}
+
+notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+       static unsigned int op;
+
+       addr = GET_ADDR(addr);
+
+       /* Set to "bl addr" */
+       op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffe);
+
+       /*
+        * No locking needed, this must be called via kstop_machine
+        * which in essence is like running on a uniprocessor machine.
+        */
+       return (unsigned char *)&op;
+}
+
+#ifdef CONFIG_PPC64
+# define _ASM_ALIGN    " .align 3 "
+# define _ASM_PTR      " .llong "
+#else
+# define _ASM_ALIGN    " .align 2 "
+# define _ASM_PTR      " .long "
+#endif
+
+notrace int
+ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+                  unsigned char *new_code)
+{
+       unsigned replaced;
+       unsigned old = *(unsigned *)old_code;
+       unsigned new = *(unsigned *)new_code;
+       int faulted = 0;
+
+       /* move the IP back to the start of the call */
+       ip -= CALL_BACK;
+
+       /*
+        * Note: Due to modules and __init, code can
+        *  disappear and change, we need to protect against faulting
+        *  as well as code changing.
+        *
+        * No real locking needed, this code is run through
+        * kstop_machine.
+        */
+       asm volatile (
+               "1: lwz         %1, 0(%2)\n"
+               "   cmpw        %1, %5\n"
+               "   bne         2f\n"
+               "   stwu        %3, 0(%2)\n"
+               "2:\n"
+               ".section .fixup, \"ax\"\n"
+               "3:     li %0, 1\n"
+               "       b 2b\n"
+               ".previous\n"
+               ".section __ex_table,\"a\"\n"
+               _ASM_ALIGN "\n"
+               _ASM_PTR "1b, 3b\n"
+               ".previous"
+               : "=r"(faulted), "=r"(replaced)
+               : "r"(ip), "r"(new),
+                 "0"(faulted), "r"(old)
+               : "memory");
+
+       if (replaced != old && replaced != new)
+               faulted = 2;
+
+       if (!faulted)
+               flush_icache_range(ip, ip + 8);
+
+       return faulted;
+}
+
+notrace int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+       unsigned long ip = (unsigned long)(&ftrace_call);
+       unsigned char old[4], *new;
+       int ret;
+
+       ip += CALL_BACK;
+
+       memcpy(old, &ftrace_call, 4);
+       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 = (long)(&mcount_call);
+       unsigned long *addr = data;
+       unsigned char old[4], *new;
+
+       /* ip is at the location, but modify code will subtact this */
+       ip += CALL_BACK;
+
+       /*
+        * Replace the mcount stub with a pointer to the
+        * ip recorder function.
+        */
+       memcpy(old, &mcount_call, 4);
+       new = ftrace_call_replace(ip, *addr);
+       *addr = ftrace_modify_code(ip, old, new);
+
+       return 0;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+       /* This is running in kstop_machine */
+
+       ftrace_mcount_set(data);
+
+       return 0;
+}
+
index e31aca9..1882bf4 100644 (file)
@@ -120,7 +120,8 @@ EXPORT_SYMBOL(_outsl_ns);
 
 #define IO_CHECK_ALIGN(v,a) ((((unsigned long)(v)) & ((a) - 1)) == 0)
 
-void _memset_io(volatile void __iomem *addr, int c, unsigned long n)
+notrace void
+_memset_io(volatile void __iomem *addr, int c, unsigned long n)
 {
        void *p = (void __force *)addr;
        u32 lc = c;
index 2f73f70..6e01eb0 100644 (file)
@@ -98,7 +98,7 @@ EXPORT_SYMBOL(irq_desc);
 
 int distribute_irqs = 1;
 
-static inline unsigned long get_hard_enabled(void)
+static inline notrace unsigned long get_hard_enabled(void)
 {
        unsigned long enabled;
 
@@ -108,13 +108,13 @@ static inline unsigned long get_hard_enabled(void)
        return enabled;
 }
 
-static inline void set_soft_enabled(unsigned long enable)
+static inline notrace void set_soft_enabled(unsigned long enable)
 {
        __asm__ __volatile__("stb %0,%1(13)"
        : : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled)));
 }
 
-void raw_local_irq_restore(unsigned long en)
+notrace void raw_local_irq_restore(unsigned long en)
 {
        /*
         * get_paca()->soft_enabled = en;
index 5112a4a..22f8e2b 100644 (file)
 #include <asm/kgdb.h>
 #endif
 
+#ifdef CONFIG_FTRACE
+extern void _mcount(void);
+EXPORT_SYMBOL(_mcount);
+#endif
+
 extern void bootx_init(unsigned long r4, unsigned long phys);
 
 int boot_cpuid;
@@ -81,7 +86,7 @@ int ucache_bsize;
  * from the address that it was linked at, so we must use RELOC/PTRRELOC
  * to access static data (including strings).  -- paulus
  */
-unsigned long __init early_init(unsigned long dt_ptr)
+notrace unsigned long __init early_init(unsigned long dt_ptr)
 {
        unsigned long offset = reloc_offset();
        struct cpu_spec *spec;
@@ -111,7 +116,7 @@ unsigned long __init early_init(unsigned long dt_ptr)
  * This is called very early on the boot process, after a minimal
  * MMU environment has been set up but before MMU_init is called.
  */
-void __init machine_init(unsigned long dt_ptr, unsigned long phys)
+notrace void __init machine_init(unsigned long dt_ptr, unsigned long phys)
 {
        /* Enable early debugging if any specified (see udbg.h) */
        udbg_early_init();
@@ -133,7 +138,7 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys)
 
 #ifdef CONFIG_BOOKE_WDT
 /* Checks wdt=x and wdt_period=xx command-line option */
-int __init early_parse_wdt(char *p)
+notrace int __init early_parse_wdt(char *p)
 {
        if (p && strncmp(p, "0", 1) != 0)
               booke_wdt_enabled = 1;
index 098fd96..277bf18 100644 (file)
@@ -85,6 +85,11 @@ struct ppc64_caches ppc64_caches = {
 };
 EXPORT_SYMBOL_GPL(ppc64_caches);
 
+#ifdef CONFIG_FTRACE
+extern void _mcount(void);
+EXPORT_SYMBOL(_mcount);
+#endif
+
 /*
  * These are used in binfmt_elf.c to put aux entries on the stack
  * for each elf executable being started.
index 4d72c8f..8977417 100644 (file)
@@ -1,5 +1,10 @@
 CFLAGS_bootx_init.o            += -fPIC
 
+ifdef CONFIG_FTRACE
+# Do not trace early boot code
+CFLAGS_REMOVE_bootx_init.o = -pg
+endif
+
 obj-y                          += pic.o setup.o time.o feature.o pci.o \
                                   sleep.o low_i2c.o cache.o pfunc_core.o \
                                   pfunc_base.o
index a5f6001..3877dd9 100644 (file)
@@ -123,6 +123,7 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
        int ret;
        int save_ftrace_enabled = ftrace_enabled;
        int save_tracer_enabled = tracer_enabled;
+       char *func_name;
 
        /* The ftrace test PASSED */
        printk(KERN_CONT "PASSED\n");
@@ -142,9 +143,15 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
                return ret;
        }
 
+       /*
+        * Some archs *cough*PowerPC*cough* add charachters to the
+        * start of the function names. We simply put a '*' to
+        * accomodate them.
+        */
+       func_name = "*" STR(DYN_FTRACE_TEST_NAME);
+
        /* filter only on our function */
-       ftrace_set_filter(STR(DYN_FTRACE_TEST_NAME),
-                         sizeof(STR(DYN_FTRACE_TEST_NAME)), 1);
+       ftrace_set_filter(func_name, strlen(func_name), 1);
 
        /* enable tracing */
        tr->ctrl = 1;