aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2008-11-14 19:21:19 -0500
committerIngo Molnar <mingo@elte.hu>2008-11-16 01:36:02 -0500
commit31e889098a80ceb3e9e3c555d522b2686a6663c6 (patch)
tree2acb73507de4191d4a9aa5ccf08fa24e7044c89e
parentd51ad7ac48f991c4a8834485727efa99a691cb87 (diff)
ftrace: pass module struct to arch dynamic ftrace functions
Impact: allow archs more flexibility on dynamic ftrace implementations Dynamic ftrace has largly been developed on x86. Since x86 does not have the same limitations as other architectures, the ftrace interaction between the generic code and the architecture specific code was not flexible enough to handle some of the issues that other architectures have. Most notably, module trampolines. Due to the limited branch distance that archs make in calling kernel core code from modules, the module load code must create a trampoline to jump to what will make the larger jump into core kernel code. The problem arises when this happens to a call to mcount. Ftrace checks all code before modifying it and makes sure the current code is what it expects. Right now, there is not enough information to handle modifying module trampolines. This patch changes the API between generic dynamic ftrace code and the arch dependent code. There is now two functions for modifying code: ftrace_make_nop(mod, rec, addr) - convert the code at rec->ip into a nop, where the original text is calling addr. (mod is the module struct if called by module init) ftrace_make_caller(rec, addr) - convert the code rec->ip that should be a nop into a caller to addr. The record "rec" now has a new field called "arch" where the architecture can add any special attributes to each call site record. Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--arch/x86/include/asm/ftrace.h8
-rw-r--r--arch/x86/kernel/ftrace.c29
-rw-r--r--include/linux/ftrace.h53
-rw-r--r--kernel/module.c2
-rw-r--r--kernel/trace/ftrace.c62
5 files changed, 100 insertions, 54 deletions
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 9b6a1fa19e70..2bb43b433e07 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -17,6 +17,14 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
17 */ 17 */
18 return addr - 1; 18 return addr - 1;
19} 19}
20
21#ifdef CONFIG_DYNAMIC_FTRACE
22
23struct dyn_arch_ftrace {
24 /* No extra data needed for x86 */
25};
26
27#endif /* CONFIG_DYNAMIC_FTRACE */
20#endif /* __ASSEMBLY__ */ 28#endif /* __ASSEMBLY__ */
21#endif /* CONFIG_FUNCTION_TRACER */ 29#endif /* CONFIG_FUNCTION_TRACER */
22 30
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index fe832738e1e2..762222ad1387 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -166,7 +166,7 @@ static int ftrace_calc_offset(long ip, long addr)
166 return (int)(addr - ip); 166 return (int)(addr - ip);
167} 167}
168 168
169unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) 169static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
170{ 170{
171 static union ftrace_code_union calc; 171 static union ftrace_code_union calc;
172 172
@@ -311,12 +311,12 @@ do_ftrace_mod_code(unsigned long ip, void *new_code)
311 311
312static unsigned char ftrace_nop[MCOUNT_INSN_SIZE]; 312static unsigned char ftrace_nop[MCOUNT_INSN_SIZE];
313 313
314unsigned char *ftrace_nop_replace(void) 314static unsigned char *ftrace_nop_replace(void)
315{ 315{
316 return ftrace_nop; 316 return ftrace_nop;
317} 317}
318 318
319int 319static int
320ftrace_modify_code(unsigned long ip, unsigned char *old_code, 320ftrace_modify_code(unsigned long ip, unsigned char *old_code,
321 unsigned char *new_code) 321 unsigned char *new_code)
322{ 322{
@@ -349,6 +349,29 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
349 return 0; 349 return 0;
350} 350}
351 351
352int ftrace_make_nop(struct module *mod,
353 struct dyn_ftrace *rec, unsigned long addr)
354{
355 unsigned char *new, *old;
356 unsigned long ip = rec->ip;
357
358 old = ftrace_call_replace(ip, addr);
359 new = ftrace_nop_replace();
360
361 return ftrace_modify_code(rec->ip, old, new);
362}
363
364int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
365{
366 unsigned char *new, *old;
367 unsigned long ip = rec->ip;
368
369 old = ftrace_nop_replace();
370 new = ftrace_call_replace(ip, addr);
371
372 return ftrace_modify_code(rec->ip, old, new);
373}
374
352int ftrace_update_ftrace_func(ftrace_func_t func) 375int ftrace_update_ftrace_func(ftrace_func_t func)
353{ 376{
354 unsigned long ip = (unsigned long)(&ftrace_call); 377 unsigned long ip = (unsigned long)(&ftrace_call);
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 4fbc4a8b86a5..166a2070ef65 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -74,6 +74,9 @@ static inline void ftrace_start(void) { }
74#endif /* CONFIG_FUNCTION_TRACER */ 74#endif /* CONFIG_FUNCTION_TRACER */
75 75
76#ifdef CONFIG_DYNAMIC_FTRACE 76#ifdef CONFIG_DYNAMIC_FTRACE
77/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
78#include <asm/ftrace.h>
79
77enum { 80enum {
78 FTRACE_FL_FREE = (1 << 0), 81 FTRACE_FL_FREE = (1 << 0),
79 FTRACE_FL_FAILED = (1 << 1), 82 FTRACE_FL_FAILED = (1 << 1),
@@ -88,6 +91,7 @@ struct dyn_ftrace {
88 struct list_head list; 91 struct list_head list;
89 unsigned long ip; /* address of mcount call-site */ 92 unsigned long ip; /* address of mcount call-site */
90 unsigned long flags; 93 unsigned long flags;
94 struct dyn_arch_ftrace arch;
91}; 95};
92 96
93int ftrace_force_update(void); 97int ftrace_force_update(void);
@@ -95,22 +99,40 @@ void ftrace_set_filter(unsigned char *buf, int len, int reset);
95 99
96/* defined in arch */ 100/* defined in arch */
97extern int ftrace_ip_converted(unsigned long ip); 101extern int ftrace_ip_converted(unsigned long ip);
98extern unsigned char *ftrace_nop_replace(void);
99extern unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr);
100extern int ftrace_dyn_arch_init(void *data); 102extern int ftrace_dyn_arch_init(void *data);
101extern int ftrace_update_ftrace_func(ftrace_func_t func); 103extern int ftrace_update_ftrace_func(ftrace_func_t func);
102extern void ftrace_caller(void); 104extern void ftrace_caller(void);
103extern void ftrace_call(void); 105extern void ftrace_call(void);
104extern void mcount_call(void); 106extern void mcount_call(void);
105 107
106/* May be defined in arch */ 108/**
107extern int ftrace_arch_read_dyn_info(char *buf, int size); 109 * ftrace_make_nop - convert code into top
110 * @mod: module structure if called by module load initialization
111 * @rec: the mcount call site record
112 * @addr: the address that the call site should be calling
113 *
114 * This is a very sensitive operation and great care needs
115 * to be taken by the arch. The operation should carefully
116 * read the location, check to see if what is read is indeed
117 * what we expect it to be, and then on success of the compare,
118 * it should write to the location.
119 *
120 * The code segment at @rec->ip should be a caller to @addr
121 *
122 * Return must be:
123 * 0 on success
124 * -EFAULT on error reading the location
125 * -EINVAL on a failed compare of the contents
126 * -EPERM on error writing to the location
127 * Any other value will be considered a failure.
128 */
129extern int ftrace_make_nop(struct module *mod,
130 struct dyn_ftrace *rec, unsigned long addr);
108 131
109/** 132/**
110 * ftrace_modify_code - modify code segment 133 * ftrace_make_call - convert a nop call site into a call to addr
111 * @ip: the address of the code segment 134 * @rec: the mcount call site record
112 * @old_code: the contents of what is expected to be there 135 * @addr: the address that the call site should call
113 * @new_code: the code to patch in
114 * 136 *
115 * This is a very sensitive operation and great care needs 137 * This is a very sensitive operation and great care needs
116 * to be taken by the arch. The operation should carefully 138 * to be taken by the arch. The operation should carefully
@@ -118,6 +140,8 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size);
118 * what we expect it to be, and then on success of the compare, 140 * what we expect it to be, and then on success of the compare,
119 * it should write to the location. 141 * it should write to the location.
120 * 142 *
143 * The code segment at @rec->ip should be a nop
144 *
121 * Return must be: 145 * Return must be:
122 * 0 on success 146 * 0 on success
123 * -EFAULT on error reading the location 147 * -EFAULT on error reading the location
@@ -125,8 +149,11 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size);
125 * -EPERM on error writing to the location 149 * -EPERM on error writing to the location
126 * Any other value will be considered a failure. 150 * Any other value will be considered a failure.
127 */ 151 */
128extern int ftrace_modify_code(unsigned long ip, unsigned char *old_code, 152extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr);
129 unsigned char *new_code); 153
154
155/* May be defined in arch */
156extern int ftrace_arch_read_dyn_info(char *buf, int size);
130 157
131extern int skip_trace(unsigned long ip); 158extern int skip_trace(unsigned long ip);
132 159
@@ -259,11 +286,13 @@ static inline void ftrace_dump(void) { }
259 286
260#ifdef CONFIG_FTRACE_MCOUNT_RECORD 287#ifdef CONFIG_FTRACE_MCOUNT_RECORD
261extern void ftrace_init(void); 288extern void ftrace_init(void);
262extern void ftrace_init_module(unsigned long *start, unsigned long *end); 289extern void ftrace_init_module(struct module *mod,
290 unsigned long *start, unsigned long *end);
263#else 291#else
264static inline void ftrace_init(void) { } 292static inline void ftrace_init(void) { }
265static inline void 293static inline void
266ftrace_init_module(unsigned long *start, unsigned long *end) { } 294ftrace_init_module(struct module *mod,
295 unsigned long *start, unsigned long *end) { }
267#endif 296#endif
268 297
269 298
diff --git a/kernel/module.c b/kernel/module.c
index 1f4cc00e0c20..69791274e899 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2201,7 +2201,7 @@ static noinline struct module *load_module(void __user *umod,
2201 /* sechdrs[0].sh_size is always zero */ 2201 /* sechdrs[0].sh_size is always zero */
2202 mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc", 2202 mseg = section_objs(hdr, sechdrs, secstrings, "__mcount_loc",
2203 sizeof(*mseg), &num_mcount); 2203 sizeof(*mseg), &num_mcount);
2204 ftrace_init_module(mseg, mseg + num_mcount); 2204 ftrace_init_module(mod, mseg, mseg + num_mcount);
2205 2205
2206 err = module_finalize(hdr, sechdrs, mod); 2206 err = module_finalize(hdr, sechdrs, mod);
2207 if (err < 0) 2207 if (err < 0)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 3940c71ac2a2..e9a5fbfce08e 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -358,9 +358,7 @@ static void print_ip_ins(const char *fmt, unsigned char *p)
358 printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]); 358 printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]);
359} 359}
360 360
361static void ftrace_bug(int failed, unsigned long ip, 361static void ftrace_bug(int failed, unsigned long ip)
362 unsigned char *expected,
363 unsigned char *replace)
364{ 362{
365 switch (failed) { 363 switch (failed) {
366 case -EFAULT: 364 case -EFAULT:
@@ -372,9 +370,7 @@ static void ftrace_bug(int failed, unsigned long ip,
372 FTRACE_WARN_ON_ONCE(1); 370 FTRACE_WARN_ON_ONCE(1);
373 pr_info("ftrace failed to modify "); 371 pr_info("ftrace failed to modify ");
374 print_ip_sym(ip); 372 print_ip_sym(ip);
375 print_ip_ins(" expected: ", expected);
376 print_ip_ins(" actual: ", (unsigned char *)ip); 373 print_ip_ins(" actual: ", (unsigned char *)ip);
377 print_ip_ins(" replace: ", replace);
378 printk(KERN_CONT "\n"); 374 printk(KERN_CONT "\n");
379 break; 375 break;
380 case -EPERM: 376 case -EPERM:
@@ -392,8 +388,7 @@ static void ftrace_bug(int failed, unsigned long ip,
392#define FTRACE_ADDR ((long)(ftrace_caller)) 388#define FTRACE_ADDR ((long)(ftrace_caller))
393 389
394static int 390static int
395__ftrace_replace_code(struct dyn_ftrace *rec, 391__ftrace_replace_code(struct dyn_ftrace *rec, int enable)
396 unsigned char *old, unsigned char *new, int enable)
397{ 392{
398 unsigned long ip, fl; 393 unsigned long ip, fl;
399 394
@@ -435,12 +430,10 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
435 * otherwise enable it! 430 * otherwise enable it!
436 */ 431 */
437 if (fl & FTRACE_FL_ENABLED) { 432 if (fl & FTRACE_FL_ENABLED) {
438 /* swap new and old */ 433 enable = 0;
439 new = old;
440 old = ftrace_call_replace(ip, FTRACE_ADDR);
441 rec->flags &= ~FTRACE_FL_ENABLED; 434 rec->flags &= ~FTRACE_FL_ENABLED;
442 } else { 435 } else {
443 new = ftrace_call_replace(ip, FTRACE_ADDR); 436 enable = 1;
444 rec->flags |= FTRACE_FL_ENABLED; 437 rec->flags |= FTRACE_FL_ENABLED;
445 } 438 }
446 } else { 439 } else {
@@ -453,10 +446,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
453 fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); 446 fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED);
454 if (fl == FTRACE_FL_NOTRACE) 447 if (fl == FTRACE_FL_NOTRACE)
455 return 0; 448 return 0;
456 449 }
457 new = ftrace_call_replace(ip, FTRACE_ADDR);
458 } else
459 old = ftrace_call_replace(ip, FTRACE_ADDR);
460 450
461 if (enable) { 451 if (enable) {
462 if (rec->flags & FTRACE_FL_ENABLED) 452 if (rec->flags & FTRACE_FL_ENABLED)
@@ -469,21 +459,18 @@ __ftrace_replace_code(struct dyn_ftrace *rec,
469 } 459 }
470 } 460 }
471 461
472 return ftrace_modify_code(ip, old, new); 462 if (enable)
463 return ftrace_make_call(rec, FTRACE_ADDR);
464 else
465 return ftrace_make_nop(NULL, rec, FTRACE_ADDR);
473} 466}
474 467
475static void ftrace_replace_code(int enable) 468static void ftrace_replace_code(int enable)
476{ 469{
477 int i, failed; 470 int i, failed;
478 unsigned char *new = NULL, *old = NULL;
479 struct dyn_ftrace *rec; 471 struct dyn_ftrace *rec;
480 struct ftrace_page *pg; 472 struct ftrace_page *pg;
481 473
482 if (enable)
483 old = ftrace_nop_replace();
484 else
485 new = ftrace_nop_replace();
486
487 for (pg = ftrace_pages_start; pg; pg = pg->next) { 474 for (pg = ftrace_pages_start; pg; pg = pg->next) {
488 for (i = 0; i < pg->index; i++) { 475 for (i = 0; i < pg->index; i++) {
489 rec = &pg->records[i]; 476 rec = &pg->records[i];
@@ -504,34 +491,30 @@ static void ftrace_replace_code(int enable)
504 unfreeze_record(rec); 491 unfreeze_record(rec);
505 } 492 }
506 493
507 failed = __ftrace_replace_code(rec, old, new, enable); 494 failed = __ftrace_replace_code(rec, enable);
508 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { 495 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
509 rec->flags |= FTRACE_FL_FAILED; 496 rec->flags |= FTRACE_FL_FAILED;
510 if ((system_state == SYSTEM_BOOTING) || 497 if ((system_state == SYSTEM_BOOTING) ||
511 !core_kernel_text(rec->ip)) { 498 !core_kernel_text(rec->ip)) {
512 ftrace_free_rec(rec); 499 ftrace_free_rec(rec);
513 } else 500 } else
514 ftrace_bug(failed, rec->ip, old, new); 501 ftrace_bug(failed, rec->ip);
515 } 502 }
516 } 503 }
517 } 504 }
518} 505}
519 506
520static int 507static int
521ftrace_code_disable(struct dyn_ftrace *rec) 508ftrace_code_disable(struct module *mod, struct dyn_ftrace *rec)
522{ 509{
523 unsigned long ip; 510 unsigned long ip;
524 unsigned char *nop, *call;
525 int ret; 511 int ret;
526 512
527 ip = rec->ip; 513 ip = rec->ip;
528 514
529 nop = ftrace_nop_replace(); 515 ret = ftrace_make_nop(mod, rec, mcount_addr);
530 call = ftrace_call_replace(ip, mcount_addr);
531
532 ret = ftrace_modify_code(ip, call, nop);
533 if (ret) { 516 if (ret) {
534 ftrace_bug(ret, ip, call, nop); 517 ftrace_bug(ret, ip);
535 rec->flags |= FTRACE_FL_FAILED; 518 rec->flags |= FTRACE_FL_FAILED;
536 return 0; 519 return 0;
537 } 520 }
@@ -650,7 +633,7 @@ static cycle_t ftrace_update_time;
650static unsigned long ftrace_update_cnt; 633static unsigned long ftrace_update_cnt;
651unsigned long ftrace_update_tot_cnt; 634unsigned long ftrace_update_tot_cnt;
652 635
653static int ftrace_update_code(void) 636static int ftrace_update_code(struct module *mod)
654{ 637{
655 struct dyn_ftrace *p, *t; 638 struct dyn_ftrace *p, *t;
656 cycle_t start, stop; 639 cycle_t start, stop;
@@ -667,7 +650,7 @@ static int ftrace_update_code(void)
667 list_del_init(&p->list); 650 list_del_init(&p->list);
668 651
669 /* convert record (i.e, patch mcount-call with NOP) */ 652 /* convert record (i.e, patch mcount-call with NOP) */
670 if (ftrace_code_disable(p)) { 653 if (ftrace_code_disable(mod, p)) {
671 p->flags |= FTRACE_FL_CONVERTED; 654 p->flags |= FTRACE_FL_CONVERTED;
672 ftrace_update_cnt++; 655 ftrace_update_cnt++;
673 } else 656 } else
@@ -1309,7 +1292,8 @@ static __init int ftrace_init_debugfs(void)
1309 1292
1310fs_initcall(ftrace_init_debugfs); 1293fs_initcall(ftrace_init_debugfs);
1311 1294
1312static int ftrace_convert_nops(unsigned long *start, 1295static int ftrace_convert_nops(struct module *mod,
1296 unsigned long *start,
1313 unsigned long *end) 1297 unsigned long *end)
1314{ 1298{
1315 unsigned long *p; 1299 unsigned long *p;
@@ -1325,18 +1309,19 @@ static int ftrace_convert_nops(unsigned long *start,
1325 1309
1326 /* disable interrupts to prevent kstop machine */ 1310 /* disable interrupts to prevent kstop machine */
1327 local_irq_save(flags); 1311 local_irq_save(flags);
1328 ftrace_update_code(); 1312 ftrace_update_code(mod);
1329 local_irq_restore(flags); 1313 local_irq_restore(flags);
1330 mutex_unlock(&ftrace_start_lock); 1314 mutex_unlock(&ftrace_start_lock);
1331 1315
1332 return 0; 1316 return 0;
1333} 1317}
1334 1318
1335void ftrace_init_module(unsigned long *start, unsigned long *end) 1319void ftrace_init_module(struct module *mod,
1320 unsigned long *start, unsigned long *end)
1336{ 1321{
1337 if (ftrace_disabled || start == end) 1322 if (ftrace_disabled || start == end)
1338 return; 1323 return;
1339 ftrace_convert_nops(start, end); 1324 ftrace_convert_nops(mod, start, end);
1340} 1325}
1341 1326
1342extern unsigned long __start_mcount_loc[]; 1327extern unsigned long __start_mcount_loc[];
@@ -1366,7 +1351,8 @@ void __init ftrace_init(void)
1366 1351
1367 last_ftrace_enabled = ftrace_enabled = 1; 1352 last_ftrace_enabled = ftrace_enabled = 1;
1368 1353
1369 ret = ftrace_convert_nops(__start_mcount_loc, 1354 ret = ftrace_convert_nops(NULL,
1355 __start_mcount_loc,
1370 __stop_mcount_loc); 1356 __stop_mcount_loc);
1371 1357
1372 return; 1358 return;