diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
| -rw-r--r-- | kernel/trace/ftrace.c | 242 |
1 files changed, 118 insertions, 124 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 0fa92f677c92..a008663d86c8 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -1383,44 +1383,73 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) | |||
| 1383 | 1383 | ||
| 1384 | static int ftrace_cmp_recs(const void *a, const void *b) | 1384 | static int ftrace_cmp_recs(const void *a, const void *b) |
| 1385 | { | 1385 | { |
| 1386 | const struct dyn_ftrace *reca = a; | 1386 | const struct dyn_ftrace *key = a; |
| 1387 | const struct dyn_ftrace *recb = b; | 1387 | const struct dyn_ftrace *rec = b; |
| 1388 | 1388 | ||
| 1389 | if (reca->ip > recb->ip) | 1389 | if (key->flags < rec->ip) |
| 1390 | return 1; | ||
| 1391 | if (reca->ip < recb->ip) | ||
| 1392 | return -1; | 1390 | return -1; |
| 1391 | if (key->ip >= rec->ip + MCOUNT_INSN_SIZE) | ||
| 1392 | return 1; | ||
| 1393 | return 0; | 1393 | return 0; |
| 1394 | } | 1394 | } |
| 1395 | 1395 | ||
| 1396 | /** | 1396 | static unsigned long ftrace_location_range(unsigned long start, unsigned long end) |
| 1397 | * ftrace_location - return true if the ip giving is a traced location | ||
| 1398 | * @ip: the instruction pointer to check | ||
| 1399 | * | ||
| 1400 | * Returns 1 if @ip given is a pointer to a ftrace location. | ||
| 1401 | * That is, the instruction that is either a NOP or call to | ||
| 1402 | * the function tracer. It checks the ftrace internal tables to | ||
| 1403 | * determine if the address belongs or not. | ||
| 1404 | */ | ||
| 1405 | int ftrace_location(unsigned long ip) | ||
| 1406 | { | 1397 | { |
| 1407 | struct ftrace_page *pg; | 1398 | struct ftrace_page *pg; |
| 1408 | struct dyn_ftrace *rec; | 1399 | struct dyn_ftrace *rec; |
| 1409 | struct dyn_ftrace key; | 1400 | struct dyn_ftrace key; |
| 1410 | 1401 | ||
| 1411 | key.ip = ip; | 1402 | key.ip = start; |
| 1403 | key.flags = end; /* overload flags, as it is unsigned long */ | ||
| 1412 | 1404 | ||
| 1413 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 1405 | for (pg = ftrace_pages_start; pg; pg = pg->next) { |
| 1406 | if (end < pg->records[0].ip || | ||
| 1407 | start >= (pg->records[pg->index - 1].ip + MCOUNT_INSN_SIZE)) | ||
| 1408 | continue; | ||
| 1414 | rec = bsearch(&key, pg->records, pg->index, | 1409 | rec = bsearch(&key, pg->records, pg->index, |
| 1415 | sizeof(struct dyn_ftrace), | 1410 | sizeof(struct dyn_ftrace), |
| 1416 | ftrace_cmp_recs); | 1411 | ftrace_cmp_recs); |
| 1417 | if (rec) | 1412 | if (rec) |
| 1418 | return 1; | 1413 | return rec->ip; |
| 1419 | } | 1414 | } |
| 1420 | 1415 | ||
| 1421 | return 0; | 1416 | return 0; |
| 1422 | } | 1417 | } |
| 1423 | 1418 | ||
| 1419 | /** | ||
| 1420 | * ftrace_location - return true if the ip giving is a traced location | ||
| 1421 | * @ip: the instruction pointer to check | ||
| 1422 | * | ||
| 1423 | * Returns rec->ip if @ip given is a pointer to a ftrace location. | ||
| 1424 | * That is, the instruction that is either a NOP or call to | ||
| 1425 | * the function tracer. It checks the ftrace internal tables to | ||
| 1426 | * determine if the address belongs or not. | ||
| 1427 | */ | ||
| 1428 | unsigned long ftrace_location(unsigned long ip) | ||
| 1429 | { | ||
| 1430 | return ftrace_location_range(ip, ip); | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | /** | ||
| 1434 | * ftrace_text_reserved - return true if range contains an ftrace location | ||
| 1435 | * @start: start of range to search | ||
| 1436 | * @end: end of range to search (inclusive). @end points to the last byte to check. | ||
| 1437 | * | ||
| 1438 | * Returns 1 if @start and @end contains a ftrace location. | ||
| 1439 | * That is, the instruction that is either a NOP or call to | ||
| 1440 | * the function tracer. It checks the ftrace internal tables to | ||
| 1441 | * determine if the address belongs or not. | ||
| 1442 | */ | ||
| 1443 | int ftrace_text_reserved(void *start, void *end) | ||
| 1444 | { | ||
| 1445 | unsigned long ret; | ||
| 1446 | |||
| 1447 | ret = ftrace_location_range((unsigned long)start, | ||
| 1448 | (unsigned long)end); | ||
| 1449 | |||
| 1450 | return (int)!!ret; | ||
| 1451 | } | ||
| 1452 | |||
| 1424 | static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | 1453 | static void __ftrace_hash_rec_update(struct ftrace_ops *ops, |
| 1425 | int filter_hash, | 1454 | int filter_hash, |
| 1426 | bool inc) | 1455 | bool inc) |
| @@ -1520,35 +1549,6 @@ static void ftrace_hash_rec_enable(struct ftrace_ops *ops, | |||
| 1520 | __ftrace_hash_rec_update(ops, filter_hash, 1); | 1549 | __ftrace_hash_rec_update(ops, filter_hash, 1); |
| 1521 | } | 1550 | } |
| 1522 | 1551 | ||
| 1523 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | ||
| 1524 | { | ||
| 1525 | if (ftrace_pages->index == ftrace_pages->size) { | ||
| 1526 | /* We should have allocated enough */ | ||
| 1527 | if (WARN_ON(!ftrace_pages->next)) | ||
| 1528 | return NULL; | ||
| 1529 | ftrace_pages = ftrace_pages->next; | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | return &ftrace_pages->records[ftrace_pages->index++]; | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | static struct dyn_ftrace * | ||
| 1536 | ftrace_record_ip(unsigned long ip) | ||
| 1537 | { | ||
| 1538 | struct dyn_ftrace *rec; | ||
| 1539 | |||
| 1540 | if (ftrace_disabled) | ||
| 1541 | return NULL; | ||
| 1542 | |||
| 1543 | rec = ftrace_alloc_dyn_node(ip); | ||
| 1544 | if (!rec) | ||
| 1545 | return NULL; | ||
| 1546 | |||
| 1547 | rec->ip = ip; | ||
| 1548 | |||
| 1549 | return rec; | ||
| 1550 | } | ||
| 1551 | |||
| 1552 | static void print_ip_ins(const char *fmt, unsigned char *p) | 1552 | static void print_ip_ins(const char *fmt, unsigned char *p) |
| 1553 | { | 1553 | { |
| 1554 | int i; | 1554 | int i; |
| @@ -1598,21 +1598,6 @@ void ftrace_bug(int failed, unsigned long ip) | |||
| 1598 | } | 1598 | } |
| 1599 | } | 1599 | } |
| 1600 | 1600 | ||
| 1601 | |||
| 1602 | /* Return 1 if the address range is reserved for ftrace */ | ||
| 1603 | int ftrace_text_reserved(void *start, void *end) | ||
| 1604 | { | ||
| 1605 | struct dyn_ftrace *rec; | ||
| 1606 | struct ftrace_page *pg; | ||
| 1607 | |||
| 1608 | do_for_each_ftrace_rec(pg, rec) { | ||
| 1609 | if (rec->ip <= (unsigned long)end && | ||
| 1610 | rec->ip + MCOUNT_INSN_SIZE > (unsigned long)start) | ||
| 1611 | return 1; | ||
| 1612 | } while_for_each_ftrace_rec(); | ||
| 1613 | return 0; | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) | 1601 | static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) |
| 1617 | { | 1602 | { |
| 1618 | unsigned long flag = 0UL; | 1603 | unsigned long flag = 0UL; |
| @@ -1698,7 +1683,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | |||
| 1698 | return -1; /* unknow ftrace bug */ | 1683 | return -1; /* unknow ftrace bug */ |
| 1699 | } | 1684 | } |
| 1700 | 1685 | ||
| 1701 | static void ftrace_replace_code(int update) | 1686 | void __weak ftrace_replace_code(int enable) |
| 1702 | { | 1687 | { |
| 1703 | struct dyn_ftrace *rec; | 1688 | struct dyn_ftrace *rec; |
| 1704 | struct ftrace_page *pg; | 1689 | struct ftrace_page *pg; |
| @@ -1708,7 +1693,7 @@ static void ftrace_replace_code(int update) | |||
| 1708 | return; | 1693 | return; |
| 1709 | 1694 | ||
| 1710 | do_for_each_ftrace_rec(pg, rec) { | 1695 | do_for_each_ftrace_rec(pg, rec) { |
| 1711 | failed = __ftrace_replace_code(rec, update); | 1696 | failed = __ftrace_replace_code(rec, enable); |
| 1712 | if (failed) { | 1697 | if (failed) { |
| 1713 | ftrace_bug(failed, rec->ip); | 1698 | ftrace_bug(failed, rec->ip); |
| 1714 | /* Stop processing */ | 1699 | /* Stop processing */ |
| @@ -1826,22 +1811,27 @@ int __weak ftrace_arch_code_modify_post_process(void) | |||
| 1826 | return 0; | 1811 | return 0; |
| 1827 | } | 1812 | } |
| 1828 | 1813 | ||
| 1829 | static int __ftrace_modify_code(void *data) | 1814 | void ftrace_modify_all_code(int command) |
| 1830 | { | 1815 | { |
| 1831 | int *command = data; | 1816 | if (command & FTRACE_UPDATE_CALLS) |
| 1832 | |||
| 1833 | if (*command & FTRACE_UPDATE_CALLS) | ||
| 1834 | ftrace_replace_code(1); | 1817 | ftrace_replace_code(1); |
| 1835 | else if (*command & FTRACE_DISABLE_CALLS) | 1818 | else if (command & FTRACE_DISABLE_CALLS) |
| 1836 | ftrace_replace_code(0); | 1819 | ftrace_replace_code(0); |
| 1837 | 1820 | ||
| 1838 | if (*command & FTRACE_UPDATE_TRACE_FUNC) | 1821 | if (command & FTRACE_UPDATE_TRACE_FUNC) |
| 1839 | ftrace_update_ftrace_func(ftrace_trace_function); | 1822 | ftrace_update_ftrace_func(ftrace_trace_function); |
| 1840 | 1823 | ||
| 1841 | if (*command & FTRACE_START_FUNC_RET) | 1824 | if (command & FTRACE_START_FUNC_RET) |
| 1842 | ftrace_enable_ftrace_graph_caller(); | 1825 | ftrace_enable_ftrace_graph_caller(); |
| 1843 | else if (*command & FTRACE_STOP_FUNC_RET) | 1826 | else if (command & FTRACE_STOP_FUNC_RET) |
| 1844 | ftrace_disable_ftrace_graph_caller(); | 1827 | ftrace_disable_ftrace_graph_caller(); |
| 1828 | } | ||
| 1829 | |||
| 1830 | static int __ftrace_modify_code(void *data) | ||
| 1831 | { | ||
| 1832 | int *command = data; | ||
| 1833 | |||
| 1834 | ftrace_modify_all_code(*command); | ||
| 1845 | 1835 | ||
| 1846 | return 0; | 1836 | return 0; |
| 1847 | } | 1837 | } |
| @@ -2469,57 +2459,35 @@ static int | |||
| 2469 | ftrace_avail_open(struct inode *inode, struct file *file) | 2459 | ftrace_avail_open(struct inode *inode, struct file *file) |
| 2470 | { | 2460 | { |
| 2471 | struct ftrace_iterator *iter; | 2461 | struct ftrace_iterator *iter; |
| 2472 | int ret; | ||
| 2473 | 2462 | ||
| 2474 | if (unlikely(ftrace_disabled)) | 2463 | if (unlikely(ftrace_disabled)) |
| 2475 | return -ENODEV; | 2464 | return -ENODEV; |
| 2476 | 2465 | ||
| 2477 | iter = kzalloc(sizeof(*iter), GFP_KERNEL); | 2466 | iter = __seq_open_private(file, &show_ftrace_seq_ops, sizeof(*iter)); |
| 2478 | if (!iter) | 2467 | if (iter) { |
| 2479 | return -ENOMEM; | 2468 | iter->pg = ftrace_pages_start; |
| 2480 | 2469 | iter->ops = &global_ops; | |
| 2481 | iter->pg = ftrace_pages_start; | ||
| 2482 | iter->ops = &global_ops; | ||
| 2483 | |||
| 2484 | ret = seq_open(file, &show_ftrace_seq_ops); | ||
| 2485 | if (!ret) { | ||
| 2486 | struct seq_file *m = file->private_data; | ||
| 2487 | |||
| 2488 | m->private = iter; | ||
| 2489 | } else { | ||
| 2490 | kfree(iter); | ||
| 2491 | } | 2470 | } |
| 2492 | 2471 | ||
| 2493 | return ret; | 2472 | return iter ? 0 : -ENOMEM; |
| 2494 | } | 2473 | } |
| 2495 | 2474 | ||
| 2496 | static int | 2475 | static int |
| 2497 | ftrace_enabled_open(struct inode *inode, struct file *file) | 2476 | ftrace_enabled_open(struct inode *inode, struct file *file) |
| 2498 | { | 2477 | { |
| 2499 | struct ftrace_iterator *iter; | 2478 | struct ftrace_iterator *iter; |
| 2500 | int ret; | ||
| 2501 | 2479 | ||
| 2502 | if (unlikely(ftrace_disabled)) | 2480 | if (unlikely(ftrace_disabled)) |
| 2503 | return -ENODEV; | 2481 | return -ENODEV; |
| 2504 | 2482 | ||
| 2505 | iter = kzalloc(sizeof(*iter), GFP_KERNEL); | 2483 | iter = __seq_open_private(file, &show_ftrace_seq_ops, sizeof(*iter)); |
| 2506 | if (!iter) | 2484 | if (iter) { |
| 2507 | return -ENOMEM; | 2485 | iter->pg = ftrace_pages_start; |
| 2508 | 2486 | iter->flags = FTRACE_ITER_ENABLED; | |
| 2509 | iter->pg = ftrace_pages_start; | 2487 | iter->ops = &global_ops; |
| 2510 | iter->flags = FTRACE_ITER_ENABLED; | ||
| 2511 | iter->ops = &global_ops; | ||
| 2512 | |||
| 2513 | ret = seq_open(file, &show_ftrace_seq_ops); | ||
| 2514 | if (!ret) { | ||
| 2515 | struct seq_file *m = file->private_data; | ||
| 2516 | |||
| 2517 | m->private = iter; | ||
| 2518 | } else { | ||
| 2519 | kfree(iter); | ||
| 2520 | } | 2488 | } |
| 2521 | 2489 | ||
| 2522 | return ret; | 2490 | return iter ? 0 : -ENOMEM; |
| 2523 | } | 2491 | } |
| 2524 | 2492 | ||
| 2525 | static void ftrace_filter_reset(struct ftrace_hash *hash) | 2493 | static void ftrace_filter_reset(struct ftrace_hash *hash) |
| @@ -3688,22 +3656,36 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) | |||
| 3688 | return 0; | 3656 | return 0; |
| 3689 | } | 3657 | } |
| 3690 | 3658 | ||
| 3691 | static void ftrace_swap_recs(void *a, void *b, int size) | 3659 | static int ftrace_cmp_ips(const void *a, const void *b) |
| 3660 | { | ||
| 3661 | const unsigned long *ipa = a; | ||
| 3662 | const unsigned long *ipb = b; | ||
| 3663 | |||
| 3664 | if (*ipa > *ipb) | ||
| 3665 | return 1; | ||
| 3666 | if (*ipa < *ipb) | ||
| 3667 | return -1; | ||
| 3668 | return 0; | ||
| 3669 | } | ||
| 3670 | |||
| 3671 | static void ftrace_swap_ips(void *a, void *b, int size) | ||
| 3692 | { | 3672 | { |
| 3693 | struct dyn_ftrace *reca = a; | 3673 | unsigned long *ipa = a; |
| 3694 | struct dyn_ftrace *recb = b; | 3674 | unsigned long *ipb = b; |
| 3695 | struct dyn_ftrace t; | 3675 | unsigned long t; |
| 3696 | 3676 | ||
| 3697 | t = *reca; | 3677 | t = *ipa; |
| 3698 | *reca = *recb; | 3678 | *ipa = *ipb; |
| 3699 | *recb = t; | 3679 | *ipb = t; |
| 3700 | } | 3680 | } |
| 3701 | 3681 | ||
| 3702 | static int ftrace_process_locs(struct module *mod, | 3682 | static int ftrace_process_locs(struct module *mod, |
| 3703 | unsigned long *start, | 3683 | unsigned long *start, |
| 3704 | unsigned long *end) | 3684 | unsigned long *end) |
| 3705 | { | 3685 | { |
| 3686 | struct ftrace_page *start_pg; | ||
| 3706 | struct ftrace_page *pg; | 3687 | struct ftrace_page *pg; |
| 3688 | struct dyn_ftrace *rec; | ||
| 3707 | unsigned long count; | 3689 | unsigned long count; |
| 3708 | unsigned long *p; | 3690 | unsigned long *p; |
| 3709 | unsigned long addr; | 3691 | unsigned long addr; |
| @@ -3715,8 +3697,11 @@ static int ftrace_process_locs(struct module *mod, | |||
| 3715 | if (!count) | 3697 | if (!count) |
| 3716 | return 0; | 3698 | return 0; |
| 3717 | 3699 | ||
| 3718 | pg = ftrace_allocate_pages(count); | 3700 | sort(start, count, sizeof(*start), |
| 3719 | if (!pg) | 3701 | ftrace_cmp_ips, ftrace_swap_ips); |
| 3702 | |||
| 3703 | start_pg = ftrace_allocate_pages(count); | ||
| 3704 | if (!start_pg) | ||
| 3720 | return -ENOMEM; | 3705 | return -ENOMEM; |
| 3721 | 3706 | ||
| 3722 | mutex_lock(&ftrace_lock); | 3707 | mutex_lock(&ftrace_lock); |
| @@ -3729,7 +3714,7 @@ static int ftrace_process_locs(struct module *mod, | |||
| 3729 | if (!mod) { | 3714 | if (!mod) { |
| 3730 | WARN_ON(ftrace_pages || ftrace_pages_start); | 3715 | WARN_ON(ftrace_pages || ftrace_pages_start); |
| 3731 | /* First initialization */ | 3716 | /* First initialization */ |
| 3732 | ftrace_pages = ftrace_pages_start = pg; | 3717 | ftrace_pages = ftrace_pages_start = start_pg; |
| 3733 | } else { | 3718 | } else { |
| 3734 | if (!ftrace_pages) | 3719 | if (!ftrace_pages) |
| 3735 | goto out; | 3720 | goto out; |
| @@ -3740,11 +3725,11 @@ static int ftrace_process_locs(struct module *mod, | |||
| 3740 | ftrace_pages = ftrace_pages->next; | 3725 | ftrace_pages = ftrace_pages->next; |
| 3741 | } | 3726 | } |
| 3742 | 3727 | ||
| 3743 | ftrace_pages->next = pg; | 3728 | ftrace_pages->next = start_pg; |
| 3744 | ftrace_pages = pg; | ||
| 3745 | } | 3729 | } |
| 3746 | 3730 | ||
| 3747 | p = start; | 3731 | p = start; |
| 3732 | pg = start_pg; | ||
| 3748 | while (p < end) { | 3733 | while (p < end) { |
| 3749 | addr = ftrace_call_adjust(*p++); | 3734 | addr = ftrace_call_adjust(*p++); |
| 3750 | /* | 3735 | /* |
| @@ -3755,17 +3740,26 @@ static int ftrace_process_locs(struct module *mod, | |||
| 3755 | */ | 3740 | */ |
| 3756 | if (!addr) | 3741 | if (!addr) |
| 3757 | continue; | 3742 | continue; |
| 3758 | if (!ftrace_record_ip(addr)) | 3743 | |
| 3759 | break; | 3744 | if (pg->index == pg->size) { |
| 3745 | /* We should have allocated enough */ | ||
| 3746 | if (WARN_ON(!pg->next)) | ||
| 3747 | break; | ||
| 3748 | pg = pg->next; | ||
| 3749 | } | ||
| 3750 | |||
| 3751 | rec = &pg->records[pg->index++]; | ||
| 3752 | rec->ip = addr; | ||
| 3760 | } | 3753 | } |
| 3761 | 3754 | ||
| 3762 | /* These new locations need to be initialized */ | 3755 | /* We should have used all pages */ |
| 3763 | ftrace_new_pgs = pg; | 3756 | WARN_ON(pg->next); |
| 3757 | |||
| 3758 | /* Assign the last page to ftrace_pages */ | ||
| 3759 | ftrace_pages = pg; | ||
| 3764 | 3760 | ||
| 3765 | /* Make each individual set of pages sorted by ips */ | 3761 | /* These new locations need to be initialized */ |
| 3766 | for (; pg; pg = pg->next) | 3762 | ftrace_new_pgs = start_pg; |
| 3767 | sort(pg->records, pg->index, sizeof(struct dyn_ftrace), | ||
| 3768 | ftrace_cmp_recs, ftrace_swap_recs); | ||
| 3769 | 3763 | ||
| 3770 | /* | 3764 | /* |
| 3771 | * We only need to disable interrupts on start up | 3765 | * We only need to disable interrupts on start up |
