aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/ftrace.c
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 /kernel/trace/ftrace.c
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>
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r--kernel/trace/ftrace.c62
1 files changed, 24 insertions, 38 deletions
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;