]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/mips/kernel/ftrace.c
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/upstream-linus
[mv-sheeva.git] / arch / mips / kernel / ftrace.c
index 5a84a1f11231fa000b71525842fc2cd6c1ec4d71..94ca2b018af70ff3191d4e44055311efc09e800d 100644 (file)
 #include <asm/cacheflush.h>
 #include <asm/uasm.h>
 
-/*
- * If the Instruction Pointer is in module space (0xc0000000), return true;
- * otherwise, it is in kernel space (0x80000000), return false.
- *
- * FIXME: This will not work when the kernel space and module space are the
- * same. If they are the same, we need to modify scripts/recordmcount.pl,
- * ftrace_make_nop/call() and the other related parts to ensure the
- * enabling/disabling of the calling site to _mcount is right for both kernel
- * and module.
- */
-
-static inline int in_module(unsigned long ip)
-{
-       return ip & 0x40000000;
-}
+#include <asm-generic/sections.h>
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 
 #define JAL 0x0c000000         /* jump & link: ip --> ra, jump to target */
 #define ADDR_MASK 0x03ffffff   /*  op_code|addr : 31...26|25 ....0 */
 
-#define INSN_B_1F_4 0x10000004 /* b 1f; offset = 4 */
-#define INSN_B_1F_5 0x10000005 /* b 1f; offset = 5 */
 #define INSN_NOP 0x00000000    /* nop */
 #define INSN_JAL(addr) \
        ((unsigned int)(JAL | (((addr) >> 2) & ADDR_MASK)))
@@ -69,6 +53,20 @@ static inline void ftrace_dyn_arch_init_insns(void)
 #endif
 }
 
+/*
+ * Check if the address is in kernel space
+ *
+ * Clone core_kernel_text() from kernel/extable.c, but doesn't call
+ * init_kernel_text() for Ftrace doesn't trace functions in init sections.
+ */
+static inline int in_kernel_space(unsigned long ip)
+{
+       if (ip >= (unsigned long)_stext &&
+           ip <= (unsigned long)_etext)
+               return 1;
+       return 0;
+}
+
 static int ftrace_modify_code(unsigned long ip, unsigned int new_code)
 {
        int faulted;
@@ -84,6 +82,42 @@ static int ftrace_modify_code(unsigned long ip, unsigned int new_code)
        return 0;
 }
 
