diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
| -rw-r--r-- | kernel/trace/ftrace.c | 135 | 
1 files changed, 65 insertions, 70 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index e51a1bcb7bed..83783579378f 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c  | |||
| @@ -22,7 +22,6 @@ | |||
| 22 | #include <linux/hardirq.h> | 22 | #include <linux/hardirq.h> | 
| 23 | #include <linux/kthread.h> | 23 | #include <linux/kthread.h> | 
| 24 | #include <linux/uaccess.h> | 24 | #include <linux/uaccess.h> | 
| 25 | #include <linux/kprobes.h> | ||
| 26 | #include <linux/ftrace.h> | 25 | #include <linux/ftrace.h> | 
| 27 | #include <linux/sysctl.h> | 26 | #include <linux/sysctl.h> | 
| 28 | #include <linux/ctype.h> | 27 | #include <linux/ctype.h> | 
| @@ -898,36 +897,6 @@ static struct dyn_ftrace *ftrace_free_records; | |||
| 898 | } \ | 897 | } \ | 
| 899 | } | 898 | } | 
| 900 | 899 | ||
| 901 | #ifdef CONFIG_KPROBES | ||
| 902 | |||
| 903 | static int frozen_record_count; | ||
| 904 | |||
| 905 | static inline void freeze_record(struct dyn_ftrace *rec) | ||
| 906 | { | ||
| 907 | if (!(rec->flags & FTRACE_FL_FROZEN)) { | ||
| 908 | rec->flags |= FTRACE_FL_FROZEN; | ||
| 909 | frozen_record_count++; | ||
| 910 | } | ||
| 911 | } | ||
| 912 | |||
| 913 | static inline void unfreeze_record(struct dyn_ftrace *rec) | ||
| 914 | { | ||
| 915 | if (rec->flags & FTRACE_FL_FROZEN) { | ||
| 916 | rec->flags &= ~FTRACE_FL_FROZEN; | ||
| 917 | frozen_record_count--; | ||
| 918 | } | ||
| 919 | } | ||
| 920 | |||
| 921 | static inline int record_frozen(struct dyn_ftrace *rec) | ||
| 922 | { | ||
| 923 | return rec->flags & FTRACE_FL_FROZEN; | ||
| 924 | } | ||
| 925 | #else | ||
| 926 | # define freeze_record(rec) ({ 0; }) | ||
| 927 | # define unfreeze_record(rec) ({ 0; }) | ||
| 928 | # define record_frozen(rec) ({ 0; }) | ||
| 929 | #endif /* CONFIG_KPROBES */ | ||
| 930 | |||
| 931 | static void ftrace_free_rec(struct dyn_ftrace *rec) | 900 | static void ftrace_free_rec(struct dyn_ftrace *rec) | 
| 932 | { | 901 | { | 
| 933 | rec->freelist = ftrace_free_records; | 902 | rec->freelist = ftrace_free_records; | 
| @@ -1025,6 +994,21 @@ static void ftrace_bug(int failed, unsigned long ip) | |||
| 1025 | } | 994 | } | 
| 1026 | 995 | ||
| 1027 | 996 | ||
| 997 | /* Return 1 if the address range is reserved for ftrace */ | ||
| 998 | int ftrace_text_reserved(void *start, void *end) | ||
| 999 | { | ||
| 1000 | struct dyn_ftrace *rec; | ||
| 1001 | struct ftrace_page *pg; | ||
| 1002 | |||
| 1003 | do_for_each_ftrace_rec(pg, rec) { | ||
| 1004 | if (rec->ip <= (unsigned long)end && | ||
| 1005 | rec->ip + MCOUNT_INSN_SIZE > (unsigned long)start) | ||
| 1006 | return 1; | ||
| 1007 | } while_for_each_ftrace_rec(); | ||
| 1008 | return 0; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | |||
| 1028 | static int | 1012 | static int | 
| 1029 | __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | 1013 | __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | 
| 1030 | { | 1014 | { | 
| @@ -1076,14 +1060,6 @@ static void ftrace_replace_code(int enable) | |||
| 1076 | !(rec->flags & FTRACE_FL_CONVERTED)) | 1060 | !(rec->flags & FTRACE_FL_CONVERTED)) | 
| 1077 | continue; | 1061 | continue; | 
| 1078 | 1062 | ||
| 1079 | /* ignore updates to this record's mcount site */ | ||
| 1080 | if (get_kprobe((void *)rec->ip)) { | ||
| 1081 | freeze_record(rec); | ||
| 1082 | continue; | ||
| 1083 | } else { | ||
| 1084 | unfreeze_record(rec); | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | failed = __ftrace_replace_code(rec, enable); | 1063 | failed = __ftrace_replace_code(rec, enable); | 
| 1088 | if (failed) { | 1064 | if (failed) { | 
| 1089 | rec->flags |= FTRACE_FL_FAILED; | 1065 | rec->flags |= FTRACE_FL_FAILED; | 
| @@ -1690,7 +1666,7 @@ ftrace_regex_lseek(struct file *file, loff_t offset, int origin) | |||
| 1690 | static int ftrace_match(char *str, char *regex, int len, int type) | 1666 | static int ftrace_match(char *str, char *regex, int len, int type) | 
| 1691 | { | 1667 | { | 
| 1692 | int matched = 0; | 1668 | int matched = 0; | 
| 1693 | char *ptr; | 1669 | int slen; | 
| 1694 | 1670 | ||
| 1695 | switch (type) { | 1671 | switch (type) { | 
| 1696 | case MATCH_FULL: | 1672 | case MATCH_FULL: | 
| @@ -1706,8 +1682,8 @@ static int ftrace_match(char *str, char *regex, int len, int type) | |||
| 1706 | matched = 1; | 1682 | matched = 1; | 
| 1707 | break; | 1683 | break; | 
| 1708 | case MATCH_END_ONLY: | 1684 | case MATCH_END_ONLY: | 
| 1709 | ptr = strstr(str, regex); | 1685 | slen = strlen(str); | 
| 1710 | if (ptr && (ptr[len] == 0)) | 1686 | if (slen >= len && memcmp(str + slen - len, regex, len) == 0) | 
| 1711 | matched = 1; | 1687 | matched = 1; | 
| 1712 | break; | 1688 | break; | 
| 1713 | } | 1689 | } | 
| @@ -1724,7 +1700,7 @@ ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type) | |||
| 1724 | return ftrace_match(str, regex, len, type); | 1700 | return ftrace_match(str, regex, len, type); | 
| 1725 | } | 1701 | } | 
| 1726 | 1702 | ||
| 1727 | static void ftrace_match_records(char *buff, int len, int enable) | 1703 | static int ftrace_match_records(char *buff, int len, int enable) | 
| 1728 | { | 1704 | { | 
| 1729 | unsigned int search_len; | 1705 | unsigned int search_len; | 
| 1730 | struct ftrace_page *pg; | 1706 | struct ftrace_page *pg; | 
| @@ -1733,6 +1709,7 @@ static void ftrace_match_records(char *buff, int len, int enable) | |||
| 1733 | char *search; | 1709 | char *search; | 
| 1734 | int type; | 1710 | int type; | 
| 1735 | int not; | 1711 | int not; | 
| 1712 | int found = 0; | ||
| 1736 | 1713 | ||
| 1737 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1714 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 
| 1738 | type = filter_parse_regex(buff, len, &search, ¬); | 1715 | type = filter_parse_regex(buff, len, &search, ¬); | 
| @@ -1750,6 +1727,7 @@ static void ftrace_match_records(char *buff, int len, int enable) | |||
| 1750 | rec->flags &= ~flag; | 1727 | rec->flags &= ~flag; | 
| 1751 | else | 1728 | else | 
| 1752 | rec->flags |= flag; | 1729 | rec->flags |= flag; | 
| 1730 | found = 1; | ||
| 1753 | } | 1731 | } | 
| 1754 | /* | 1732 | /* | 
| 1755 | * Only enable filtering if we have a function that | 1733 | * Only enable filtering if we have a function that | 
| @@ -1759,6 +1737,8 @@ static void ftrace_match_records(char *buff, int len, int enable) | |||
| 1759 | ftrace_filtered = 1; | 1737 | ftrace_filtered = 1; | 
| 1760 | } while_for_each_ftrace_rec(); | 1738 | } while_for_each_ftrace_rec(); | 
| 1761 | mutex_unlock(&ftrace_lock); | 1739 | mutex_unlock(&ftrace_lock); | 
| 1740 | |||
| 1741 | return found; | ||
| 1762 | } | 1742 | } | 
| 1763 | 1743 | ||
| 1764 | static int | 1744 | static int | 
| @@ -1780,7 +1760,7 @@ ftrace_match_module_record(struct dyn_ftrace *rec, char *mod, | |||
| 1780 | return 1; | 1760 | return 1; | 
| 1781 | } | 1761 | } | 
| 1782 | 1762 | ||
| 1783 | static void ftrace_match_module_records(char *buff, char *mod, int enable) | 1763 | static int ftrace_match_module_records(char *buff, char *mod, int enable) | 
| 1784 | { | 1764 | { | 
| 1785 | unsigned search_len = 0; | 1765 | unsigned search_len = 0; | 
| 1786 | struct ftrace_page *pg; | 1766 | struct ftrace_page *pg; | 
| @@ -1789,6 +1769,7 @@ static void ftrace_match_module_records(char *buff, char *mod, int enable) | |||
| 1789 | char *search = buff; | 1769 | char *search = buff; | 
| 1790 | unsigned long flag; | 1770 | unsigned long flag; | 
| 1791 | int not = 0; | 1771 | int not = 0; | 
| 1772 | int found = 0; | ||
| 1792 | 1773 | ||
| 1793 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1774 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 
| 1794 | 1775 | ||
| @@ -1819,12 +1800,15 @@ static void ftrace_match_module_records(char *buff, char *mod, int enable) | |||
| 1819 | rec->flags &= ~flag; | 1800 | rec->flags &= ~flag; | 
| 1820 | else | 1801 | else | 
| 1821 | rec->flags |= flag; | 1802 | rec->flags |= flag; | 
| 1803 | found = 1; | ||
| 1822 | } | 1804 | } | 
| 1823 | if (enable && (rec->flags & FTRACE_FL_FILTER)) | 1805 | if (enable && (rec->flags & FTRACE_FL_FILTER)) | 
| 1824 | ftrace_filtered = 1; | 1806 | ftrace_filtered = 1; | 
| 1825 | 1807 | ||
| 1826 | } while_for_each_ftrace_rec(); | 1808 | } while_for_each_ftrace_rec(); | 
| 1827 | mutex_unlock(&ftrace_lock); | 1809 | mutex_unlock(&ftrace_lock); | 
| 1810 | |||
| 1811 | return found; | ||
| 1828 | } | 1812 | } | 
| 1829 | 1813 | ||
| 1830 | /* | 1814 | /* | 
| @@ -1853,8 +1837,9 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | |||
| 1853 | if (!strlen(mod)) | 1837 | if (!strlen(mod)) | 
| 1854 | return -EINVAL; | 1838 | return -EINVAL; | 
| 1855 | 1839 | ||
| 1856 | ftrace_match_module_records(func, mod, enable); | 1840 | if (ftrace_match_module_records(func, mod, enable)) | 
| 1857 | return 0; | 1841 | return 0; | 
| 1842 | return -EINVAL; | ||
| 1858 | } | 1843 | } | 
| 1859 | 1844 | ||
| 1860 | static struct ftrace_func_command ftrace_mod_cmd = { | 1845 | static struct ftrace_func_command ftrace_mod_cmd = { | 
| @@ -2151,8 +2136,9 @@ static int ftrace_process_regex(char *buff, int len, int enable) | |||
| 2151 | func = strsep(&next, ":"); | 2136 | func = strsep(&next, ":"); | 
| 2152 | 2137 | ||
| 2153 | if (!next) { | 2138 | if (!next) { | 
| 2154 | ftrace_match_records(func, len, enable); | 2139 | if (ftrace_match_records(func, len, enable)) | 
| 2155 | return 0; | 2140 | return 0; | 
| 2141 | return ret; | ||
| 2156 | } | 2142 | } | 
| 2157 | 2143 | ||
| 2158 | /* command found */ | 2144 | /* command found */ | 
| @@ -2198,10 +2184,9 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, | |||
| 2198 | !trace_parser_cont(parser)) { | 2184 | !trace_parser_cont(parser)) { | 
| 2199 | ret = ftrace_process_regex(parser->buffer, | 2185 | ret = ftrace_process_regex(parser->buffer, | 
| 2200 | parser->idx, enable); | 2186 | parser->idx, enable); | 
| 2187 | trace_parser_clear(parser); | ||
| 2201 | if (ret) | 2188 | if (ret) | 
| 2202 | goto out_unlock; | 2189 | goto out_unlock; | 
| 2203 | |||
| 2204 | trace_parser_clear(parser); | ||
| 2205 | } | 2190 | } | 
| 2206 | 2191 | ||
| 2207 | ret = read; | 2192 | ret = read; | 
| @@ -2417,6 +2402,7 @@ static const struct file_operations ftrace_notrace_fops = { | |||
| 2417 | static DEFINE_MUTEX(graph_lock); | 2402 | static DEFINE_MUTEX(graph_lock); | 
| 2418 | 2403 | ||
| 2419 | int ftrace_graph_count; | 2404 | int ftrace_graph_count; | 
| 2405 | int ftrace_graph_filter_enabled; | ||
| 2420 | unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; | 2406 | unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; | 
| 2421 | 2407 | ||
| 2422 | static void * | 2408 | static void * | 
| @@ -2439,7 +2425,7 @@ static void *g_start(struct seq_file *m, loff_t *pos) | |||
| 2439 | mutex_lock(&graph_lock); | 2425 | mutex_lock(&graph_lock); | 
| 2440 | 2426 | ||
| 2441 | /* Nothing, tell g_show to print all functions are enabled */ | 2427 | /* Nothing, tell g_show to print all functions are enabled */ | 
| 2442 | if (!ftrace_graph_count && !*pos) | 2428 | if (!ftrace_graph_filter_enabled && !*pos) | 
| 2443 | return (void *)1; | 2429 | return (void *)1; | 
| 2444 | 2430 | ||
| 2445 | return __g_next(m, pos); | 2431 | return __g_next(m, pos); | 
| @@ -2485,6 +2471,7 @@ ftrace_graph_open(struct inode *inode, struct file *file) | |||
| 2485 | mutex_lock(&graph_lock); | 2471 | mutex_lock(&graph_lock); | 
| 2486 | if ((file->f_mode & FMODE_WRITE) && | 2472 | if ((file->f_mode & FMODE_WRITE) && | 
| 2487 | (file->f_flags & O_TRUNC)) { | 2473 | (file->f_flags & O_TRUNC)) { | 
| 2474 | ftrace_graph_filter_enabled = 0; | ||
| 2488 | ftrace_graph_count = 0; | 2475 | ftrace_graph_count = 0; | 
| 2489 | memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); | 2476 | memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); | 
| 2490 | } | 2477 | } | 
| @@ -2510,7 +2497,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer) | |||
| 2510 | struct dyn_ftrace *rec; | 2497 | struct dyn_ftrace *rec; | 
| 2511 | struct ftrace_page *pg; | 2498 | struct ftrace_page *pg; | 
| 2512 | int search_len; | 2499 | int search_len; | 
| 2513 | int found = 0; | 2500 | int fail = 1; | 
| 2514 | int type, not; | 2501 | int type, not; | 
| 2515 | char *search; | 2502 | char *search; | 
| 2516 | bool exists; | 2503 | bool exists; | 
| @@ -2521,38 +2508,51 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer) | |||
| 2521 | 2508 | ||
| 2522 | /* decode regex */ | 2509 | /* decode regex */ | 
| 2523 | type = filter_parse_regex(buffer, strlen(buffer), &search, ¬); | 2510 | type = filter_parse_regex(buffer, strlen(buffer), &search, ¬); | 
| 2524 | if (not) | 2511 | if (!not && *idx >= FTRACE_GRAPH_MAX_FUNCS) | 
| 2525 | return -EINVAL; | 2512 | return -EBUSY; | 
| 2526 | 2513 | ||
| 2527 | search_len = strlen(search); | 2514 | search_len = strlen(search); | 
| 2528 | 2515 | ||
| 2529 | mutex_lock(&ftrace_lock); | 2516 | mutex_lock(&ftrace_lock); | 
| 2530 | do_for_each_ftrace_rec(pg, rec) { | 2517 | do_for_each_ftrace_rec(pg, rec) { | 
| 2531 | 2518 | ||
| 2532 | if (*idx >= FTRACE_GRAPH_MAX_FUNCS) | ||
| 2533 | break; | ||
| 2534 | |||
| 2535 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) | 2519 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) | 
| 2536 | continue; | 2520 | continue; | 
| 2537 | 2521 | ||
| 2538 | if (ftrace_match_record(rec, search, search_len, type)) { | 2522 | if (ftrace_match_record(rec, search, search_len, type)) { | 
| 2539 | /* ensure it is not already in the array */ | 2523 | /* if it is in the array */ | 
| 2540 | exists = false; | 2524 | exists = false; | 
| 2541 | for (i = 0; i < *idx; i++) | 2525 | for (i = 0; i < *idx; i++) { | 
| 2542 | if (array[i] == rec->ip) { | 2526 | if (array[i] == rec->ip) { | 
| 2543 | exists = true; | 2527 | exists = true; | 
| 2544 | break; | 2528 | break; | 
| 2545 | } | 2529 | } | 
| 2546 | if (!exists) { | 2530 | } | 
| 2547 | array[(*idx)++] = rec->ip; | 2531 | |
| 2548 | found = 1; | 2532 | if (!not) { | 
| 2533 | fail = 0; | ||
| 2534 | if (!exists) { | ||
| 2535 | array[(*idx)++] = rec->ip; | ||
| 2536 | if (*idx >= FTRACE_GRAPH_MAX_FUNCS) | ||
| 2537 | goto out; | ||
| 2538 | } | ||
| 2539 | } else { | ||
| 2540 | if (exists) { | ||
| 2541 | array[i] = array[--(*idx)]; | ||
| 2542 | array[*idx] = 0; | ||
| 2543 | fail = 0; | ||
| 2544 | } | ||
| 2549 | } | 2545 | } | 
| 2550 | } | 2546 | } | 
| 2551 | } while_for_each_ftrace_rec(); | 2547 | } while_for_each_ftrace_rec(); | 
| 2552 | 2548 | out: | |
| 2553 | mutex_unlock(&ftrace_lock); | 2549 | mutex_unlock(&ftrace_lock); | 
| 2554 | 2550 | ||
| 2555 | return found ? 0 : -EINVAL; | 2551 | if (fail) | 
| 2552 | return -EINVAL; | ||
| 2553 | |||
| 2554 | ftrace_graph_filter_enabled = 1; | ||
| 2555 | return 0; | ||
| 2556 | } | 2556 | } | 
| 2557 | 2557 | ||
| 2558 | static ssize_t | 2558 | static ssize_t | 
| @@ -2562,16 +2562,11 @@ ftrace_graph_write(struct file *file, const char __user *ubuf, | |||
| 2562 | struct trace_parser parser; | 2562 | struct trace_parser parser; | 
| 2563 | ssize_t read, ret; | 2563 | ssize_t read, ret; | 
| 2564 | 2564 | ||
| 2565 | if (!cnt || cnt < 0) | 2565 | if (!cnt) | 
| 2566 | return 0; | 2566 | return 0; | 
| 2567 | 2567 | ||
| 2568 | mutex_lock(&graph_lock); | 2568 | mutex_lock(&graph_lock); | 
| 2569 | 2569 | ||
| 2570 | if (ftrace_graph_count >= FTRACE_GRAPH_MAX_FUNCS) { | ||
| 2571 | ret = -EBUSY; | ||
| 2572 | goto out_unlock; | ||
| 2573 | } | ||
| 2574 | |||
| 2575 | if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) { | 2570 | if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) { | 
| 2576 | ret = -ENOMEM; | 2571 | ret = -ENOMEM; | 
| 2577 | goto out_unlock; | 2572 | goto out_unlock; | 
