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 /kernel/trace | |
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>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/ftrace.c | 176 |
1 files changed, 150 insertions, 26 deletions
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 */ |