+/*
+ * The details about the calling site of mcount on MIPS
+ *
+ * 1. For kernel:
+ *
+ * move at, ra
+ * jal _mcount         --> nop
+ *
+ * 2. For modules:
+ *
+ * 2.1 For KBUILD_MCOUNT_RA_ADDRESS and CONFIG_32BIT
+ *
+ * lui v1, hi_16bit_of_mcount        --> b 1f (0x10000005)
+ * addiu v1, v1, low_16bit_of_mcount
+ * move at, ra
+ * move $12, ra_address
+ * jalr v1
+ *  sub sp, sp, 8
+ *                                  1: offset = 5 instructions
+ * 2.2 For the Other situations
+ *
+ * lui v1, hi_16bit_of_mcount        --> b 1f (0x10000004)
+ * addiu v1, v1, low_16bit_of_mcount
+ * move at, ra
+ * jalr v1
+ *  nop | move $12, ra_address | sub sp, sp, 8
+ *                                  1: offset = 4 instructions
+ */
+
+#if defined(KBUILD_MCOUNT_RA_ADDRESS) && defined(CONFIG_32BIT)
+#define MCOUNT_OFFSET_INSNS 5
+#else
+#define MCOUNT_OFFSET_INSNS 4
+#endif
+#define INSN_B_1F (0x10000000 | MCOUNT_OFFSET_INSNS)
+
 int ftrace_make_nop(struct module *mod,
                    struct dyn_ftrace *rec, unsigned long addr)
 {
@@ -91,39 +125,11 @@ int ftrace_make_nop(struct module *mod,
        unsigned long ip = rec->ip;
 
        /*
-        * We have compiled module with -mlong-calls, but compiled the kernel
-        * without it, we need to cope with them respectively.
+        * If ip is in kernel space, no long call, otherwise, long call is
+        * needed.
         */
-       if (in_module(ip)) {
-#if defined(KBUILD_MCOUNT_RA_ADDRESS) && defined(CONFIG_32BIT)
-               /*
-                * lui v1, hi_16bit_of_mcount        --> b 1f (0x10000005)
-                * addiu v1, v1, low_16bit_of_mcount
-                * move at, ra
-                * move $12, ra_address
-                * jalr v1
-                *  sub sp, sp, 8
-                *                                  1: offset = 5 instructions
-                */
-               new = INSN_B_1F_5;
-#else
-               /*
-                * lui v1, hi_16bit_of_mcount        --> b 1f (0x10000004)
-                * addiu v1, v1, low_16bit_of_mcount
-                * move at, ra
-                * jalr v1
-                *  nop | move $12, ra_address | sub sp, sp, 8
-                *                                  1: offset = 4 instructions
-                */
-               new = INSN_B_1F_4;
-#endif
-       } else {
-               /*
-                * move at, ra
-                * jal _mcount          --> nop
-                */
-               new = INSN_NOP;
-       }
+       new = in_kernel_space(ip) ? INSN_NOP : INSN_B_1F;
+
        return ftrace_modify_code(ip, new);
 }
 
@@ -132,8 +138,8 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
        unsigned int new;
        unsigned long ip = rec->ip;
 
-       /* ip, module: 0xc0000000, kernel: 0x80000000 */
-       new = in_module(ip) ? insn_lui_v1_hi16_mcount : insn_jal_ftrace_caller;
+       new = in_kernel_space(ip) ? insn_jal_ftrace_caller :
+               insn_lui_v1_hi16_mcount;
 
        return ftrace_modify_code(ip, new);
 }
@@ -190,29 +196,25 @@ int ftrace_disable_ftrace_graph_caller(void)
 #define S_R_SP (0xafb0 << 16)  /* s{d,w} R, offset(sp) */
 #define OFFSET_MASK    0xffff  /* stack offset range: 0 ~ PT_SIZE */
 
-unsigned long ftrace_get_parent_addr(unsigned long self_addr,
-                                    unsigned long parent,
-                                    unsigned long parent_addr,
-                                    unsigned long fp)
+unsigned long ftrace_get_parent_ra_addr(unsigned long self_ra, unsigned long
+               old_parent_ra, unsigned long parent_ra_addr, unsigned long fp)
 {
-       unsigned long sp, ip, ra;
+       unsigned long sp, ip, tmp;
        unsigned int code;
        int faulted;
 
        /*
-        * For module, move the ip from calling site of mcount to the
-        * instruction "lui v1, hi_16bit_of_mcount"(offset is 20), but for
-        * kernel, move to the instruction "move ra, at"(offset is 12)
+        * For module, move the ip from the return address after the
+        * instruction "lui v1, hi_16bit_of_mcount"(offset is 24), but for
+        * kernel, move after the instruction "move ra, at"(offset is 16)
         */
-       ip = self_addr - (in_module(self_addr) ? 20 : 12);
+       ip = self_ra - (in_kernel_space(self_ra) ? 16 : 24);
 
        /*
         * search the text until finding the non-store instruction or "s{d,w}
         * ra, offset(sp)" instruction
         */
        do {
-               ip -= 4;
-
                /* get the code at "ip": code = *(unsigned int *)ip; */
                safe_load_code(code, ip, faulted);
 
@@ -224,18 +226,20 @@ unsigned long ftrace_get_parent_addr(unsigned long self_addr,
                 * store the ra on the stack
                 */
                if ((code & S_R_SP) != S_R_SP)
-                       return parent_addr;
+                       return parent_ra_addr;
 
-       } while (((code & S_RA_SP) != S_RA_SP));
+               /* Move to the next instruction */
+               ip -= 4;
+       } while ((code & S_RA_SP) != S_RA_SP);
 
        sp = fp + (code & OFFSET_MASK);
 
-       /* ra = *(unsigned long *)sp; */
-       safe_load_stack(ra, sp, faulted);
+       /* tmp = *(unsigned long *)sp; */
+       safe_load_stack(tmp, sp, faulted);
        if (unlikely(faulted))
                return 0;
 
-       if (ra == parent)
+       if (tmp == old_parent_ra)
                return sp;
        return 0;
 }
