diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/jump_label.c | 14 | ||||
-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 |
6 files changed, 144 insertions, 35 deletions
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index fa27e750dbc0..a8ce45097f3d 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
@@ -375,15 +375,19 @@ int jump_label_text_reserved(void *start, void *end) | |||
375 | 375 | ||
376 | static void jump_label_update(struct jump_label_key *key, int enable) | 376 | static void jump_label_update(struct jump_label_key *key, int enable) |
377 | { | 377 | { |
378 | struct jump_entry *entry = key->entries; | 378 | struct jump_entry *entry = key->entries, *stop = __stop___jump_table; |
379 | |||
380 | /* if there are no users, entry can be NULL */ | ||
381 | if (entry) | ||
382 | __jump_label_update(key, entry, __stop___jump_table, enable); | ||
383 | 379 | ||
384 | #ifdef CONFIG_MODULES | 380 | #ifdef CONFIG_MODULES |
381 | struct module *mod = __module_address((jump_label_t)key); | ||
382 | |||
385 | __jump_label_mod_update(key, enable); | 383 | __jump_label_mod_update(key, enable); |
384 | |||
385 | if (mod) | ||
386 | stop = mod->jump_entries + mod->num_jump_entries; | ||
386 | #endif | 387 | #endif |
388 | /* if there are no users, entry can be NULL */ | ||
389 | if (entry) | ||
390 | __jump_label_update(key, entry, stop, enable); | ||
387 | } | 391 | } |
388 | 392 | ||
389 | #endif | 393 | #endif |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 62e26d930535..c3e4575e7829 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -1781,10 +1781,36 @@ static cycle_t ftrace_update_time; | |||
1781 | static unsigned long ftrace_update_cnt; | 1781 | static unsigned long ftrace_update_cnt; |
1782 | unsigned long ftrace_update_tot_cnt; | 1782 | unsigned long ftrace_update_tot_cnt; |
1783 | 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 | |||
1784 | static int ftrace_update_code(struct module *mod) | 1792 | static int ftrace_update_code(struct module *mod) |
1785 | { | 1793 | { |
1786 | struct dyn_ftrace *p; | 1794 | struct dyn_ftrace *p; |
1787 | 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 | } | ||
1788 | 1814 | ||
1789 | start = ftrace_now(raw_smp_processor_id()); | 1815 | start = ftrace_now(raw_smp_processor_id()); |
1790 | ftrace_update_cnt = 0; | 1816 | ftrace_update_cnt = 0; |
@@ -1797,7 +1823,7 @@ static int ftrace_update_code(struct module *mod) | |||
1797 | 1823 | ||
1798 | p = ftrace_new_addrs; | 1824 | p = ftrace_new_addrs; |
1799 | ftrace_new_addrs = p->newlist; | 1825 | ftrace_new_addrs = p->newlist; |
1800 | p->flags = 0L; | 1826 | p->flags = ref; |
1801 | 1827 | ||
1802 | /* | 1828 | /* |
1803 | * Do the initial record conversion from mcount jump | 1829 | * Do the initial record conversion from mcount jump |
@@ -1820,7 +1846,7 @@ static int ftrace_update_code(struct module *mod) | |||
1820 | * conversion puts the module to the correct state, thus | 1846 | * conversion puts the module to the correct state, thus |
1821 | * passing the ftrace_make_call check. | 1847 | * passing the ftrace_make_call check. |
1822 | */ | 1848 | */ |
1823 | if (ftrace_start_up) { | 1849 | if (ftrace_start_up && ref) { |
1824 | int failed = __ftrace_replace_code(p, 1); | 1850 | int failed = __ftrace_replace_code(p, 1); |
1825 | if (failed) { | 1851 | if (failed) { |
1826 | ftrace_bug(failed, p->ip); | 1852 | ftrace_bug(failed, p->ip); |
@@ -2444,10 +2470,9 @@ ftrace_match_module_records(struct ftrace_hash *hash, char *buff, char *mod) | |||
2444 | */ | 2470 | */ |
2445 | 2471 | ||
2446 | static int | 2472 | static int |
2447 | 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) | ||
2448 | { | 2475 | { |
2449 | struct ftrace_ops *ops = &global_ops; | ||
2450 | struct ftrace_hash *hash; | ||
2451 | char *mod; | 2476 | char *mod; |
2452 | int ret = -EINVAL; | 2477 | int ret = -EINVAL; |
2453 | 2478 | ||
@@ -2467,11 +2492,6 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | |||
2467 | if (!strlen(mod)) | 2492 | if (!strlen(mod)) |
2468 | return ret; | 2493 | return ret; |
2469 | 2494 | ||
2470 | if (enable) | ||
2471 | hash = ops->filter_hash; | ||
2472 | else | ||
2473 | hash = ops->notrace_hash; | ||
2474 | |||
2475 | ret = ftrace_match_module_records(hash, func, mod); | 2495 | ret = ftrace_match_module_records(hash, func, mod); |
2476 | if (!ret) | 2496 | if (!ret) |
2477 | ret = -EINVAL; | 2497 | ret = -EINVAL; |
@@ -2797,7 +2817,7 @@ static int ftrace_process_regex(struct ftrace_hash *hash, | |||
2797 | mutex_lock(&ftrace_cmd_mutex); | 2817 | mutex_lock(&ftrace_cmd_mutex); |
2798 | list_for_each_entry(p, &ftrace_commands, list) { | 2818 | list_for_each_entry(p, &ftrace_commands, list) { |
2799 | if (strcmp(p->name, command) == 0) { | 2819 | if (strcmp(p->name, command) == 0) { |
2800 | ret = p->func(func, command, next, enable); | 2820 | ret = p->func(hash, func, command, next, enable); |
2801 | goto out_unlock; | 2821 | goto out_unlock; |
2802 | } | 2822 | } |
2803 | } | 2823 | } |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 651f35be372a..3f381d0b20a8 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -720,6 +720,7 @@ struct event_subsystem { | |||
720 | struct dentry *entry; | 720 | struct dentry *entry; |
721 | struct event_filter *filter; | 721 | struct event_filter *filter; |
722 | int nr_events; | 722 | int nr_events; |
723 | int ref_count; | ||
723 | }; | 724 | }; |
724 | 725 | ||
725 | #define FILTER_PRED_INVALID ((unsigned short)-1) | 726 | #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; |