diff options
| author | Ingo Molnar <mingo@elte.hu> | 2011-07-21 03:29:14 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2011-07-21 03:29:21 -0400 |
| commit | 492f73a303b488ffd67097b2351d54aa6e6c7c73 (patch) | |
| tree | 6e6c16fbd628bb5eb577cfc70a488ca286563e58 /kernel/trace | |
| parent | e08fbb78f03fe2c4f88824faf6f51ce6af185e11 (diff) | |
| parent | f7bc8b61f65726ff98f52e286b28e294499d7a08 (diff) | |
Merge branch 'perf/urgent' into perf/core
Merge reason: pick up the latest fixes - they won't make v3.0.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/ftrace.c | 42 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 1 | ||||
| -rw-r--r-- | kernel/trace/trace_events.c | 113 | ||||
| -rw-r--r-- | kernel/trace/trace_events_filter.c | 6 | ||||
| -rw-r--r-- | kernel/trace/trace_functions.c | 3 |
5 files changed, 135 insertions, 30 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index c997f7371c65..a0e246e2cee3 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -1732,10 +1732,36 @@ static cycle_t ftrace_update_time; | |||
| 1732 | static unsigned long ftrace_update_cnt; | 1732 | static unsigned long ftrace_update_cnt; |
| 1733 | unsigned long ftrace_update_tot_cnt; | 1733 | unsigned long ftrace_update_tot_cnt; |
| 1734 | 1734 | ||
| 1735 | static int ops_traces_mod(struct ftrace_ops *ops) | ||
| 1736 | { | ||
| 1737 | struct ftrace_hash *hash; | ||
| 1738 | |||
| 1739 | hash = ops->filter_hash; | ||
| 1740 | return !!(!hash || !hash->count); | ||
| 1741 | } | ||
| 1742 | |||
| 1735 | static int ftrace_update_code(struct module *mod) | 1743 | static int ftrace_update_code(struct module *mod) |
| 1736 | { | 1744 | { |
| 1737 | struct dyn_ftrace *p; | 1745 | struct dyn_ftrace *p; |
| 1738 | cycle_t start, stop; | 1746 | cycle_t start, stop; |
| 1747 | unsigned long ref = 0; | ||
| 1748 | |||
| 1749 | /* | ||
| 1750 | * When adding a module, we need to check if tracers are | ||
| 1751 | * currently enabled and if they are set to trace all functions. | ||
| 1752 | * If they are, we need to enable the module functions as well | ||
| 1753 | * as update the reference counts for those function records. | ||
| 1754 | */ | ||
| 1755 | if (mod) { | ||
| 1756 | struct ftrace_ops *ops; | ||
| 1757 | |||
| 1758 | for (ops = ftrace_ops_list; | ||
| 1759 | ops != &ftrace_list_end; ops = ops->next) { | ||
| 1760 | if (ops->flags & FTRACE_OPS_FL_ENABLED && | ||
| 1761 | ops_traces_mod(ops)) | ||
| 1762 | ref++; | ||
| 1763 | } | ||
| 1764 | } | ||
| 1739 | 1765 | ||
| 1740 | start = ftrace_now(raw_smp_processor_id()); | 1766 | start = ftrace_now(raw_smp_processor_id()); |
| 1741 | ftrace_update_cnt = 0; | 1767 | ftrace_update_cnt = 0; |
| @@ -1748,7 +1774,7 @@ static int ftrace_update_code(struct module *mod) | |||
| 1748 | 1774 | ||
| 1749 | p = ftrace_new_addrs; | 1775 | p = ftrace_new_addrs; |
| 1750 | ftrace_new_addrs = p->newlist; | 1776 | ftrace_new_addrs = p->newlist; |
| 1751 | p->flags = 0L; | 1777 | p->flags = ref; |
| 1752 | 1778 | ||
| 1753 | /* | 1779 | /* |
| 1754 | * Do the initial record conversion from mcount jump | 1780 | * Do the initial record conversion from mcount jump |
| @@ -1771,7 +1797,7 @@ static int ftrace_update_code(struct module *mod) | |||
| 1771 | * conversion puts the module to the correct state, thus | 1797 | * conversion puts the module to the correct state, thus |
| 1772 | * passing the ftrace_make_call check. | 1798 | * passing the ftrace_make_call check. |
| 1773 | */ | 1799 | */ |
| 1774 | if (ftrace_start_up) { | 1800 | if (ftrace_start_up && ref) { |
| 1775 | int failed = __ftrace_replace_code(p, 1); | 1801 | int failed = __ftrace_replace_code(p, 1); |
| 1776 | if (failed) { | 1802 | if (failed) { |
| 1777 | ftrace_bug(failed, p->ip); | 1803 | ftrace_bug(failed, p->ip); |
| @@ -2395,10 +2421,9 @@ ftrace_match_module_records(struct ftrace_hash *hash, char *buff, char *mod) | |||
| 2395 | */ | 2421 | */ |
| 2396 | 2422 | ||
| 2397 | static int | 2423 | static int |
| 2398 | ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | 2424 | ftrace_mod_callback(struct ftrace_hash *hash, |
| 2425 | char *func, char *cmd, char *param, int enable) | ||
| 2399 | { | 2426 | { |
| 2400 | struct ftrace_ops *ops = &global_ops; | ||
| 2401 | struct ftrace_hash *hash; | ||
| 2402 | char *mod; | 2427 | char *mod; |
| 2403 | int ret = -EINVAL; | 2428 | int ret = -EINVAL; |
| 2404 | 2429 | ||
| @@ -2418,11 +2443,6 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | |||
| 2418 | if (!strlen(mod)) | 2443 | if (!strlen(mod)) |
| 2419 | return ret; | 2444 | return ret; |
| 2420 | 2445 | ||
| 2421 | if (enable) | ||
| 2422 | hash = ops->filter_hash; | ||
| 2423 | else | ||
| 2424 | hash = ops->notrace_hash; | ||
| 2425 | |||
| 2426 | ret = ftrace_match_module_records(hash, func, mod); | 2446 | ret = ftrace_match_module_records(hash, func, mod); |
| 2427 | if (!ret) | 2447 | if (!ret) |
| 2428 | ret = -EINVAL; | 2448 | ret = -EINVAL; |
| @@ -2748,7 +2768,7 @@ static int ftrace_process_regex(struct ftrace_hash *hash, | |||
| 2748 | mutex_lock(&ftrace_cmd_mutex); | 2768 | mutex_lock(&ftrace_cmd_mutex); |
| 2749 | list_for_each_entry(p, &ftrace_commands, list) { | 2769 | list_for_each_entry(p, &ftrace_commands, list) { |
| 2750 | if (strcmp(p->name, command) == 0) { | 2770 | if (strcmp(p->name, command) == 0) { |
| 2751 | ret = p->func(func, command, next, enable); | 2771 | ret = p->func(hash, func, command, next, enable); |
| 2752 | goto out_unlock; | 2772 | goto out_unlock; |
| 2753 | } | 2773 | } |
| 2754 | } | 2774 | } |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index a3e2db708072..30a94c26dcb3 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -687,6 +687,7 @@ struct event_subsystem { | |||
| 687 | struct dentry *entry; | 687 | struct dentry *entry; |
| 688 | struct event_filter *filter; | 688 | struct event_filter *filter; |
| 689 | int nr_events; | 689 | int nr_events; |
| 690 | int ref_count; | ||
| 690 | }; | 691 | }; |
| 691 | 692 | ||
| 692 | #define FILTER_PRED_INVALID ((unsigned short)-1) | 693 | #define FILTER_PRED_INVALID ((unsigned short)-1) |
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 4d7e1498ae91..581876f9f387 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
| @@ -244,6 +244,35 @@ static void ftrace_clear_events(void) | |||
| 244 | mutex_unlock(&event_mutex); | 244 | mutex_unlock(&event_mutex); |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | static void __put_system(struct event_subsystem *system) | ||
| 248 | { | ||
| 249 | struct event_filter *filter = system->filter; | ||
| 250 | |||
| 251 | WARN_ON_ONCE(system->ref_count == 0); | ||
| 252 | if (--system->ref_count) | ||
| 253 | return; | ||
| 254 | |||
| 255 | if (filter) { | ||
| 256 | kfree(filter->filter_string); | ||
| 257 | kfree(filter); | ||
| 258 | } | ||
| 259 | kfree(system->name); | ||
| 260 | kfree(system); | ||
| 261 | } | ||
| 262 | |||
| 263 | static void __get_system(struct event_subsystem *system) | ||
| 264 | { | ||
| 265 | WARN_ON_ONCE(system->ref_count == 0); | ||
| 266 | system->ref_count++; | ||
| 267 | } | ||
| 268 | |||
| 269 | static void put_system(struct event_subsystem *system) | ||
| 270 | { | ||
| 271 | mutex_lock(&event_mutex); | ||
| 272 | __put_system(system); | ||
| 273 | mutex_unlock(&event_mutex); | ||
| 274 | } | ||
| 275 | |||
| 247 | /* | 276 | /* |
| 248 | * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. | 277 | * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. |
| 249 | */ | 278 | */ |
| @@ -519,7 +548,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
| 519 | loff_t *ppos) | 548 | loff_t *ppos) |
| 520 | { | 549 | { |
| 521 | const char set_to_char[4] = { '?', '0', '1', 'X' }; | 550 | const char set_to_char[4] = { '?', '0', '1', 'X' }; |
| 522 | const char *system = filp->private_data; | 551 | struct event_subsystem *system = filp->private_data; |
| 523 | struct ftrace_event_call *call; | 552 | struct ftrace_event_call *call; |
| 524 | char buf[2]; | 553 | char buf[2]; |
| 525 | int set = 0; | 554 | int set = 0; |
| @@ -530,7 +559,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
| 530 | if (!call->name || !call->class || !call->class->reg) | 559 | if (!call->name || !call->class || !call->class->reg) |
| 531 | continue; | 560 | continue; |
| 532 | 561 | ||
| 533 | if (system && strcmp(call->class->system, system) != 0) | 562 | if (system && strcmp(call->class->system, system->name) != 0) |
| 534 | continue; | 563 | continue; |
| 535 | 564 | ||
| 536 | /* | 565 | /* |
| @@ -560,7 +589,8 @@ static ssize_t | |||
| 560 | system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | 589 | system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, |
| 561 | loff_t *ppos) | 590 | loff_t *ppos) |
| 562 | { | 591 | { |
| 563 | const char *system = filp->private_data; | 592 | struct event_subsystem *system = filp->private_data; |
| 593 | const char *name = NULL; | ||
| 564 | unsigned long val; | 594 | unsigned long val; |
| 565 | ssize_t ret; | 595 | ssize_t ret; |
| 566 | 596 | ||
| @@ -575,7 +605,14 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
| 575 | if (val != 0 && val != 1) | 605 | if (val != 0 && val != 1) |
| 576 | return -EINVAL; | 606 | return -EINVAL; |
| 577 | 607 | ||
| 578 | ret = __ftrace_set_clr_event(NULL, system, NULL, val); | 608 | /* |
| 609 | * Opening of "enable" adds a ref count to system, | ||
| 610 | * so the name is safe to use. | ||
| 611 | */ | ||
| 612 | if (system) | ||
| 613 | name = system->name; | ||
| 614 | |||
| 615 | ret = __ftrace_set_clr_event(NULL, name, NULL, val); | ||
| 579 | if (ret) | 616 | if (ret) |
| 580 | goto out; | 617 | goto out; |
| 581 | 618 | ||
| @@ -808,6 +845,52 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
| 808 | return cnt; | 845 | return cnt; |
| 809 | } | 846 | } |
| 810 | 847 | ||
| 848 | static LIST_HEAD(event_subsystems); | ||
| 849 | |||
| 850 | static int subsystem_open(struct inode *inode, struct file *filp) | ||
| 851 | { | ||
| 852 | struct event_subsystem *system = NULL; | ||
| 853 | int ret; | ||
| 854 | |||
| 855 | if (!inode->i_private) | ||
| 856 | goto skip_search; | ||
| 857 | |||
| 858 | /* Make sure the system still exists */ | ||
| 859 | mutex_lock(&event_mutex); | ||
| 860 | list_for_each_entry(system, &event_subsystems, list) { | ||
| 861 | if (system == inode->i_private) { | ||
| 862 | /* Don't open systems with no events */ | ||
| 863 | if (!system->nr_events) { | ||
| 864 | system = NULL; | ||
| 865 | break; | ||
| 866 | } | ||
| 867 | __get_system(system); | ||
| 868 | break; | ||
| 869 | } | ||
| 870 | } | ||
| 871 | mutex_unlock(&event_mutex); | ||
| 872 | |||
| 873 | if (system != inode->i_private) | ||
| 874 | return -ENODEV; | ||
| 875 | |||
| 876 | skip_search: | ||
| 877 | ret = tracing_open_generic(inode, filp); | ||
| 878 | if (ret < 0 && system) | ||
| 879 | put_system(system); | ||
| 880 | |||
| 881 | return ret; | ||
| 882 | } | ||
| 883 | |||
| 884 | static int subsystem_release(struct inode *inode, struct file *file) | ||
| 885 | { | ||
| 886 | struct event_subsystem *system = inode->i_private; | ||
| 887 | |||
| 888 | if (system) | ||
| 889 | put_system(system); | ||
| 890 | |||
| 891 | return 0; | ||
| 892 | } | ||
| 893 | |||
| 811 | static ssize_t | 894 | static ssize_t |
| 812 | subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | 895 | subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, |
| 813 | loff_t *ppos) | 896 | loff_t *ppos) |
| @@ -945,17 +1028,19 @@ static const struct file_operations ftrace_event_filter_fops = { | |||
| 945 | }; | 1028 | }; |
| 946 | 1029 | ||
| 947 | static const struct file_operations ftrace_subsystem_filter_fops = { | 1030 | static const struct file_operations ftrace_subsystem_filter_fops = { |
| 948 | .open = tracing_open_generic, | 1031 | .open = subsystem_open, |
| 949 | .read = subsystem_filter_read, | 1032 | .read = subsystem_filter_read, |
| 950 | .write = subsystem_filter_write, | 1033 | .write = subsystem_filter_write, |
| 951 | .llseek = default_llseek, | 1034 | .llseek = default_llseek, |
| 1035 | .release = subsystem_release, | ||
| 952 | }; | 1036 | }; |
| 953 | 1037 | ||
| 954 | static const struct file_operations ftrace_system_enable_fops = { | 1038 | static const struct file_operations ftrace_system_enable_fops = { |
| 955 | .open = tracing_open_generic, | 1039 | .open = subsystem_open, |
| 956 | .read = system_enable_read, | 1040 | .read = system_enable_read, |
| 957 | .write = system_enable_write, | 1041 | .write = system_enable_write, |
| 958 | .llseek = default_llseek, | 1042 | .llseek = default_llseek, |
| 1043 | .release = subsystem_release, | ||
| 959 | }; | 1044 | }; |
| 960 | 1045 | ||
| 961 | static const struct file_operations ftrace_show_header_fops = { | 1046 | static const struct file_operations ftrace_show_header_fops = { |
| @@ -984,8 +1069,6 @@ static struct dentry *event_trace_events_dir(void) | |||
| 984 | return d_events; | 1069 | return d_events; |
| 985 | } | 1070 | } |
| 986 | 1071 | ||
| 987 | static LIST_HEAD(event_subsystems); | ||
| 988 | |||
| 989 | static struct dentry * | 1072 | static struct dentry * |
| 990 | event_subsystem_dir(const char *name, struct dentry *d_events) | 1073 | event_subsystem_dir(const char *name, struct dentry *d_events) |
| 991 | { | 1074 | { |
| @@ -995,6 +1078,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
| 995 | /* First see if we did not already create this dir */ | 1078 | /* First see if we did not already create this dir */ |
| 996 | list_for_each_entry(system, &event_subsystems, list) { | 1079 | list_for_each_entry(system, &event_subsystems, list) { |
| 997 | if (strcmp(system->name, name) == 0) { | 1080 | if (strcmp(system->name, name) == 0) { |
| 1081 | __get_system(system); | ||
| 998 | system->nr_events++; | 1082 | system->nr_events++; |
| 999 | return system->entry; | 1083 | return system->entry; |
| 1000 | } | 1084 | } |
| @@ -1017,6 +1101,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
| 1017 | } | 1101 | } |
| 1018 | 1102 | ||
| 1019 | system->nr_events = 1; | 1103 | system->nr_events = 1; |
| 1104 | system->ref_count = 1; | ||
| 1020 | system->name = kstrdup(name, GFP_KERNEL); | 1105 | system->name = kstrdup(name, GFP_KERNEL); |
| 1021 | if (!system->name) { | 1106 | if (!system->name) { |
| 1022 | debugfs_remove(system->entry); | 1107 | debugfs_remove(system->entry); |
| @@ -1044,8 +1129,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
| 1044 | "'%s/filter' entry\n", name); | 1129 | "'%s/filter' entry\n", name); |
| 1045 | } | 1130 | } |
| 1046 | 1131 | ||
| 1047 | trace_create_file("enable", 0644, system->entry, | 1132 | trace_create_file("enable", 0644, system->entry, system, |
| 1048 | (void *)system->name, | ||
| 1049 | &ftrace_system_enable_fops); | 1133 | &ftrace_system_enable_fops); |
| 1050 | 1134 | ||
| 1051 | return system->entry; | 1135 | return system->entry; |
| @@ -1166,16 +1250,9 @@ static void remove_subsystem_dir(const char *name) | |||
| 1166 | list_for_each_entry(system, &event_subsystems, list) { | 1250 | list_for_each_entry(system, &event_subsystems, list) { |
| 1167 | if (strcmp(system->name, name) == 0) { | 1251 | if (strcmp(system->name, name) == 0) { |
| 1168 | if (!--system->nr_events) { | 1252 | if (!--system->nr_events) { |
| 1169 | struct event_filter *filter = system->filter; | ||
| 1170 | |||
| 1171 | debugfs_remove_recursive(system->entry); | 1253 | debugfs_remove_recursive(system->entry); |
| 1172 | list_del(&system->list); | 1254 | list_del(&system->list); |
| 1173 | if (filter) { | 1255 | __put_system(system); |
| 1174 | kfree(filter->filter_string); | ||
| 1175 | kfree(filter); | ||
| 1176 | } | ||
| 1177 | kfree(system->name); | ||
| 1178 | kfree(system); | ||
| 1179 | } | 1256 | } |
| 1180 | break; | 1257 | break; |
| 1181 | } | 1258 | } |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 8008ddcfbf20..256764ecccd6 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
| @@ -1886,6 +1886,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system, | |||
| 1886 | 1886 | ||
| 1887 | mutex_lock(&event_mutex); | 1887 | mutex_lock(&event_mutex); |
| 1888 | 1888 | ||
| 1889 | /* Make sure the system still has events */ | ||
| 1890 | if (!system->nr_events) { | ||
| 1891 | err = -ENODEV; | ||
| 1892 | goto out_unlock; | ||
| 1893 | } | ||
| 1894 | |||
| 1889 | if (!strcmp(strstrip(filter_string), "0")) { | 1895 | if (!strcmp(strstrip(filter_string), "0")) { |
| 1890 | filter_free_subsystem_preds(system); | 1896 | filter_free_subsystem_preds(system); |
| 1891 | remove_filter_string(system->filter); | 1897 | remove_filter_string(system->filter); |
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 8d0e1cc4e974..c7b0c6a7db09 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
| @@ -324,7 +324,8 @@ ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param) | |||
| 324 | } | 324 | } |
| 325 | 325 | ||
| 326 | static int | 326 | static int |
| 327 | ftrace_trace_onoff_callback(char *glob, char *cmd, char *param, int enable) | 327 | ftrace_trace_onoff_callback(struct ftrace_hash *hash, |
| 328 | char *glob, char *cmd, char *param, int enable) | ||
| 328 | { | 329 | { |
| 329 | struct ftrace_probe_ops *ops; | 330 | struct ftrace_probe_ops *ops; |
| 330 | void *count = (void *)-1; | 331 | void *count = (void *)-1; |
