diff options
| author | Steven Rostedt <srostedt@redhat.com> | 2011-04-29 15:12:32 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2011-05-18 15:29:44 -0400 |
| commit | b448c4e3ae6d20108dba1d7833f2c0d3dbad87ce (patch) | |
| tree | 504e5a3640328458e652e41cfd2ed74e4652e5b3 | |
| parent | 94692349c4fc1bc74c19a28f9379509361a06a3b (diff) | |
ftrace: Replace FTRACE_FL_NOTRACE flag with a hash of ignored functions
To prepare for the accounting system that will allow multiple users of
the function tracer, having the FTRACE_FL_NOTRACE as a flag in the
dyn_trace record does not make sense.
All ftrace_ops will soon have a hash of functions they should trace
and not trace. By making a global hash of functions not to trace makes
this easier for the transition.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
| -rw-r--r-- | include/linux/ftrace.h | 1 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 176 |
2 files changed, 150 insertions, 27 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 32047449b309..fe0a90a09243 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -149,7 +149,6 @@ enum { | |||
| 149 | FTRACE_FL_FREE = (1 << 0), | 149 | FTRACE_FL_FREE = (1 << 0), |
| 150 | FTRACE_FL_FILTER = (1 << 1), | 150 | FTRACE_FL_FILTER = (1 << 1), |
| 151 | FTRACE_FL_ENABLED = (1 << 2), | 151 | FTRACE_FL_ENABLED = (1 << 2), |
| 152 | FTRACE_FL_NOTRACE = (1 << 3), | ||
| 153 | }; | 152 | }; |
| 154 | 153 | ||
| 155 | struct dyn_ftrace { | 154 | struct dyn_ftrace { |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index d3406346ced6..04c002a491fb 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -57,6 +57,7 @@ | |||
| 57 | /* hash bits for specific function selection */ | 57 | /* hash bits for specific function selection */ |
| 58 | #define FTRACE_HASH_BITS 7 | 58 | #define FTRACE_HASH_BITS 7 |
| 59 | #define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS) | 59 | #define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS) |
| 60 | #define FTRACE_HASH_MAX_BITS 10 | ||
| 60 | 61 | ||
| 61 | /* ftrace_enabled is a method to turn ftrace on or off */ | 62 | /* ftrace_enabled is a method to turn ftrace on or off */ |
| 62 | int ftrace_enabled __read_mostly; | 63 | int ftrace_enabled __read_mostly; |
| @@ -865,6 +866,22 @@ enum { | |||
| 865 | FTRACE_START_FUNC_RET = (1 << 3), | 866 | FTRACE_START_FUNC_RET = (1 << 3), |
| 866 | FTRACE_STOP_FUNC_RET = (1 << 4), | 867 | FTRACE_STOP_FUNC_RET = (1 << 4), |
| 867 | }; | 868 | }; |
| 869 | struct ftrace_func_entry { | ||
| 870 | struct hlist_node hlist; | ||
| 871 | unsigned long ip; | ||
| 872 | }; | ||
| 873 | |||
| 874 | struct ftrace_hash { | ||
| 875 | unsigned long size_bits; | ||
| 876 | struct hlist_head *buckets; | ||
| 877 | unsigned long count; | ||
| 878 | }; | ||
| 879 | |||
| 880 | static struct hlist_head notrace_buckets[1 << FTRACE_HASH_MAX_BITS]; | ||
| 881 | static struct ftrace_hash notrace_hash = { | ||
| 882 | .size_bits = FTRACE_HASH_MAX_BITS, | ||
| 883 | .buckets = notrace_buckets, | ||
| 884 | }; | ||
| 868 | 885 | ||
| 869 | static int ftrace_filtered; | 886 | static int ftrace_filtered; |
| 870 | 887 | ||
| @@ -889,6 +906,79 @@ static struct ftrace_page *ftrace_pages; | |||
| 889 | 906 | ||
| 890 | static struct dyn_ftrace *ftrace_free_records; | 907 | static struct dyn_ftrace *ftrace_free_records; |
| 891 | 908 | ||
| 909 | static struct ftrace_func_entry * | ||
| 910 | ftrace_lookup_ip(struct ftrace_hash *hash, unsigned long ip) | ||
| 911 | { | ||
| 912 | unsigned long key; | ||
| 913 | struct ftrace_func_entry *entry; | ||
| 914 | struct hlist_head *hhd; | ||
| 915 | struct hlist_node *n; | ||
| 916 | |||
| 917 | if (!hash->count) | ||
| 918 | return NULL; | ||
| 919 | |||
| 920 | if (hash->size_bits > 0) | ||
| 921 | key = hash_long(ip, hash->size_bits); | ||
| 922 | else | ||
| 923 | key = 0; | ||
| 924 | |||
| 925 | hhd = &hash->buckets[key]; | ||
| 926 | |||
| 927 | hlist_for_each_entry_rcu(entry, n, hhd, hlist) { | ||
| 928 | if (entry->ip == ip) | ||
| 929 | return entry; | ||
| 930 | } | ||
| 931 | return NULL; | ||
| 932 | } | ||
| 933 | |||
| 934 | static int add_hash_entry(struct ftrace_hash *hash, unsigned long ip) | ||
| 935 | { | ||
| 936 | struct ftrace_func_entry *entry; | ||
| 937 | struct hlist_head *hhd; | ||
| 938 | unsigned long key; | ||
| 939 | |||
| 940 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 941 | if (!entry) | ||
| 942 | return -ENOMEM; | ||
| 943 | |||
| 944 | if (hash->size_bits) | ||
| 945 | key = hash_long(ip, hash->size_bits); | ||
| 946 | else | ||
| 947 | key = 0; | ||
| 948 | |||
| 949 | entry->ip = ip; | ||
| 950 | hhd = &hash->buckets[key]; | ||
| 951 | hlist_add_head(&entry->hlist, hhd); | ||
| 952 | hash->count++; | ||
| 953 | |||
| 954 | return 0; | ||
| 955 | } | ||
| 956 | |||
| 957 | static void | ||
| 958 | remove_hash_entry(struct ftrace_hash *hash, | ||
| 959 | struct ftrace_func_entry *entry) | ||
| 960 | { | ||
| 961 | hlist_del(&entry->hlist); | ||
| 962 | kfree(entry); | ||
| 963 | hash->count--; | ||
| 964 | } | ||
| 965 | |||
| 966 | static void ftrace_hash_clear(struct ftrace_hash *hash) | ||
| 967 | { | ||
| 968 | struct hlist_head *hhd; | ||
| 969 | struct hlist_node *tp, *tn; | ||
| 970 | struct ftrace_func_entry *entry; | ||
| 971 | int size = 1 << hash->size_bits; | ||
| 972 | int i; | ||
| 973 | |||
| 974 | for (i = 0; i < size; i++) { | ||
| 975 | hhd = &hash->buckets[i]; | ||
| 976 | hlist_for_each_entry_safe(entry, tp, tn, hhd, hlist) | ||
| 977 | remove_hash_entry(hash, entry); | ||
| 978 | } | ||
| 979 | FTRACE_WARN_ON(hash->count); | ||
| 980 | } | ||
| 981 | |||
| 892 | /* | 982 | /* |
| 893 | * This is a double for. Do not use 'break' to break out of the loop, | 983 | * This is a double for. Do not use 'break' to break out of the loop, |
| 894 | * you must use a goto. | 984 | * you must use a goto. |
| @@ -1032,7 +1122,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | |||
| 1032 | * If we want to enable it and filtering is on, enable it only if | 1122 | * If we want to enable it and filtering is on, enable it only if |
| 1033 | * it's filtered | 1123 | * it's filtered |
| 1034 | */ | 1124 | */ |
| 1035 | if (enable && !(rec->flags & FTRACE_FL_NOTRACE)) { | 1125 | if (enable && !ftrace_lookup_ip(¬race_hash, rec->ip)) { |
| 1036 | if (!ftrace_filtered || (rec->flags & FTRACE_FL_FILTER)) | 1126 | if (!ftrace_filtered || (rec->flags & FTRACE_FL_FILTER)) |
| 1037 | flag = FTRACE_FL_ENABLED; | 1127 | flag = FTRACE_FL_ENABLED; |
| 1038 | } | 1128 | } |
| @@ -1465,7 +1555,7 @@ t_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 1465 | !(rec->flags & FTRACE_FL_FILTER)) || | 1555 | !(rec->flags & FTRACE_FL_FILTER)) || |
| 1466 | 1556 | ||
| 1467 | ((iter->flags & FTRACE_ITER_NOTRACE) && | 1557 | ((iter->flags & FTRACE_ITER_NOTRACE) && |
| 1468 | !(rec->flags & FTRACE_FL_NOTRACE))) { | 1558 | !ftrace_lookup_ip(¬race_hash, rec->ip))) { |
| 1469 | rec = NULL; | 1559 | rec = NULL; |
| 1470 | goto retry; | 1560 | goto retry; |
| 1471 | } | 1561 | } |
| @@ -1609,14 +1699,15 @@ static void ftrace_filter_reset(int enable) | |||
| 1609 | { | 1699 | { |
| 1610 | struct ftrace_page *pg; | 1700 | struct ftrace_page *pg; |
| 1611 | struct dyn_ftrace *rec; | 1701 | struct dyn_ftrace *rec; |
| 1612 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
| 1613 | 1702 | ||
| 1614 | mutex_lock(&ftrace_lock); | 1703 | mutex_lock(&ftrace_lock); |
| 1615 | if (enable) | 1704 | if (enable) { |
| 1616 | ftrace_filtered = 0; | 1705 | ftrace_filtered = 0; |
| 1617 | do_for_each_ftrace_rec(pg, rec) { | 1706 | do_for_each_ftrace_rec(pg, rec) { |
| 1618 | rec->flags &= ~type; | 1707 | rec->flags &= ~FTRACE_FL_FILTER; |
| 1619 | } while_for_each_ftrace_rec(); | 1708 | } while_for_each_ftrace_rec(); |
| 1709 | } else | ||
| 1710 | ftrace_hash_clear(¬race_hash); | ||
| 1620 | mutex_unlock(&ftrace_lock); | 1711 | mutex_unlock(&ftrace_lock); |
| 1621 | } | 1712 | } |
| 1622 | 1713 | ||
| @@ -1716,13 +1807,36 @@ static int ftrace_match(char *str, char *regex, int len, int type) | |||
| 1716 | return matched; | 1807 | return matched; |
| 1717 | } | 1808 | } |
| 1718 | 1809 | ||
| 1719 | static void | 1810 | static int |
| 1720 | update_record(struct dyn_ftrace *rec, unsigned long flag, int not) | 1811 | update_record(struct dyn_ftrace *rec, int enable, int not) |
| 1721 | { | 1812 | { |
| 1722 | if (not) | 1813 | struct ftrace_func_entry *entry; |
| 1723 | rec->flags &= ~flag; | 1814 | struct ftrace_hash *hash = ¬race_hash; |
| 1724 | else | 1815 | int ret = 0; |
| 1725 | rec->flags |= flag; | 1816 | |
| 1817 | if (enable) { | ||
| 1818 | if (not) | ||
| 1819 | rec->flags &= ~FTRACE_FL_FILTER; | ||
| 1820 | else | ||
| 1821 | rec->flags |= FTRACE_FL_FILTER; | ||
| 1822 | } else { | ||
| 1823 | if (not) { | ||
| 1824 | /* Do nothing if it doesn't exist */ | ||
| 1825 | entry = ftrace_lookup_ip(hash, rec->ip); | ||
| 1826 | if (!entry) | ||
| 1827 | return 0; | ||
| 1828 | |||
| 1829 | remove_hash_entry(hash, entry); | ||
| 1830 | } else { | ||
| 1831 | /* Do nothing if it exists */ | ||
| 1832 | entry = ftrace_lookup_ip(hash, rec->ip); | ||
| 1833 | if (entry) | ||
| 1834 | return 0; | ||
| 1835 | |||
| 1836 | ret = add_hash_entry(hash, rec->ip); | ||
| 1837 | } | ||
| 1838 | } | ||
| 1839 | return ret; | ||
| 1726 | } | 1840 | } |
| 1727 | 1841 | ||
| 1728 | static int | 1842 | static int |
| @@ -1754,16 +1868,14 @@ static int match_records(char *buff, int len, char *mod, int enable, int not) | |||
| 1754 | struct dyn_ftrace *rec; | 1868 | struct dyn_ftrace *rec; |
| 1755 | int type = MATCH_FULL; | 1869 | int type = MATCH_FULL; |
| 1756 | char *search = buff; | 1870 | char *search = buff; |
| 1757 | unsigned long flag; | ||
| 1758 | int found = 0; | 1871 | int found = 0; |
| 1872 | int ret; | ||
| 1759 | 1873 | ||
| 1760 | if (len) { | 1874 | if (len) { |
| 1761 | type = filter_parse_regex(buff, len, &search, ¬); | 1875 | type = filter_parse_regex(buff, len, &search, ¬); |
| 1762 | search_len = strlen(search); | 1876 | search_len = strlen(search); |
| 1763 | } | 1877 | } |
| 1764 | 1878 | ||
| 1765 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
| 1766 | |||
| 1767 | mutex_lock(&ftrace_lock); | 1879 | mutex_lock(&ftrace_lock); |
| 1768 | 1880 | ||
| 1769 | if (unlikely(ftrace_disabled)) | 1881 | if (unlikely(ftrace_disabled)) |
| @@ -1772,7 +1884,11 @@ static int match_records(char *buff, int len, char *mod, int enable, int not) | |||
| 1772 | do_for_each_ftrace_rec(pg, rec) { | 1884 | do_for_each_ftrace_rec(pg, rec) { |
| 1773 | 1885 | ||
| 1774 | if (ftrace_match_record(rec, mod, search, search_len, type)) { | 1886 | if (ftrace_match_record(rec, mod, search, search_len, type)) { |
| 1775 | update_record(rec, flag, not); | 1887 | ret = update_record(rec, enable, not); |
| 1888 | if (ret < 0) { | ||
| 1889 | found = ret; | ||
| 1890 | goto out_unlock; | ||
| 1891 | } | ||
| 1776 | found = 1; | 1892 | found = 1; |
| 1777 | } | 1893 | } |
| 1778 | /* | 1894 | /* |
| @@ -1821,6 +1937,7 @@ static int | |||
| 1821 | ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | 1937 | ftrace_mod_callback(char *func, char *cmd, char *param, int enable) |
| 1822 | { | 1938 | { |
| 1823 | char *mod; | 1939 | char *mod; |
| 1940 | int ret = -EINVAL; | ||
| 1824 | 1941 | ||
| 1825 | /* | 1942 | /* |
| 1826 | * cmd == 'mod' because we only registered this func | 1943 | * cmd == 'mod' because we only registered this func |
| @@ -1832,15 +1949,19 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | |||
| 1832 | 1949 | ||
| 1833 | /* we must have a module name */ | 1950 | /* we must have a module name */ |
| 1834 | if (!param) | 1951 | if (!param) |
| 1835 | return -EINVAL; | 1952 | return ret; |
| 1836 | 1953 | ||
| 1837 | mod = strsep(¶m, ":"); | 1954 | mod = strsep(¶m, ":"); |
| 1838 | if (!strlen(mod)) | 1955 | if (!strlen(mod)) |
| 1839 | return -EINVAL; | 1956 | return ret; |
| 1840 | 1957 | ||
| 1841 | if (ftrace_match_module_records(func, mod, enable)) | 1958 | ret = ftrace_match_module_records(func, mod, enable); |
| 1842 | return 0; | 1959 | if (!ret) |
| 1843 | return -EINVAL; | 1960 | ret = -EINVAL; |
| 1961 | if (ret < 0) | ||
| 1962 | return ret; | ||
| 1963 | |||
| 1964 | return 0; | ||
| 1844 | } | 1965 | } |
| 1845 | 1966 | ||
| 1846 | static struct ftrace_func_command ftrace_mod_cmd = { | 1967 | static struct ftrace_func_command ftrace_mod_cmd = { |
| @@ -2132,14 +2253,17 @@ static int ftrace_process_regex(char *buff, int len, int enable) | |||
| 2132 | { | 2253 | { |
| 2133 | char *func, *command, *next = buff; | 2254 | char *func, *command, *next = buff; |
| 2134 | struct ftrace_func_command *p; | 2255 | struct ftrace_func_command *p; |
| 2135 | int ret = -EINVAL; | 2256 | int ret; |
| 2136 | 2257 | ||
| 2137 | func = strsep(&next, ":"); | 2258 | func = strsep(&next, ":"); |
| 2138 | 2259 | ||
| 2139 | if (!next) { | 2260 | if (!next) { |
| 2140 | if (ftrace_match_records(func, len, enable)) | 2261 | ret = ftrace_match_records(func, len, enable); |
| 2141 | return 0; | 2262 | if (!ret) |
| 2142 | return ret; | 2263 | ret = -EINVAL; |
| 2264 | if (ret < 0) | ||
| 2265 | return ret; | ||
| 2266 | return 0; | ||
| 2143 | } | 2267 | } |
| 2144 | 2268 | ||
| 2145 | /* command found */ | 2269 | /* command found */ |
