#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)
{
" 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();
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;
}