diff options
author | Steven Rostedt <srostedt@redhat.com> | 2008-11-14 19:21:19 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-16 01:36:02 -0500 |
commit | 31e889098a80ceb3e9e3c555d522b2686a6663c6 (patch) | |
tree | 2acb73507de4191d4a9aa5ccf08fa24e7044c89e /kernel | |
parent | d51ad7ac48f991c4a8834485727efa99a691cb87 (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')
-rw-r--r-- | kernel/module.c | 2 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 62 |
2 files changed, 25 insertions, 39 deletions
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 | ||
361 | static void ftrace_bug(int failed, unsigned long ip, | 361 | static 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 | ||
394 | static int | 390 | static 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 | ||
475 | static void ftrace_replace_code(int enable) | 468 | static 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 | ||
520 | static int | 507 | static int |
521 | ftrace_code_disable(struct dyn_ftrace *rec) | 508 | ftrace_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; | |||
650 | static unsigned long ftrace_update_cnt; | 633 | static unsigned long ftrace_update_cnt; |
651 | unsigned long ftrace_update_tot_cnt; | 634 | unsigned long ftrace_update_tot_cnt; |
652 | 635 | ||
653 | static int ftrace_update_code(void) | 636 | static 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 | ||
1310 | fs_initcall(ftrace_init_debugfs); | 1293 | fs_initcall(ftrace_init_debugfs); |
1311 | 1294 | ||
1312 | static int ftrace_convert_nops(unsigned long *start, | 1295 | static 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 | ||
1335 | void ftrace_init_module(unsigned long *start, unsigned long *end) | 1319 | void 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 | ||
1342 | extern unsigned long __start_mcount_loc[]; | 1327 | extern 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; |