aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2008-05-12 15:20:43 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-05-23 14:33:35 -0400
commit3c1720f00bb619302ba19d55986ab565e74d06db (patch)
treed58aaa54bc8e7a465597f385de36204c3b0b9cf8
parentdfa60aba04dae7833d75b2e2be124bb7cfb8239f (diff)
ftrace: move memory management out of arch code
This patch moves the memory management of the ftrace records out of the arch code and into the generic code making the arch code simpler. Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/kernel/ftrace.c183
-rw-r--r--include/linux/ftrace.h18
-rw-r--r--kernel/trace/ftrace.c154
3 files changed, 192 insertions, 163 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 2e060c58b860..b69795efa226 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -23,25 +23,6 @@
23/* Long is fine, even if it is only 4 bytes ;-) */ 23/* Long is fine, even if it is only 4 bytes ;-) */
24static long *ftrace_nop; 24static long *ftrace_nop;
25 25
26struct ftrace_record {
27 struct dyn_ftrace rec;
28 int failed;
29} __attribute__((packed));
30
31struct ftrace_page {
32 struct ftrace_page *next;
33 int index;
34 struct ftrace_record records[];
35} __attribute__((packed));
36
37#define ENTRIES_PER_PAGE \
38 ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct ftrace_record))
39
40/* estimate from running different kernels */
41#define NR_TO_INIT 10000
42
43#define MCOUNT_ADDR ((long)(&mcount))
44
45union ftrace_code_union { 26union ftrace_code_union {
46 char code[5]; 27 char code[5];
47 struct { 28 struct {
@@ -50,33 +31,41 @@ union ftrace_code_union {
50 } __attribute__((packed)); 31 } __attribute__((packed));
51}; 32};
52 33
53static struct ftrace_page *ftrace_pages_start; 34notrace int ftrace_ip_converted(unsigned long ip)
54static struct ftrace_page *ftrace_pages;
55
56notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip)
57{ 35{
58 struct ftrace_record *rec;
59 unsigned long save; 36 unsigned long save;
60 37
61 ip -= CALL_BACK; 38 ip -= CALL_BACK;
62 save = *(long *)ip; 39 save = *(long *)ip;
63 40
64 /* If this was already converted, skip it */ 41 return save == *ftrace_nop;
65 if (save == *ftrace_nop) 42}
66 return NULL;
67 43
68 if (ftrace_pages->index == ENTRIES_PER_PAGE) { 44static int notrace ftrace_calc_offset(long ip, long addr)
69 if (!ftrace_pages->next) 45{
70 return NULL; 46 return (int)(addr - ip);
71 ftrace_pages = ftrace_pages->next; 47}
72 }
73 48
74 rec = &ftrace_pages->records[ftrace_pages->index++]; 49notrace unsigned char *ftrace_nop_replace(void)
50{
51 return (char *)ftrace_nop;
52}
53
54notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
55{
56 static union ftrace_code_union calc;
75 57
76 return &rec->rec; 58 calc.e8 = 0xe8;
59 calc.offset = ftrace_calc_offset(ip, addr);
60
61 /*
62 * No locking needed, this must be called via kstop_machine
63 * which in essence is like running on a uniprocessor machine.
64 */
65 return calc.code;
77} 66}
78 67
79static int notrace 68notrace int
80ftrace_modify_code(unsigned long ip, unsigned char *old_code, 69ftrace_modify_code(unsigned long ip, unsigned char *old_code,
81 unsigned char *new_code) 70 unsigned char *new_code)
82{ 71{
@@ -86,6 +75,9 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
86 unsigned char newch = new_code[4]; 75 unsigned char newch = new_code[4];
87 int faulted = 0; 76 int faulted = 0;
88 77
78 /* move the IP back to the start of the call */
79 ip -= CALL_BACK;
80
89 /* 81 /*
90 * Note: Due to modules and __init, code can 82 * Note: Due to modules and __init, code can
91 * disappear and change, we need to protect against faulting 83 * disappear and change, we need to protect against faulting
@@ -117,129 +109,12 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
117 return faulted; 109 return faulted;
118} 110}
119 111
120static int notrace ftrace_calc_offset(long ip) 112int __init ftrace_dyn_arch_init(void)
121{
122 return (int)(MCOUNT_ADDR - ip);
123}
124
125notrace void ftrace_code_disable(struct dyn_ftrace *rec)
126{
127 unsigned long ip;
128 union ftrace_code_union save;
129 struct ftrace_record *r =
130 container_of(rec, struct ftrace_record, rec);
131
132 ip = rec->ip;
133
134 save.e8 = 0xe8;
135 save.offset = ftrace_calc_offset(ip);
136
137 /* move the IP back to the start of the call */
138 ip -= CALL_BACK;
139
140 r->failed = ftrace_modify_code(ip, save.code, (char *)ftrace_nop);
141}
142
143static void notrace ftrace_replace_code(int saved)
144{
145 unsigned char *new = NULL, *old = NULL;
146 struct ftrace_record *rec;
147 struct ftrace_page *pg;
148 unsigned long ip;
149 int i;
150
151 if (saved)
152 old = (char *)ftrace_nop;
153 else
154 new = (char *)ftrace_nop;
155
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];
160
161 /* don't modify code that has already faulted */
162 if (rec->failed)
163 continue;
164
165 ip = rec->rec.ip;
166
167 calc.e8 = 0xe8;
168 calc.offset = ftrace_calc_offset(ip);
169
170 if (saved)
171 new = calc.code;
172 else
173 old = calc.code;
174
175 ip -= CALL_BACK;
176
177 rec->failed = ftrace_modify_code(ip, old, new);
178 }
179 }
180
181}
182
183notrace void ftrace_startup_code(void)
184{
185 ftrace_replace_code(1);
186}
187
188notrace void ftrace_shutdown_code(void)
189{
190 ftrace_replace_code(0);
191}
192
193notrace void ftrace_shutdown_replenish(void)
194{
195 if (ftrace_pages->next)
196 return;
197
198 /* allocate another page */
199 ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL);
200}
201
202notrace int __init ftrace_shutdown_arch_init(void)
203{ 113{
204 const unsigned char *const *noptable = find_nop_table(); 114 const unsigned char *const *noptable = find_nop_table();
205 struct ftrace_page *pg;
206 int cnt;
207 int i;
208 115
209 ftrace_nop = (unsigned long *)noptable[CALL_BACK]; 116 ftrace_nop = (unsigned long *)noptable[CALL_BACK];
210 117
211 /* allocate a few pages */
212 ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL);
213 if (!ftrace_pages_start)
214 return -1;
215
216 /*
217 * Allocate a few more pages.
218 *
219 * TODO: have some parser search vmlinux before
220 * final linking to find all calls to ftrace.
221 * Then we can:
222 * a) know how many pages to allocate.
223 * and/or
224 * b) set up the table then.
225 *
226 * The dynamic code is still necessary for
227 * modules.
228 */
229
230 pg = ftrace_pages = ftrace_pages_start;
231
232 cnt = NR_TO_INIT / ENTRIES_PER_PAGE;
233
234 for (i = 0; i < cnt; i++) {
235 pg->next = (void *)get_zeroed_page(GFP_KERNEL);
236
237 /* If we fail, we'll try later anyway */
238 if (!pg->next)
239 break;
240
241 pg = pg->next;
242 }
243
244 return 0; 118 return 0;
245} 119}
120
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index ccd8537dbdb7..d509ad6c9cb8 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -42,19 +42,23 @@ extern void mcount(void);
42# define FTRACE_HASHBITS 10 42# define FTRACE_HASHBITS 10
43# define FTRACE_HASHSIZE (1<<FTRACE_HASHBITS) 43# define FTRACE_HASHSIZE (1<<FTRACE_HASHBITS)
44 44
45enum {
46 FTRACE_FL_FAILED = (1<<0),
47};
48
45struct dyn_ftrace { 49struct dyn_ftrace {
46 struct hlist_node node; 50 struct hlist_node node;
47 unsigned long ip; 51 unsigned long ip;
52 unsigned long flags;
48}; 53};
49 54
50/* defined in arch */ 55/* defined in arch */
51extern struct dyn_ftrace * 56extern int ftrace_ip_converted(unsigned long ip);
52ftrace_alloc_shutdown_node(unsigned long ip); 57extern unsigned char *ftrace_nop_replace(void);
53extern int ftrace_shutdown_arch_init(void); 58extern unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr);
54extern void ftrace_code_disable(struct dyn_ftrace *rec); 59extern int ftrace_dyn_arch_init(void);
55extern void ftrace_startup_code(void); 60extern int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
56extern void ftrace_shutdown_code(void); 61 unsigned char *new_code);
57extern void ftrace_shutdown_replenish(void);
58#endif 62#endif
59 63
60#ifdef CONFIG_FRAME_POINTER 64#ifdef CONFIG_FRAME_POINTER
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index d3de37299ba4..f6d9af3bf66b 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -156,6 +156,21 @@ static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu);
156static DEFINE_SPINLOCK(ftrace_shutdown_lock); 156static DEFINE_SPINLOCK(ftrace_shutdown_lock);
157static DEFINE_MUTEX(ftraced_lock); 157static DEFINE_MUTEX(ftraced_lock);
158 158
159struct ftrace_page {
160 struct ftrace_page *next;
161 int index;
162 struct dyn_ftrace records[];
163} __attribute__((packed));
164
165#define ENTRIES_PER_PAGE \
166 ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct dyn_ftrace))
167
168/* estimate from running different kernels */
169#define NR_TO_INIT 10000
170
171static struct ftrace_page *ftrace_pages_start;
172static struct ftrace_page *ftrace_pages;
173
159static int ftraced_trigger; 174static int ftraced_trigger;
160static int ftraced_suspend; 175static int ftraced_suspend;
161 176
@@ -184,6 +199,21 @@ ftrace_add_hash(struct dyn_ftrace *node, unsigned long key)
184 hlist_add_head(&node->node, &ftrace_hash[key]); 199 hlist_add_head(&node->node, &ftrace_hash[key]);
185} 200}
186 201
202static notrace struct dyn_ftrace *ftrace_alloc_shutdown_node(unsigned long ip)
203{
204 /* If this was already converted, skip it */
205 if (ftrace_ip_converted(ip))
206 return NULL;
207
208 if (ftrace_pages->index == ENTRIES_PER_PAGE) {
209 if (!ftrace_pages->next)
210 return NULL;
211 ftrace_pages = ftrace_pages->next;
212 }
213
214 return &ftrace_pages->records[ftrace_pages->index++];
215}
216
187static void notrace 217static void notrace
188ftrace_record_ip(unsigned long ip, unsigned long parent_ip) 218ftrace_record_ip(unsigned long ip, unsigned long parent_ip)
189{ 219{
@@ -252,6 +282,62 @@ static struct ftrace_ops ftrace_shutdown_ops __read_mostly =
252 .func = ftrace_record_ip, 282 .func = ftrace_record_ip,
253}; 283};
254 284
285#define MCOUNT_ADDR ((long)(&mcount))
286
287static void notrace ftrace_replace_code(int saved)
288{
289 unsigned char *new = NULL, *old = NULL;
290 struct dyn_ftrace *rec;
291 struct ftrace_page *pg;
292 unsigned long ip;
293 int failed;
294 int i;
295
296 if (saved)
297 old = ftrace_nop_replace();
298 else
299 new = ftrace_nop_replace();
300
301 for (pg = ftrace_pages_start; pg; pg = pg->next) {
302 for (i = 0; i < pg->index; i++) {
303 rec = &pg->records[i];
304
305 /* don't modify code that has already faulted */
306 if (rec->flags & FTRACE_FL_FAILED)
307 continue;
308
309 ip = rec->ip;
310
311 if (saved)
312 new = ftrace_call_replace(ip, MCOUNT_ADDR);
313 else
314 old = ftrace_call_replace(ip, MCOUNT_ADDR);
315
316 failed = ftrace_modify_code(ip, old, new);
317 if (failed)
318 rec->flags |= FTRACE_FL_FAILED;
319 }
320 }
321}
322
323static notrace void ftrace_startup_code(void)
324{
325 ftrace_replace_code(1);
326}
327
328static notrace void ftrace_shutdown_code(void)
329{
330 ftrace_replace_code(0);
331}
332
333static notrace void ftrace_shutdown_replenish(void)
334{
335 if (ftrace_pages->next)
336 return;
337
338 /* allocate another page */
339 ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL);
340}
255 341
256static int notrace __ftrace_modify_code(void *data) 342static int notrace __ftrace_modify_code(void *data)
257{ 343{
@@ -261,6 +347,23 @@ static int notrace __ftrace_modify_code(void *data)
261 return 0; 347 return 0;
262} 348}
263 349
350static notrace void
351ftrace_code_disable(struct dyn_ftrace *rec, unsigned long addr)
352{
353 unsigned long ip;
354 unsigned char *nop, *call;
355 int failed;
356
357 ip = rec->ip;
358
359 nop = ftrace_nop_replace();
360 call = ftrace_call_replace(ip, addr);
361
362 failed = ftrace_modify_code(ip, call, nop);
363 if (failed)
364 rec->flags |= FTRACE_FL_FAILED;
365}
366
264static void notrace ftrace_run_startup_code(void) 367static void notrace ftrace_run_startup_code(void)
265{ 368{
266 stop_machine_run(__ftrace_modify_code, ftrace_startup_code, NR_CPUS); 369 stop_machine_run(__ftrace_modify_code, ftrace_startup_code, NR_CPUS);
@@ -346,7 +449,7 @@ static int notrace __ftrace_update_code(void *ignore)
346 449
347 /* all CPUS are stopped, we are safe to modify code */ 450 /* all CPUS are stopped, we are safe to modify code */
348 hlist_for_each_entry(p, t, &head, node) { 451 hlist_for_each_entry(p, t, &head, node) {
349 ftrace_code_disable(p); 452 ftrace_code_disable(p, MCOUNT_ADDR);
350 ftrace_update_cnt++; 453 ftrace_update_cnt++;
351 } 454 }
352 455
@@ -407,12 +510,59 @@ static int notrace ftraced(void *ignore)
407 return 0; 510 return 0;
408} 511}
409 512
513static int __init ftrace_dyn_table_alloc(void)
514{
515 struct ftrace_page *pg;
516 int cnt;
517 int i;
518 int ret;
519
520 ret = ftrace_dyn_arch_init();
521 if (ret)
522 return ret;
523
524 /* allocate a few pages */
525 ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL);
526 if (!ftrace_pages_start)
527 return -1;
528
529 /*
530 * Allocate a few more pages.
531 *
532 * TODO: have some parser search vmlinux before
533 * final linking to find all calls to ftrace.
534 * Then we can:
535 * a) know how many pages to allocate.
536 * and/or
537 * b) set up the table then.
538 *
539 * The dynamic code is still necessary for
540 * modules.
541 */
542
543 pg = ftrace_pages = ftrace_pages_start;
544
545 cnt = NR_TO_INIT / ENTRIES_PER_PAGE;
546
547 for (i = 0; i < cnt; i++) {
548 pg->next = (void *)get_zeroed_page(GFP_KERNEL);
549
550 /* If we fail, we'll try later anyway */
551 if (!pg->next)
552 break;
553
554 pg = pg->next;
555 }
556
557 return 0;
558}
559
410static int __init notrace ftrace_shutdown_init(void) 560static int __init notrace ftrace_shutdown_init(void)
411{ 561{
412 struct task_struct *p; 562 struct task_struct *p;
413 int ret; 563 int ret;
414 564
415 ret = ftrace_shutdown_arch_init(); 565 ret = ftrace_dyn_table_alloc();
416 if (ret) 566 if (ret)
417 return ret; 567 return ret;
418 568