diff options
| author | Takashi Iwai <tiwai@suse.de> | 2011-08-08 08:30:29 -0400 |
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2011-08-08 08:30:29 -0400 |
| commit | 0a2d31b62dba9b5b92a38c67c9cc42630513662a (patch) | |
| tree | f755d74ec85248de645e10c45ed1a2ed467530f6 /kernel/trace/ftrace.c | |
| parent | 8039290a91c5dc4414093c086987a5d7738fe2fd (diff) | |
| parent | df944f66784e6d4f2f50739263a4947885d8b6ae (diff) | |
Merge branch 'fix/kconfig' into for-linus
Diffstat (limited to 'kernel/trace/ftrace.c')
| -rw-r--r-- | kernel/trace/ftrace.c | 156 |
1 files changed, 112 insertions, 44 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1ee417fcbfa5..c3e4575e7829 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -32,7 +32,6 @@ | |||
| 32 | 32 | ||
| 33 | #include <trace/events/sched.h> | 33 | #include <trace/events/sched.h> |
| 34 | 34 | ||
| 35 | #include <asm/ftrace.h> | ||
| 36 | #include <asm/setup.h> | 35 | #include <asm/setup.h> |
| 37 | 36 | ||
| 38 | #include "trace_output.h" | 37 | #include "trace_output.h" |
| @@ -82,14 +81,14 @@ static int ftrace_disabled __read_mostly; | |||
| 82 | 81 | ||
| 83 | static DEFINE_MUTEX(ftrace_lock); | 82 | static DEFINE_MUTEX(ftrace_lock); |
| 84 | 83 | ||
| 85 | static struct ftrace_ops ftrace_list_end __read_mostly = | 84 | static struct ftrace_ops ftrace_list_end __read_mostly = { |
| 86 | { | ||
| 87 | .func = ftrace_stub, | 85 | .func = ftrace_stub, |
| 88 | }; | 86 | }; |
| 89 | 87 | ||
| 90 | static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; | 88 | static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; |
| 91 | static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; | 89 | static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; |
| 92 | ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; | 90 | ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; |
| 91 | static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub; | ||
| 93 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; | 92 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; |
| 94 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; | 93 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; |
| 95 | static struct ftrace_ops global_ops; | 94 | static struct ftrace_ops global_ops; |
| @@ -148,9 +147,11 @@ void clear_ftrace_function(void) | |||
| 148 | { | 147 | { |
| 149 | ftrace_trace_function = ftrace_stub; | 148 | ftrace_trace_function = ftrace_stub; |
| 150 | __ftrace_trace_function = ftrace_stub; | 149 | __ftrace_trace_function = ftrace_stub; |
| 150 | __ftrace_trace_function_delay = ftrace_stub; | ||
| 151 | ftrace_pid_function = ftrace_stub; | 151 | ftrace_pid_function = ftrace_stub; |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | #undef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST | ||
| 154 | #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST | 155 | #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST |
| 155 | /* | 156 | /* |
| 156 | * For those archs that do not test ftrace_trace_stop in their | 157 | * For those archs that do not test ftrace_trace_stop in their |
| @@ -210,7 +211,12 @@ static void update_ftrace_function(void) | |||
| 210 | #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST | 211 | #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST |
| 211 | ftrace_trace_function = func; | 212 | ftrace_trace_function = func; |
| 212 | #else | 213 | #else |
| 214 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 215 | /* do not update till all functions have been modified */ | ||
| 216 | __ftrace_trace_function_delay = func; | ||
| 217 | #else | ||
| 213 | __ftrace_trace_function = func; | 218 | __ftrace_trace_function = func; |
| 219 | #endif | ||
| 214 | ftrace_trace_function = ftrace_test_stop_func; | 220 | ftrace_trace_function = ftrace_test_stop_func; |
| 215 | #endif | 221 | #endif |
| 216 | } | 222 | } |
| @@ -785,8 +791,7 @@ static void unregister_ftrace_profiler(void) | |||
| 785 | unregister_ftrace_graph(); | 791 | unregister_ftrace_graph(); |
| 786 | } | 792 | } |
| 787 | #else | 793 | #else |
| 788 | static struct ftrace_ops ftrace_profile_ops __read_mostly = | 794 | static struct ftrace_ops ftrace_profile_ops __read_mostly = { |
| 789 | { | ||
| 790 | .func = function_profile_call, | 795 | .func = function_profile_call, |
| 791 | }; | 796 | }; |
| 792 | 797 | ||
| @@ -806,19 +811,10 @@ ftrace_profile_write(struct file *filp, const char __user *ubuf, | |||
| 806 | size_t cnt, loff_t *ppos) | 811 | size_t cnt, loff_t *ppos) |
| 807 | { | 812 | { |
| 808 | unsigned long val; | 813 | unsigned long val; |
| 809 | char buf[64]; /* big enough to hold a number */ | ||
| 810 | int ret; | 814 | int ret; |
| 811 | 815 | ||
| 812 | if (cnt >= sizeof(buf)) | 816 | ret = kstrtoul_from_user(ubuf, cnt, 10, &val); |
| 813 | return -EINVAL; | 817 | if (ret) |
| 814 | |||
| 815 | if (copy_from_user(&buf, ubuf, cnt)) | ||
| 816 | return -EFAULT; | ||
| 817 | |||
| 818 | buf[cnt] = 0; | ||
| 819 | |||
| 820 | ret = strict_strtoul(buf, 10, &val); | ||
| 821 | if (ret < 0) | ||
| 822 | return ret; | 818 | return ret; |
| 823 | 819 | ||
| 824 | val = !!val; | 820 | val = !!val; |
| @@ -1182,8 +1178,14 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash) | |||
| 1182 | return NULL; | 1178 | return NULL; |
| 1183 | } | 1179 | } |
| 1184 | 1180 | ||
| 1181 | static void | ||
| 1182 | ftrace_hash_rec_disable(struct ftrace_ops *ops, int filter_hash); | ||
| 1183 | static void | ||
| 1184 | ftrace_hash_rec_enable(struct ftrace_ops *ops, int filter_hash); | ||
| 1185 | |||
| 1185 | static int | 1186 | static int |
| 1186 | ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | 1187 | ftrace_hash_move(struct ftrace_ops *ops, int enable, |
| 1188 | struct ftrace_hash **dst, struct ftrace_hash *src) | ||
| 1187 | { | 1189 | { |
| 1188 | struct ftrace_func_entry *entry; | 1190 | struct ftrace_func_entry *entry; |
| 1189 | struct hlist_node *tp, *tn; | 1191 | struct hlist_node *tp, *tn; |
| @@ -1193,9 +1195,16 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
| 1193 | unsigned long key; | 1195 | unsigned long key; |
| 1194 | int size = src->count; | 1196 | int size = src->count; |
| 1195 | int bits = 0; | 1197 | int bits = 0; |
| 1198 | int ret; | ||
| 1196 | int i; | 1199 | int i; |
| 1197 | 1200 | ||
| 1198 | /* | 1201 | /* |
| 1202 | * Remove the current set, update the hash and add | ||
| 1203 | * them back. | ||
| 1204 | */ | ||
| 1205 | ftrace_hash_rec_disable(ops, enable); | ||
| 1206 | |||
| 1207 | /* | ||
| 1199 | * If the new source is empty, just free dst and assign it | 1208 | * If the new source is empty, just free dst and assign it |
| 1200 | * the empty_hash. | 1209 | * the empty_hash. |
| 1201 | */ | 1210 | */ |
| @@ -1215,9 +1224,10 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
| 1215 | if (bits > FTRACE_HASH_MAX_BITS) | 1224 | if (bits > FTRACE_HASH_MAX_BITS) |
| 1216 | bits = FTRACE_HASH_MAX_BITS; | 1225 | bits = FTRACE_HASH_MAX_BITS; |
| 1217 | 1226 | ||
| 1227 | ret = -ENOMEM; | ||
| 1218 | new_hash = alloc_ftrace_hash(bits); | 1228 | new_hash = alloc_ftrace_hash(bits); |
| 1219 | if (!new_hash) | 1229 | if (!new_hash) |
| 1220 | return -ENOMEM; | 1230 | goto out; |
| 1221 | 1231 | ||
| 1222 | size = 1 << src->size_bits; | 1232 | size = 1 << src->size_bits; |
| 1223 | for (i = 0; i < size; i++) { | 1233 | for (i = 0; i < size; i++) { |
| @@ -1236,7 +1246,16 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
| 1236 | rcu_assign_pointer(*dst, new_hash); | 1246 | rcu_assign_pointer(*dst, new_hash); |
| 1237 | free_ftrace_hash_rcu(old_hash); | 1247 | free_ftrace_hash_rcu(old_hash); |
| 1238 | 1248 | ||
| 1239 | return 0; | 1249 | ret = 0; |
| 1250 | out: | ||
| 1251 | /* | ||
| 1252 | * Enable regardless of ret: | ||
| 1253 | * On success, we enable the new hash. | ||
| 1254 | * On failure, we re-enable the original hash. | ||
| 1255 | */ | ||
| 1256 | ftrace_hash_rec_enable(ops, enable); | ||
| 1257 | |||
| 1258 | return ret; | ||
| 1240 | } | 1259 | } |
| 1241 | 1260 | ||
| 1242 | /* | 1261 | /* |
| @@ -1596,6 +1615,12 @@ static int __ftrace_modify_code(void *data) | |||
| 1596 | { | 1615 | { |
| 1597 | int *command = data; | 1616 | int *command = data; |
| 1598 | 1617 | ||
| 1618 | /* | ||
| 1619 | * Do not call function tracer while we update the code. | ||
| 1620 | * We are in stop machine, no worrying about races. | ||
| 1621 | */ | ||
| 1622 | function_trace_stop++; | ||
| 1623 | |||
| 1599 | if (*command & FTRACE_ENABLE_CALLS) | 1624 | if (*command & FTRACE_ENABLE_CALLS) |
| 1600 | ftrace_replace_code(1); | 1625 | ftrace_replace_code(1); |
| 1601 | else if (*command & FTRACE_DISABLE_CALLS) | 1626 | else if (*command & FTRACE_DISABLE_CALLS) |
| @@ -1609,6 +1634,18 @@ static int __ftrace_modify_code(void *data) | |||
| 1609 | else if (*command & FTRACE_STOP_FUNC_RET) | 1634 | else if (*command & FTRACE_STOP_FUNC_RET) |
| 1610 | ftrace_disable_ftrace_graph_caller(); | 1635 | ftrace_disable_ftrace_graph_caller(); |
| 1611 | 1636 | ||
| 1637 | #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST | ||
| 1638 | /* | ||
| 1639 | * For archs that call ftrace_test_stop_func(), we must | ||
| 1640 | * wait till after we update all the function callers | ||
| 1641 | * before we update the callback. This keeps different | ||
| 1642 | * ops that record different functions from corrupting | ||
| 1643 | * each other. | ||
| 1644 | */ | ||
| 1645 | __ftrace_trace_function = __ftrace_trace_function_delay; | ||
| 1646 | #endif | ||
| 1647 | function_trace_stop--; | ||
| 1648 | |||
| 1612 | return 0; | 1649 | return 0; |
| 1613 | } | 1650 | } |
| 1614 | 1651 | ||
| @@ -1744,10 +1781,36 @@ static cycle_t ftrace_update_time; | |||
| 1744 | static unsigned long ftrace_update_cnt; | 1781 | static unsigned long ftrace_update_cnt; |
| 1745 | unsigned long ftrace_update_tot_cnt; | 1782 | unsigned long ftrace_update_tot_cnt; |
| 1746 | 1783 | ||
| 1784 | static int ops_traces_mod(struct ftrace_ops *ops) | ||
| 1785 | { | ||
| 1786 | struct ftrace_hash *hash; | ||
| 1787 | |||
| 1788 | hash = ops->filter_hash; | ||
| 1789 | return !!(!hash || !hash->count); | ||
| 1790 | } | ||
| 1791 | |||
| 1747 | static int ftrace_update_code(struct module *mod) | 1792 | static int ftrace_update_code(struct module *mod) |
| 1748 | { | 1793 | { |
| 1749 | struct dyn_ftrace *p; | 1794 | struct dyn_ftrace *p; |
| 1750 | cycle_t start, stop; | 1795 | cycle_t start, stop; |
| 1796 | unsigned long ref = 0; | ||
| 1797 | |||
| 1798 | /* | ||
| 1799 | * When adding a module, we need to check if tracers are | ||
| 1800 | * currently enabled and if they are set to trace all functions. | ||
| 1801 | * If they are, we need to enable the module functions as well | ||
| 1802 | * as update the reference counts for those function records. | ||
| 1803 | */ | ||
| 1804 | if (mod) { | ||
| 1805 | struct ftrace_ops *ops; | ||
| 1806 | |||
| 1807 | for (ops = ftrace_ops_list; | ||
| 1808 | ops != &ftrace_list_end; ops = ops->next) { | ||
| 1809 | if (ops->flags & FTRACE_OPS_FL_ENABLED && | ||
| 1810 | ops_traces_mod(ops)) | ||
| 1811 | ref++; | ||
| 1812 | } | ||
| 1813 | } | ||
| 1751 | 1814 | ||
| 1752 | start = ftrace_now(raw_smp_processor_id()); | 1815 | start = ftrace_now(raw_smp_processor_id()); |
| 1753 | ftrace_update_cnt = 0; | 1816 | ftrace_update_cnt = 0; |
| @@ -1760,7 +1823,7 @@ static int ftrace_update_code(struct module *mod) | |||
| 1760 | 1823 | ||
| 1761 | p = ftrace_new_addrs; | 1824 | p = ftrace_new_addrs; |
| 1762 | ftrace_new_addrs = p->newlist; | 1825 | ftrace_new_addrs = p->newlist; |
| 1763 | p->flags = 0L; | 1826 | p->flags = ref; |
| 1764 | 1827 | ||
| 1765 | /* | 1828 | /* |
| 1766 | * Do the initial record conversion from mcount jump | 1829 | * Do the initial record conversion from mcount jump |
| @@ -1783,7 +1846,7 @@ static int ftrace_update_code(struct module *mod) | |||
| 1783 | * conversion puts the module to the correct state, thus | 1846 | * conversion puts the module to the correct state, thus |
| 1784 | * passing the ftrace_make_call check. | 1847 | * passing the ftrace_make_call check. |
| 1785 | */ | 1848 | */ |
| 1786 | if (ftrace_start_up) { | 1849 | if (ftrace_start_up && ref) { |
| 1787 | int failed = __ftrace_replace_code(p, 1); | 1850 | int failed = __ftrace_replace_code(p, 1); |
| 1788 | if (failed) { | 1851 | if (failed) { |
| 1789 | ftrace_bug(failed, p->ip); | 1852 | ftrace_bug(failed, p->ip); |
| @@ -2407,10 +2470,9 @@ ftrace_match_module_records(struct ftrace_hash *hash, char *buff, char *mod) | |||
| 2407 | */ | 2470 | */ |
| 2408 | 2471 | ||
| 2409 | static int | 2472 | static int |
| 2410 | ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | 2473 | ftrace_mod_callback(struct ftrace_hash *hash, |
| 2474 | char *func, char *cmd, char *param, int enable) | ||
| 2411 | { | 2475 | { |
| 2412 | struct ftrace_ops *ops = &global_ops; | ||
| 2413 | struct ftrace_hash *hash; | ||
| 2414 | char *mod; | 2476 | char *mod; |
| 2415 | int ret = -EINVAL; | 2477 | int ret = -EINVAL; |
| 2416 | 2478 | ||
| @@ -2430,11 +2492,6 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | |||
| 2430 | if (!strlen(mod)) | 2492 | if (!strlen(mod)) |
| 2431 | return ret; | 2493 | return ret; |
| 2432 | 2494 | ||
| 2433 | if (enable) | ||
| 2434 | hash = ops->filter_hash; | ||
| 2435 | else | ||
| 2436 | hash = ops->notrace_hash; | ||
| 2437 | |||
| 2438 | ret = ftrace_match_module_records(hash, func, mod); | 2495 | ret = ftrace_match_module_records(hash, func, mod); |
| 2439 | if (!ret) | 2496 | if (!ret) |
| 2440 | ret = -EINVAL; | 2497 | ret = -EINVAL; |
| @@ -2740,7 +2797,7 @@ static int ftrace_process_regex(struct ftrace_hash *hash, | |||
| 2740 | { | 2797 | { |
| 2741 | char *func, *command, *next = buff; | 2798 | char *func, *command, *next = buff; |
| 2742 | struct ftrace_func_command *p; | 2799 | struct ftrace_func_command *p; |
| 2743 | int ret; | 2800 | int ret = -EINVAL; |
| 2744 | 2801 | ||
| 2745 | func = strsep(&next, ":"); | 2802 | func = strsep(&next, ":"); |
| 2746 | 2803 | ||
| @@ -2760,7 +2817,7 @@ static int ftrace_process_regex(struct ftrace_hash *hash, | |||
| 2760 | mutex_lock(&ftrace_cmd_mutex); | 2817 | mutex_lock(&ftrace_cmd_mutex); |
| 2761 | list_for_each_entry(p, &ftrace_commands, list) { | 2818 | list_for_each_entry(p, &ftrace_commands, list) { |
| 2762 | if (strcmp(p->name, command) == 0) { | 2819 | if (strcmp(p->name, command) == 0) { |
| 2763 | ret = p->func(func, command, next, enable); | 2820 | ret = p->func(hash, func, command, next, enable); |
| 2764 | goto out_unlock; | 2821 | goto out_unlock; |
| 2765 | } | 2822 | } |
| 2766 | } | 2823 | } |
| @@ -2857,7 +2914,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, | |||
| 2857 | ftrace_match_records(hash, buf, len); | 2914 | ftrace_match_records(hash, buf, len); |
| 2858 | 2915 | ||
| 2859 | mutex_lock(&ftrace_lock); | 2916 | mutex_lock(&ftrace_lock); |
| 2860 | ret = ftrace_hash_move(orig_hash, hash); | 2917 | ret = ftrace_hash_move(ops, enable, orig_hash, hash); |
| 2918 | if (!ret && ops->flags & FTRACE_OPS_FL_ENABLED | ||
| 2919 | && ftrace_enabled) | ||
| 2920 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); | ||
| 2921 | |||
| 2861 | mutex_unlock(&ftrace_lock); | 2922 | mutex_unlock(&ftrace_lock); |
| 2862 | 2923 | ||
| 2863 | mutex_unlock(&ftrace_regex_lock); | 2924 | mutex_unlock(&ftrace_regex_lock); |
| @@ -3040,18 +3101,12 @@ ftrace_regex_release(struct inode *inode, struct file *file) | |||
| 3040 | orig_hash = &iter->ops->notrace_hash; | 3101 | orig_hash = &iter->ops->notrace_hash; |
| 3041 | 3102 | ||
| 3042 | mutex_lock(&ftrace_lock); | 3103 | mutex_lock(&ftrace_lock); |
| 3043 | /* | 3104 | ret = ftrace_hash_move(iter->ops, filter_hash, |
| 3044 | * Remove the current set, update the hash and add | 3105 | orig_hash, iter->hash); |
| 3045 | * them back. | 3106 | if (!ret && (iter->ops->flags & FTRACE_OPS_FL_ENABLED) |
| 3046 | */ | 3107 | && ftrace_enabled) |
| 3047 | ftrace_hash_rec_disable(iter->ops, filter_hash); | 3108 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); |
| 3048 | ret = ftrace_hash_move(orig_hash, iter->hash); | 3109 | |
| 3049 | if (!ret) { | ||
| 3050 | ftrace_hash_rec_enable(iter->ops, filter_hash); | ||
| 3051 | if (iter->ops->flags & FTRACE_OPS_FL_ENABLED | ||
| 3052 | && ftrace_enabled) | ||
| 3053 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); | ||
| 3054 | } | ||
| 3055 | mutex_unlock(&ftrace_lock); | 3110 | mutex_unlock(&ftrace_lock); |
| 3056 | } | 3111 | } |
| 3057 | free_ftrace_hash(iter->hash); | 3112 | free_ftrace_hash(iter->hash); |
| @@ -3330,6 +3385,7 @@ static int ftrace_process_locs(struct module *mod, | |||
| 3330 | { | 3385 | { |
| 3331 | unsigned long *p; | 3386 | unsigned long *p; |
| 3332 | unsigned long addr; | 3387 | unsigned long addr; |
| 3388 | unsigned long flags = 0; /* Shut up gcc */ | ||
| 3333 | 3389 | ||
| 3334 | mutex_lock(&ftrace_lock); | 3390 | mutex_lock(&ftrace_lock); |
| 3335 | p = start; | 3391 | p = start; |
| @@ -3346,7 +3402,19 @@ static int ftrace_process_locs(struct module *mod, | |||
| 3346 | ftrace_record_ip(addr); | 3402 | ftrace_record_ip(addr); |
| 3347 | } | 3403 | } |
| 3348 | 3404 | ||
| 3405 | /* | ||
| 3406 | * We only need to disable interrupts on start up | ||
| 3407 | * because we are modifying code that an interrupt | ||
| 3408 | * may execute, and the modification is not atomic. | ||
| 3409 | * But for modules, nothing runs the code we modify | ||
| 3410 | * until we are finished with it, and there's no | ||
| 3411 | * reason to cause large interrupt latencies while we do it. | ||
| 3412 | */ | ||
| 3413 | if (!mod) | ||
| 3414 | local_irq_save(flags); | ||
| 3349 | ftrace_update_code(mod); | 3415 | ftrace_update_code(mod); |
| 3416 | if (!mod) | ||
| 3417 | local_irq_restore(flags); | ||
| 3350 | mutex_unlock(&ftrace_lock); | 3418 | mutex_unlock(&ftrace_lock); |
| 3351 | 3419 | ||
| 3352 | return 0; | 3420 | return 0; |
