2 * Code for replacing ftrace calls with jumps.
4 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
6 * Thanks goes to Ingo Molnar, for suggesting the idea.
7 * Mathieu Desnoyers, for suggesting postponing the modifications.
8 * Arjan van de Ven, for keeping me straight, and explaining to me
9 * the dangers of modifying code on the run.
12 #include <linux/spinlock.h>
13 #include <linux/hardirq.h>
14 #include <linux/ftrace.h>
15 #include <linux/percpu.h>
16 #include <linux/init.h>
17 #include <linux/list.h>
19 #include <asm/alternative.h>
23 /* Long is fine, even if it is only 4 bytes ;-) */
24 static long *ftrace_nop;
26 struct ftrace_record {
27 struct dyn_ftrace rec;
29 } __attribute__((packed));
32 struct ftrace_page *next;
34 struct ftrace_record records[];
35 } __attribute__((packed));
37 #define ENTRIES_PER_PAGE \
38 ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct ftrace_record))
40 /* estimate from running different kernels */
41 #define NR_TO_INIT 10000
43 #define MCOUNT_ADDR ((long)(&mcount))
45 union ftrace_code_union {
50 } __attribute__((packed));
53 static struct ftrace_page *ftrace_pages_start;
54 static struct ftrace_page *ftrace_pages;
56 notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip)
58 struct ftrace_record *rec;
64 /* If this was already converted, skip it */
65 if (save == *ftrace_nop)
68 if (ftrace_pages->index == ENTRIES_PER_PAGE) {
69 if (!ftrace_pages->next)
71 ftrace_pages = ftrace_pages->next;
74 rec = &ftrace_pages->records[ftrace_pages->index++];
80 ftrace_modify_code(unsigned long ip, unsigned char *old_code,
81 unsigned char *new_code)
84 unsigned old = *(unsigned *)old_code; /* 4 bytes */
85 unsigned new = *(unsigned *)new_code; /* 4 bytes */
86 unsigned char newch = new_code[4];
90 * Note: Due to modules and __init, code can
91 * disappear and change, we need to protect against faulting
92 * as well as code changing.
94 * No real locking needed, this code is run through
103 ".section .fixup, \"ax\"\n"
108 : "=r"(faulted), "=a"(replaced)
109 : "r"(ip), "r"(new), "r"(newch),
110 "0"(faulted), "a"(old)
114 if (replaced != old && replaced != new)
120 static int notrace ftrace_calc_offset(long ip)
122 return (int)(MCOUNT_ADDR - ip);
125 notrace void ftrace_code_disable(struct dyn_ftrace *rec)
128 union ftrace_code_union save;
129 struct ftrace_record *r =
130 container_of(rec, struct ftrace_record, rec);
135 save.offset = ftrace_calc_offset(ip);
137 /* move the IP back to the start of the call */
140 r->failed = ftrace_modify_code(ip, save.code, (char *)ftrace_nop);
143 static void notrace ftrace_replace_code(int saved)
145 unsigned char *new = NULL, *old = NULL;
146 struct ftrace_record *rec;
147 struct ftrace_page *pg;
152 old = (char *)ftrace_nop;
154 new = (char *)ftrace_nop;
156 for (pg = ftrace_pages_start; pg; pg = pg->next) {
157 for (i = 0; i < pg->index; i++) {
158 union ftrace_code_union calc;
159 rec = &pg->records[i];
161 /* don't modify code that has already faulted */
168 calc.offset = ftrace_calc_offset(ip);
177 rec->failed = ftrace_modify_code(ip, old, new);
183 notrace void ftrace_startup_code(void)
185 ftrace_replace_code(1);
188 notrace void ftrace_shutdown_code(void)
190 ftrace_replace_code(0);
193 notrace void ftrace_shutdown_replenish(void)
195 if (ftrace_pages->next)
198 /* allocate another page */
199 ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL);
202 notrace int __init ftrace_shutdown_arch_init(void)
204 const unsigned char *const *noptable = find_nop_table();
205 struct ftrace_page *pg;
209 ftrace_nop = (unsigned long *)noptable[CALL_BACK];
211 /* allocate a few pages */
212 ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL);
213 if (!ftrace_pages_start)
217 * Allocate a few more pages.
219 * TODO: have some parser search vmlinux before
220 * final linking to find all calls to ftrace.
222 * a) know how many pages to allocate.
224 * b) set up the table then.
226 * The dynamic code is still necessary for
230 pg = ftrace_pages = ftrace_pages_start;
232 cnt = NR_TO_INIT / ENTRIES_PER_PAGE;
234 for (i = 0; i < cnt; i++) {
235 pg->next = (void *)get_zeroed_page(GFP_KERNEL);
237 /* If we fail, we'll try later anyway */