@@ -246,21 +250,21 @@ unsigned long ftrace_get_parent_addr(unsigned long self_addr,
  * Hook the return address and push it in the stack of return addrs
  * in current thread info.
  */
-void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
+void prepare_ftrace_return(unsigned long *parent_ra_addr, unsigned long self_ra,
                           unsigned long fp)
 {
-       unsigned long old;
+       unsigned long old_parent_ra;
        struct ftrace_graph_ent trace;
        unsigned long return_hooker = (unsigned long)
            &return_to_handler;
-       int faulted;
+       int faulted, insns;
 
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                return;
 
        /*
-        * "parent" is the stack address saved the return address of the caller
-        * of _mcount.
+        * "parent_ra_addr" is the stack address saved the return address of
+        * the caller of _mcount.
         *
         * if the gcc < 4.5, a leaf function does not save the return address
         * in the stack address, so, we "emulate" one in _mcount's stack space,
@@ -275,37 +279,44 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
         * do it in ftrace_graph_caller of mcount.S.
         */
 
-       /* old = *parent; */
-       safe_load_stack(old, parent, faulted);
+       /* old_parent_ra = *parent_ra_addr; */
+       safe_load_stack(old_parent_ra, parent_ra_addr, faulted);
        if (unlikely(faulted))
                goto out;
 #ifndef KBUILD_MCOUNT_RA_ADDRESS
-       parent = (unsigned long *)ftrace_get_parent_addr(self_addr, old,
-                       (unsigned long)parent, fp);
+       parent_ra_addr = (unsigned long *)ftrace_get_parent_ra_addr(self_ra,
+                       old_parent_ra, (unsigned long)parent_ra_addr, fp);
        /*
         * If fails when getting the stack address of the non-leaf function's
         * ra, stop function graph tracer and return
         */
-       if (parent == 0)
+       if (parent_ra_addr == 0)
                goto out;
 #endif
-       /* *parent = return_hooker; */
-       safe_store_stack(return_hooker, parent, faulted);
+       /* *parent_ra_addr = return_hooker; */
+       safe_store_stack(return_hooker, parent_ra_addr, faulted);
        if (unlikely(faulted))
                goto out;
 
-       if (ftrace_push_return_trace(old, self_addr, &trace.depth, fp) ==
-           -EBUSY) {
-               *parent = old;
+       if (ftrace_push_return_trace(old_parent_ra, self_ra, &trace.depth, fp)
+           == -EBUSY) {
+               *parent_ra_addr = old_parent_ra;
                return;
        }
 
-       trace.func = self_addr;
+       /*
+        * Get the recorded ip of the current mcount calling site in the
+        * __mcount_loc section, which will be used to filter the function
+        * entries configured through the tracing/set_graph_function interface.
+        */
+
+       insns = in_kernel_space(self_ra) ? 2 : MCOUNT_OFFSET_INSNS + 1;
+       trace.func = self_ra - (MCOUNT_INSN_SIZE * insns);
 
        /* Only trace if the calling function expects to */
        if (!ftrace_graph_entry(&trace)) {
                current->curr_ret_stack--;
-               *parent = old;
+               *parent_ra_addr = old_parent_ra;
        }
        return;
 out: