diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 304 |
1 files changed, 175 insertions, 129 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 54cb9a7d15e5..f212da486689 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -50,6 +50,9 @@ static int last_ftrace_enabled; | |||
50 | /* Quick disabling of function tracer. */ | 50 | /* Quick disabling of function tracer. */ |
51 | int function_trace_stop; | 51 | int function_trace_stop; |
52 | 52 | ||
53 | /* By default, current tracing type is normal tracing. */ | ||
54 | enum ftrace_tracing_type_t ftrace_tracing_type = FTRACE_TYPE_ENTER; | ||
55 | |||
53 | /* | 56 | /* |
54 | * ftrace_disabled is set when an anomaly is discovered. | 57 | * ftrace_disabled is set when an anomaly is discovered. |
55 | * ftrace_disabled is much stronger than ftrace_enabled. | 58 | * ftrace_disabled is much stronger than ftrace_enabled. |
@@ -334,7 +337,7 @@ ftrace_record_ip(unsigned long ip) | |||
334 | { | 337 | { |
335 | struct dyn_ftrace *rec; | 338 | struct dyn_ftrace *rec; |
336 | 339 | ||
337 | if (!ftrace_enabled || ftrace_disabled) | 340 | if (ftrace_disabled) |
338 | return NULL; | 341 | return NULL; |
339 | 342 | ||
340 | rec = ftrace_alloc_dyn_node(ip); | 343 | rec = ftrace_alloc_dyn_node(ip); |
@@ -348,107 +351,138 @@ ftrace_record_ip(unsigned long ip) | |||
348 | return rec; | 351 | return rec; |
349 | } | 352 | } |
350 | 353 | ||
351 | #define FTRACE_ADDR ((long)(ftrace_caller)) | 354 | static void print_ip_ins(const char *fmt, unsigned char *p) |
355 | { | ||
356 | int i; | ||
357 | |||
358 | printk(KERN_CONT "%s", fmt); | ||
359 | |||
360 | for (i = 0; i < MCOUNT_INSN_SIZE; i++) | ||
361 | printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]); | ||
362 | } | ||
363 | |||
364 | static void ftrace_bug(int failed, unsigned long ip) | ||
365 | { | ||
366 | switch (failed) { | ||
367 | case -EFAULT: | ||
368 | FTRACE_WARN_ON_ONCE(1); | ||
369 | pr_info("ftrace faulted on modifying "); | ||
370 | print_ip_sym(ip); | ||
371 | break; | ||
372 | case -EINVAL: | ||
373 | FTRACE_WARN_ON_ONCE(1); | ||
374 | pr_info("ftrace failed to modify "); | ||
375 | print_ip_sym(ip); | ||
376 | print_ip_ins(" actual: ", (unsigned char *)ip); | ||
377 | printk(KERN_CONT "\n"); | ||
378 | break; | ||
379 | case -EPERM: | ||
380 | FTRACE_WARN_ON_ONCE(1); | ||
381 | pr_info("ftrace faulted on writing "); | ||
382 | print_ip_sym(ip); | ||
383 | break; | ||
384 | default: | ||
385 | FTRACE_WARN_ON_ONCE(1); | ||
386 | pr_info("ftrace faulted on unknown error "); | ||
387 | print_ip_sym(ip); | ||
388 | } | ||
389 | } | ||
390 | |||
352 | 391 | ||
353 | static int | 392 | static int |
354 | __ftrace_replace_code(struct dyn_ftrace *rec, | 393 | __ftrace_replace_code(struct dyn_ftrace *rec, int enable) |
355 | unsigned char *old, unsigned char *new, int enable) | ||
356 | { | 394 | { |
357 | unsigned long ip, fl; | 395 | unsigned long ip, fl; |
396 | unsigned long ftrace_addr; | ||
397 | |||
398 | #ifdef CONFIG_FUNCTION_RET_TRACER | ||
399 | if (ftrace_tracing_type == FTRACE_TYPE_ENTER) | ||
400 | ftrace_addr = (unsigned long)ftrace_caller; | ||
401 | else | ||
402 | ftrace_addr = (unsigned long)ftrace_return_caller; | ||
403 | #else | ||
404 | ftrace_addr = (unsigned long)ftrace_caller; | ||
405 | #endif | ||
358 | 406 | ||
359 | ip = rec->ip; | 407 | ip = rec->ip; |
360 | 408 | ||
361 | if (ftrace_filtered && enable) { | 409 | /* |
410 | * If this record is not to be traced and | ||
411 | * it is not enabled then do nothing. | ||
412 | * | ||
413 | * If this record is not to be traced and | ||
414 | * it is enabled then disabled it. | ||
415 | * | ||
416 | */ | ||
417 | if (rec->flags & FTRACE_FL_NOTRACE) { | ||
418 | if (rec->flags & FTRACE_FL_ENABLED) | ||
419 | rec->flags &= ~FTRACE_FL_ENABLED; | ||
420 | else | ||
421 | return 0; | ||
422 | |||
423 | } else if (ftrace_filtered && enable) { | ||
362 | /* | 424 | /* |
363 | * If filtering is on: | 425 | * Filtering is on: |
364 | * | ||
365 | * If this record is set to be filtered and | ||
366 | * is enabled then do nothing. | ||
367 | * | ||
368 | * If this record is set to be filtered and | ||
369 | * it is not enabled, enable it. | ||
370 | * | ||
371 | * If this record is not set to be filtered | ||
372 | * and it is not enabled do nothing. | ||
373 | * | ||
374 | * If this record is set not to trace then | ||
375 | * do nothing. | ||
376 | * | ||
377 | * If this record is set not to trace and | ||
378 | * it is enabled then disable it. | ||
379 | * | ||
380 | * If this record is not set to be filtered and | ||
381 | * it is enabled, disable it. | ||
382 | */ | 426 | */ |
383 | 427 | ||
384 | fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_NOTRACE | | 428 | fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED); |
385 | FTRACE_FL_ENABLED); | ||
386 | 429 | ||
387 | if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || | 430 | /* Record is filtered and enabled, do nothing */ |
388 | (fl == (FTRACE_FL_FILTER | FTRACE_FL_NOTRACE)) || | 431 | if (fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) |
389 | !fl || (fl == FTRACE_FL_NOTRACE)) | ||
390 | return 0; | 432 | return 0; |
391 | 433 | ||
392 | /* | 434 | /* Record is not filtered and is not enabled do nothing */ |
393 | * If it is enabled disable it, | 435 | if (!fl) |
394 | * otherwise enable it! | 436 | return 0; |
395 | */ | 437 | |
396 | if (fl & FTRACE_FL_ENABLED) { | 438 | /* Record is not filtered but enabled, disable it */ |
397 | /* swap new and old */ | 439 | if (fl == FTRACE_FL_ENABLED) |
398 | new = old; | ||
399 | old = ftrace_call_replace(ip, FTRACE_ADDR); | ||
400 | rec->flags &= ~FTRACE_FL_ENABLED; | 440 | rec->flags &= ~FTRACE_FL_ENABLED; |
401 | } else { | 441 | else |
402 | new = ftrace_call_replace(ip, FTRACE_ADDR); | 442 | /* Otherwise record is filtered but not enabled, enable it */ |
403 | rec->flags |= FTRACE_FL_ENABLED; | 443 | rec->flags |= FTRACE_FL_ENABLED; |
404 | } | ||
405 | } else { | 444 | } else { |
445 | /* Disable or not filtered */ | ||
406 | 446 | ||
407 | if (enable) { | 447 | if (enable) { |
408 | /* | 448 | /* if record is enabled, do nothing */ |
409 | * If this record is set not to trace and is | ||
410 | * not enabled, do nothing. | ||
411 | */ | ||
412 | fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); | ||
413 | if (fl == FTRACE_FL_NOTRACE) | ||
414 | return 0; | ||
415 | |||
416 | new = ftrace_call_replace(ip, FTRACE_ADDR); | ||
417 | } else | ||
418 | old = ftrace_call_replace(ip, FTRACE_ADDR); | ||
419 | |||
420 | if (enable) { | ||
421 | if (rec->flags & FTRACE_FL_ENABLED) | 449 | if (rec->flags & FTRACE_FL_ENABLED) |
422 | return 0; | 450 | return 0; |
451 | |||
423 | rec->flags |= FTRACE_FL_ENABLED; | 452 | rec->flags |= FTRACE_FL_ENABLED; |
453 | |||
424 | } else { | 454 | } else { |
455 | |||
456 | /* if record is not enabled do nothing */ | ||
425 | if (!(rec->flags & FTRACE_FL_ENABLED)) | 457 | if (!(rec->flags & FTRACE_FL_ENABLED)) |
426 | return 0; | 458 | return 0; |
459 | |||
427 | rec->flags &= ~FTRACE_FL_ENABLED; | 460 | rec->flags &= ~FTRACE_FL_ENABLED; |
428 | } | 461 | } |
429 | } | 462 | } |
430 | 463 | ||
431 | return ftrace_modify_code(ip, old, new); | 464 | if (rec->flags & FTRACE_FL_ENABLED) |
465 | return ftrace_make_call(rec, ftrace_addr); | ||
466 | else | ||
467 | return ftrace_make_nop(NULL, rec, ftrace_addr); | ||
432 | } | 468 | } |
433 | 469 | ||
434 | static void ftrace_replace_code(int enable) | 470 | static void ftrace_replace_code(int enable) |
435 | { | 471 | { |
436 | int i, failed; | 472 | int i, failed; |
437 | unsigned char *new = NULL, *old = NULL; | ||
438 | struct dyn_ftrace *rec; | 473 | struct dyn_ftrace *rec; |
439 | struct ftrace_page *pg; | 474 | struct ftrace_page *pg; |
440 | 475 | ||
441 | if (enable) | ||
442 | old = ftrace_nop_replace(); | ||
443 | else | ||
444 | new = ftrace_nop_replace(); | ||
445 | |||
446 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 476 | for (pg = ftrace_pages_start; pg; pg = pg->next) { |
447 | for (i = 0; i < pg->index; i++) { | 477 | for (i = 0; i < pg->index; i++) { |
448 | rec = &pg->records[i]; | 478 | rec = &pg->records[i]; |
449 | 479 | ||
450 | /* don't modify code that has already faulted */ | 480 | /* |
451 | if (rec->flags & FTRACE_FL_FAILED) | 481 | * Skip over free records and records that have |
482 | * failed. | ||
483 | */ | ||
484 | if (rec->flags & FTRACE_FL_FREE || | ||
485 | rec->flags & FTRACE_FL_FAILED) | ||
452 | continue; | 486 | continue; |
453 | 487 | ||
454 | /* ignore updates to this record's mcount site */ | 488 | /* ignore updates to this record's mcount site */ |
@@ -459,68 +493,30 @@ static void ftrace_replace_code(int enable) | |||
459 | unfreeze_record(rec); | 493 | unfreeze_record(rec); |
460 | } | 494 | } |
461 | 495 | ||
462 | failed = __ftrace_replace_code(rec, old, new, enable); | 496 | failed = __ftrace_replace_code(rec, enable); |
463 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { | 497 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { |
464 | rec->flags |= FTRACE_FL_FAILED; | 498 | rec->flags |= FTRACE_FL_FAILED; |
465 | if ((system_state == SYSTEM_BOOTING) || | 499 | if ((system_state == SYSTEM_BOOTING) || |
466 | !core_kernel_text(rec->ip)) { | 500 | !core_kernel_text(rec->ip)) { |
467 | ftrace_free_rec(rec); | 501 | ftrace_free_rec(rec); |
468 | } | 502 | } else |
503 | ftrace_bug(failed, rec->ip); | ||
469 | } | 504 | } |
470 | } | 505 | } |
471 | } | 506 | } |
472 | } | 507 | } |
473 | 508 | ||
474 | static void print_ip_ins(const char *fmt, unsigned char *p) | ||
475 | { | ||
476 | int i; | ||
477 | |||
478 | printk(KERN_CONT "%s", fmt); | ||
479 | |||
480 | for (i = 0; i < MCOUNT_INSN_SIZE; i++) | ||
481 | printk(KERN_CONT "%s%02x", i ? ":" : "", p[i]); | ||
482 | } | ||
483 | |||
484 | static int | 509 | static int |
485 | ftrace_code_disable(struct dyn_ftrace *rec) | 510 | ftrace_code_disable(struct module *mod, struct dyn_ftrace *rec) |
486 | { | 511 | { |
487 | unsigned long ip; | 512 | unsigned long ip; |
488 | unsigned char *nop, *call; | ||
489 | int ret; | 513 | int ret; |
490 | 514 | ||
491 | ip = rec->ip; | 515 | ip = rec->ip; |
492 | 516 | ||
493 | nop = ftrace_nop_replace(); | 517 | ret = ftrace_make_nop(mod, rec, mcount_addr); |
494 | call = ftrace_call_replace(ip, mcount_addr); | ||
495 | |||
496 | ret = ftrace_modify_code(ip, call, nop); | ||
497 | if (ret) { | 518 | if (ret) { |
498 | switch (ret) { | 519 | ftrace_bug(ret, ip); |
499 | case -EFAULT: | ||
500 | FTRACE_WARN_ON_ONCE(1); | ||
501 | pr_info("ftrace faulted on modifying "); | ||
502 | print_ip_sym(ip); | ||
503 | break; | ||
504 | case -EINVAL: | ||
505 | FTRACE_WARN_ON_ONCE(1); | ||
506 | pr_info("ftrace failed to modify "); | ||
507 | print_ip_sym(ip); | ||
508 | print_ip_ins(" expected: ", call); | ||
509 | print_ip_ins(" actual: ", (unsigned char *)ip); | ||
510 | print_ip_ins(" replace: ", nop); | ||
511 | printk(KERN_CONT "\n"); | ||
512 | break; | ||
513 | case -EPERM: | ||
514 | FTRACE_WARN_ON_ONCE(1); | ||
515 | pr_info("ftrace faulted on writing "); | ||
516 | print_ip_sym(ip); | ||
517 | break; | ||
518 | default: | ||
519 | FTRACE_WARN_ON_ONCE(1); | ||
520 | pr_info("ftrace faulted on unknown error "); | ||
521 | print_ip_sym(ip); | ||
522 | } | ||
523 | |||
524 | rec->flags |= FTRACE_FL_FAILED; | 520 | rec->flags |= FTRACE_FL_FAILED; |
525 | return 0; | 521 | return 0; |
526 | } | 522 | } |
@@ -560,8 +556,7 @@ static void ftrace_startup(void) | |||
560 | 556 | ||
561 | mutex_lock(&ftrace_start_lock); | 557 | mutex_lock(&ftrace_start_lock); |
562 | ftrace_start_up++; | 558 | ftrace_start_up++; |
563 | if (ftrace_start_up == 1) | 559 | command |= FTRACE_ENABLE_CALLS; |
564 | command |= FTRACE_ENABLE_CALLS; | ||
565 | 560 | ||
566 | if (saved_ftrace_func != ftrace_trace_function) { | 561 | if (saved_ftrace_func != ftrace_trace_function) { |
567 | saved_ftrace_func = ftrace_trace_function; | 562 | saved_ftrace_func = ftrace_trace_function; |
@@ -639,7 +634,7 @@ static cycle_t ftrace_update_time; | |||
639 | static unsigned long ftrace_update_cnt; | 634 | static unsigned long ftrace_update_cnt; |
640 | unsigned long ftrace_update_tot_cnt; | 635 | unsigned long ftrace_update_tot_cnt; |
641 | 636 | ||
642 | static int ftrace_update_code(void) | 637 | static int ftrace_update_code(struct module *mod) |
643 | { | 638 | { |
644 | struct dyn_ftrace *p, *t; | 639 | struct dyn_ftrace *p, *t; |
645 | cycle_t start, stop; | 640 | cycle_t start, stop; |
@@ -656,7 +651,7 @@ static int ftrace_update_code(void) | |||
656 | list_del_init(&p->list); | 651 | list_del_init(&p->list); |
657 | 652 | ||
658 | /* convert record (i.e, patch mcount-call with NOP) */ | 653 | /* convert record (i.e, patch mcount-call with NOP) */ |
659 | if (ftrace_code_disable(p)) { | 654 | if (ftrace_code_disable(mod, p)) { |
660 | p->flags |= FTRACE_FL_CONVERTED; | 655 | p->flags |= FTRACE_FL_CONVERTED; |
661 | ftrace_update_cnt++; | 656 | ftrace_update_cnt++; |
662 | } else | 657 | } else |
@@ -699,7 +694,7 @@ static int __init ftrace_dyn_table_alloc(unsigned long num_to_init) | |||
699 | 694 | ||
700 | cnt = num_to_init / ENTRIES_PER_PAGE; | 695 | cnt = num_to_init / ENTRIES_PER_PAGE; |
701 | pr_info("ftrace: allocating %ld entries in %d pages\n", | 696 | pr_info("ftrace: allocating %ld entries in %d pages\n", |
702 | num_to_init, cnt); | 697 | num_to_init, cnt + 1); |
703 | 698 | ||
704 | for (i = 0; i < cnt; i++) { | 699 | for (i = 0; i < cnt; i++) { |
705 | pg->next = (void *)get_zeroed_page(GFP_KERNEL); | 700 | pg->next = (void *)get_zeroed_page(GFP_KERNEL); |
@@ -782,13 +777,11 @@ static void *t_start(struct seq_file *m, loff_t *pos) | |||
782 | void *p = NULL; | 777 | void *p = NULL; |
783 | loff_t l = -1; | 778 | loff_t l = -1; |
784 | 779 | ||
785 | if (*pos != iter->pos) { | 780 | if (*pos > iter->pos) |
786 | for (p = t_next(m, p, &l); p && l < *pos; p = t_next(m, p, &l)) | 781 | *pos = iter->pos; |
787 | ; | 782 | |
788 | } else { | 783 | l = *pos; |
789 | l = *pos; | 784 | p = t_next(m, p, &l); |
790 | p = t_next(m, p, &l); | ||
791 | } | ||
792 | 785 | ||
793 | return p; | 786 | return p; |
794 | } | 787 | } |
@@ -799,15 +792,21 @@ static void t_stop(struct seq_file *m, void *p) | |||
799 | 792 | ||
800 | static int t_show(struct seq_file *m, void *v) | 793 | static int t_show(struct seq_file *m, void *v) |
801 | { | 794 | { |
795 | struct ftrace_iterator *iter = m->private; | ||
802 | struct dyn_ftrace *rec = v; | 796 | struct dyn_ftrace *rec = v; |
803 | char str[KSYM_SYMBOL_LEN]; | 797 | char str[KSYM_SYMBOL_LEN]; |
798 | int ret = 0; | ||
804 | 799 | ||
805 | if (!rec) | 800 | if (!rec) |
806 | return 0; | 801 | return 0; |
807 | 802 | ||
808 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | 803 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); |
809 | 804 | ||
810 | seq_printf(m, "%s\n", str); | 805 | ret = seq_printf(m, "%s\n", str); |
806 | if (ret < 0) { | ||
807 | iter->pos--; | ||
808 | iter->idx--; | ||
809 | } | ||
811 | 810 | ||
812 | return 0; | 811 | return 0; |
813 | } | 812 | } |
@@ -833,7 +832,7 @@ ftrace_avail_open(struct inode *inode, struct file *file) | |||
833 | return -ENOMEM; | 832 | return -ENOMEM; |
834 | 833 | ||
835 | iter->pg = ftrace_pages_start; | 834 | iter->pg = ftrace_pages_start; |
836 | iter->pos = -1; | 835 | iter->pos = 0; |
837 | 836 | ||
838 | ret = seq_open(file, &show_ftrace_seq_ops); | 837 | ret = seq_open(file, &show_ftrace_seq_ops); |
839 | if (!ret) { | 838 | if (!ret) { |
@@ -920,7 +919,7 @@ ftrace_regex_open(struct inode *inode, struct file *file, int enable) | |||
920 | 919 | ||
921 | if (file->f_mode & FMODE_READ) { | 920 | if (file->f_mode & FMODE_READ) { |
922 | iter->pg = ftrace_pages_start; | 921 | iter->pg = ftrace_pages_start; |
923 | iter->pos = -1; | 922 | iter->pos = 0; |
924 | iter->flags = enable ? FTRACE_ITER_FILTER : | 923 | iter->flags = enable ? FTRACE_ITER_FILTER : |
925 | FTRACE_ITER_NOTRACE; | 924 | FTRACE_ITER_NOTRACE; |
926 | 925 | ||
@@ -1211,7 +1210,7 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable) | |||
1211 | 1210 | ||
1212 | mutex_lock(&ftrace_sysctl_lock); | 1211 | mutex_lock(&ftrace_sysctl_lock); |
1213 | mutex_lock(&ftrace_start_lock); | 1212 | mutex_lock(&ftrace_start_lock); |
1214 | if (iter->filtered && ftrace_start_up && ftrace_enabled) | 1213 | if (ftrace_start_up && ftrace_enabled) |
1215 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); | 1214 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); |
1216 | mutex_unlock(&ftrace_start_lock); | 1215 | mutex_unlock(&ftrace_start_lock); |
1217 | mutex_unlock(&ftrace_sysctl_lock); | 1216 | mutex_unlock(&ftrace_sysctl_lock); |
@@ -1298,7 +1297,8 @@ static __init int ftrace_init_debugfs(void) | |||
1298 | 1297 | ||
1299 | fs_initcall(ftrace_init_debugfs); | 1298 | fs_initcall(ftrace_init_debugfs); |
1300 | 1299 | ||
1301 | static int ftrace_convert_nops(unsigned long *start, | 1300 | static int ftrace_convert_nops(struct module *mod, |
1301 | unsigned long *start, | ||
1302 | unsigned long *end) | 1302 | unsigned long *end) |
1303 | { | 1303 | { |
1304 | unsigned long *p; | 1304 | unsigned long *p; |
@@ -1309,23 +1309,32 @@ static int ftrace_convert_nops(unsigned long *start, | |||
1309 | p = start; | 1309 | p = start; |
1310 | while (p < end) { | 1310 | while (p < end) { |
1311 | addr = ftrace_call_adjust(*p++); | 1311 | addr = ftrace_call_adjust(*p++); |
1312 | /* | ||
1313 | * Some architecture linkers will pad between | ||
1314 | * the different mcount_loc sections of different | ||
1315 | * object files to satisfy alignments. | ||
1316 | * Skip any NULL pointers. | ||
1317 | */ | ||
1318 | if (!addr) | ||
1319 | continue; | ||
1312 | ftrace_record_ip(addr); | 1320 | ftrace_record_ip(addr); |
1313 | } | 1321 | } |
1314 | 1322 | ||
1315 | /* disable interrupts to prevent kstop machine */ | 1323 | /* disable interrupts to prevent kstop machine */ |
1316 | local_irq_save(flags); | 1324 | local_irq_save(flags); |
1317 | ftrace_update_code(); | 1325 | ftrace_update_code(mod); |
1318 | local_irq_restore(flags); | 1326 | local_irq_restore(flags); |
1319 | mutex_unlock(&ftrace_start_lock); | 1327 | mutex_unlock(&ftrace_start_lock); |
1320 | 1328 | ||
1321 | return 0; | 1329 | return 0; |
1322 | } | 1330 | } |
1323 | 1331 | ||
1324 | void ftrace_init_module(unsigned long *start, unsigned long *end) | 1332 | void ftrace_init_module(struct module *mod, |
1333 | unsigned long *start, unsigned long *end) | ||
1325 | { | 1334 | { |
1326 | if (ftrace_disabled || start == end) | 1335 | if (ftrace_disabled || start == end) |
1327 | return; | 1336 | return; |
1328 | ftrace_convert_nops(start, end); | 1337 | ftrace_convert_nops(mod, start, end); |
1329 | } | 1338 | } |
1330 | 1339 | ||
1331 | extern unsigned long __start_mcount_loc[]; | 1340 | extern unsigned long __start_mcount_loc[]; |
@@ -1355,7 +1364,8 @@ void __init ftrace_init(void) | |||
1355 | 1364 | ||
1356 | last_ftrace_enabled = ftrace_enabled = 1; | 1365 | last_ftrace_enabled = ftrace_enabled = 1; |
1357 | 1366 | ||
1358 | ret = ftrace_convert_nops(__start_mcount_loc, | 1367 | ret = ftrace_convert_nops(NULL, |
1368 | __start_mcount_loc, | ||
1359 | __stop_mcount_loc); | 1369 | __stop_mcount_loc); |
1360 | 1370 | ||
1361 | return; | 1371 | return; |
@@ -1411,10 +1421,17 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
1411 | return -1; | 1421 | return -1; |
1412 | 1422 | ||
1413 | mutex_lock(&ftrace_sysctl_lock); | 1423 | mutex_lock(&ftrace_sysctl_lock); |
1424 | |||
1425 | if (ftrace_tracing_type == FTRACE_TYPE_RETURN) { | ||
1426 | ret = -EBUSY; | ||
1427 | goto out; | ||
1428 | } | ||
1429 | |||
1414 | ret = __register_ftrace_function(ops); | 1430 | ret = __register_ftrace_function(ops); |
1415 | ftrace_startup(); | 1431 | ftrace_startup(); |
1416 | mutex_unlock(&ftrace_sysctl_lock); | ||
1417 | 1432 | ||
1433 | out: | ||
1434 | mutex_unlock(&ftrace_sysctl_lock); | ||
1418 | return ret; | 1435 | return ret; |
1419 | } | 1436 | } |
1420 | 1437 | ||
@@ -1480,16 +1497,45 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, | |||
1480 | } | 1497 | } |
1481 | 1498 | ||
1482 | #ifdef CONFIG_FUNCTION_RET_TRACER | 1499 | #ifdef CONFIG_FUNCTION_RET_TRACER |
1500 | |||
1501 | /* The callback that hooks the return of a function */ | ||
1483 | trace_function_return_t ftrace_function_return = | 1502 | trace_function_return_t ftrace_function_return = |
1484 | (trace_function_return_t)ftrace_stub; | 1503 | (trace_function_return_t)ftrace_stub; |
1485 | void register_ftrace_return(trace_function_return_t func) | 1504 | |
1505 | int register_ftrace_return(trace_function_return_t func) | ||
1486 | { | 1506 | { |
1507 | int ret = 0; | ||
1508 | |||
1509 | mutex_lock(&ftrace_sysctl_lock); | ||
1510 | |||
1511 | /* | ||
1512 | * Don't launch return tracing if normal function | ||
1513 | * tracing is already running. | ||
1514 | */ | ||
1515 | if (ftrace_trace_function != ftrace_stub) { | ||
1516 | ret = -EBUSY; | ||
1517 | goto out; | ||
1518 | } | ||
1519 | |||
1520 | ftrace_tracing_type = FTRACE_TYPE_RETURN; | ||
1487 | ftrace_function_return = func; | 1521 | ftrace_function_return = func; |
1522 | ftrace_startup(); | ||
1523 | |||
1524 | out: | ||
1525 | mutex_unlock(&ftrace_sysctl_lock); | ||
1526 | return ret; | ||
1488 | } | 1527 | } |
1489 | 1528 | ||
1490 | void unregister_ftrace_return(void) | 1529 | void unregister_ftrace_return(void) |
1491 | { | 1530 | { |
1531 | mutex_lock(&ftrace_sysctl_lock); | ||
1532 | |||
1492 | ftrace_function_return = (trace_function_return_t)ftrace_stub; | 1533 | ftrace_function_return = (trace_function_return_t)ftrace_stub; |
1534 | ftrace_shutdown(); | ||
1535 | /* Restore normal tracing type */ | ||
1536 | ftrace_tracing_type = FTRACE_TYPE_ENTER; | ||
1537 | |||
1538 | mutex_unlock(&ftrace_sysctl_lock); | ||
1493 | } | 1539 | } |
1494 | #endif | 1540 | #endif |
1495 | 1541 | ||