]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86/kernel/ftrace.c
x86, xsave: dynamically allocate sigframes fpstate instead of static allocation
[linux-2.6-omap-h63xx.git] / arch / x86 / kernel / ftrace.c
index 2e060c58b860f4fa1a1b051c8e7b09a7a23abd38..ab115cd15fdfbb2cc06182385b4a60b8521f617c 100644 (file)
 #include <linux/list.h>
 
 #include <asm/alternative.h>
+#include <asm/ftrace.h>
 
-#define CALL_BACK              5
 
 /* Long is fine, even if it is only 4 bytes ;-) */
 static long *ftrace_nop;
 
-struct ftrace_record {
-       struct dyn_ftrace       rec;
-       int                     failed;
-} __attribute__((packed));
-
-struct ftrace_page {
-       struct ftrace_page      *next;
-       int                     index;
-       struct ftrace_record    records[];
-} __attribute__((packed));
-
-#define ENTRIES_PER_PAGE \
-  ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct ftrace_record))
-
-/* estimate from running different kernels */
-#define NR_TO_INIT             10000
-
-#define MCOUNT_ADDR ((long)(&mcount))
-
 union ftrace_code_union {
-       char code[5];
+       char code[MCOUNT_INSN_SIZE];
        struct {
                char e8;
                int offset;
        } __attribute__((packed));
 };
 
-static struct ftrace_page      *ftrace_pages_start;
-static struct ftrace_page      *ftrace_pages;
 
-notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip)
+static int notrace ftrace_calc_offset(long ip, long addr)
 {
-       struct ftrace_record *rec;
-       unsigned long save;
-
-       ip -= CALL_BACK;
-       save = *(long *)ip;
+       return (int)(addr - ip);
+}
 
-       /* If this was already converted, skip it */
-       if (save == *ftrace_nop)
-               return NULL;
+notrace unsigned char *ftrace_nop_replace(void)
+{
+       return (char *)ftrace_nop;
+}
 
-       if (ftrace_pages->index == ENTRIES_PER_PAGE) {
-               if (!ftrace_pages->next)
-                       return NULL;
-               ftrace_pages = ftrace_pages->next;
-       }
+notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+       static union ftrace_code_union calc;
 
-       rec = &ftrace_pages->records[ftrace_pages->index++];
+       calc.e8         = 0xe8;
+       calc.offset     = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr);
 
-       return &rec->rec;
+       /*
+        * No locking needed, this must be called via kstop_machine
+        * which in essence is like running on a uniprocessor machine.
+        */
+       return calc.code;
 }
 
-static int notrace
+notrace int
 ftrace_modify_code(unsigned long ip, unsigned char *old_code,
                   unsigned char *new_code)
 {
@@ -101,12 +81,12 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
                "   movb %b4, 4(%2)\n"
                "2:\n"
                ".section .fixup, \"ax\"\n"
-               "       movl $1, %0\n"
-               "3:     jmp 2b\n"
+               "3:     movl $1, %0\n"
+               "       jmp 2b\n"
                ".previous\n"
                _ASM_EXTABLE(1b, 3b)
                : "=r"(faulted), "=a"(replaced)
-               : "r"(ip), "r"(new), "r"(newch),
+               : "r"(ip), "r"(new), "c"(newch),
                  "0"(faulted), "a"(old)
                : "memory");
        sync_core();
@@ -117,129 +97,45 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
        return faulted;
 }
 
-static int notrace ftrace_calc_offset(long ip)
+notrace int ftrace_update_ftrace_func(ftrace_func_t func)
 {
-       return (int)(MCOUNT_ADDR - ip);
-}
-
-notrace void ftrace_code_disable(struct dyn_ftrace *rec)
-{
-       unsigned long ip;
-       union ftrace_code_union save;
-       struct ftrace_record *r =
-               container_of(rec, struct ftrace_record, rec);
-
-       ip = rec->ip;
-
-       save.e8         = 0xe8;
-       save.offset     = ftrace_calc_offset(ip);
-
-       /* move the IP back to the start of the call */
-       ip -= CALL_BACK;
-
-       r->failed = ftrace_modify_code(ip, save.code, (char *)ftrace_nop);
-}
-
-static void notrace ftrace_replace_code(int saved)
-{
-       unsigned char *new = NULL, *old = NULL;
-       struct ftrace_record *rec;
-       struct ftrace_page *pg;
-       unsigned long ip;
-       int i;
-
-       if (saved)
-               old = (char *)ftrace_nop;
-       else
-               new = (char *)ftrace_nop;
+       unsigned long ip = (unsigned long)(&ftrace_call);
+       unsigned char old[MCOUNT_INSN_SIZE], *new;
+       int ret;
 
-       for (pg = ftrace_pages_start; pg; pg = pg->next) {
-               for (i = 0; i < pg->index; i++) {
-                       union ftrace_code_union calc;
-                       rec = &pg->records[i];
-
-                       /* don't modify code that has already faulted */
-                       if (rec->failed)
-                               continue;
-
-                       ip = rec->rec.ip;
-
-                       calc.e8         = 0xe8;
-                       calc.offset     = ftrace_calc_offset(ip);
-
-                       if (saved)
-                               new = calc.code;
-                       else
-                               old = calc.code;
-
-                       ip -= CALL_BACK;
-
-                       rec->failed = ftrace_modify_code(ip, old, new);
-               }
-       }
+       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 void ftrace_startup_code(void)
+notrace int ftrace_mcount_set(unsigned long *data)
 {
-       ftrace_replace_code(1);
-}
-
-notrace void ftrace_shutdown_code(void)
-{
-       ftrace_replace_code(0);
-}
+       unsigned long ip = (long)(&mcount_call);
+       unsigned long *addr = data;
+       unsigned char old[MCOUNT_INSN_SIZE], *new;
 
-notrace void ftrace_shutdown_replenish(void)
-{
-       if (ftrace_pages->next)
-               return;
+       /*
+        * 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);
 
-       /* allocate another page */
-       ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL);
+       return 0;
 }
 
-notrace int __init ftrace_shutdown_arch_init(void)
+int __init ftrace_dyn_arch_init(void *data)
 {
        const unsigned char *const *noptable = find_nop_table();
-       struct ftrace_page *pg;
-       int cnt;
-       int i;
-
-       ftrace_nop = (unsigned long *)noptable[CALL_BACK];
-
-       /* allocate a few pages */
-       ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL);
-       if (!ftrace_pages_start)
-               return -1;
-
-       /*
-        * Allocate a few more pages.
-        *
-        * TODO: have some parser search vmlinux before
-        *   final linking to find all calls to ftrace.
-        *   Then we can:
-        *    a) know how many pages to allocate.
-        *     and/or
-        *    b) set up the table then.
-        *
-        *  The dynamic code is still necessary for
-        *  modules.
-        */
-
-       pg = ftrace_pages = ftrace_pages_start;
-
-       cnt = NR_TO_INIT / ENTRIES_PER_PAGE;
 
-       for (i = 0; i < cnt; i++) {
-               pg->next = (void *)get_zeroed_page(GFP_KERNEL);
+       /* This is running in kstop_machine */
 
-               /* If we fail, we'll try later anyway */
-               if (!pg->next)
-                       break;
+       ftrace_mcount_set(data);
 
-               pg = pg->next;
-       }
+       ftrace_nop = (unsigned long *)noptable[MCOUNT_INSN_SIZE];
 
        return 0;
 }