diff options
Diffstat (limited to 'kernel/trace/trace_events.c')
-rw-r--r-- | kernel/trace/trace_events.c | 139 |
1 files changed, 99 insertions, 40 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 686ec399f2a8..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 | */ |
@@ -486,20 +515,11 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
486 | loff_t *ppos) | 515 | loff_t *ppos) |
487 | { | 516 | { |
488 | struct ftrace_event_call *call = filp->private_data; | 517 | struct ftrace_event_call *call = filp->private_data; |
489 | char buf[64]; | ||
490 | unsigned long val; | 518 | unsigned long val; |
491 | int ret; | 519 | int ret; |
492 | 520 | ||
493 | if (cnt >= sizeof(buf)) | 521 | ret = kstrtoul_from_user(ubuf, cnt, 10, &val); |
494 | return -EINVAL; | 522 | if (ret) |
495 | |||
496 | if (copy_from_user(&buf, ubuf, cnt)) | ||
497 | return -EFAULT; | ||
498 | |||
499 | buf[cnt] = 0; | ||
500 | |||
501 | ret = strict_strtoul(buf, 10, &val); | ||
502 | if (ret < 0) | ||
503 | return ret; | 523 | return ret; |
504 | 524 | ||
505 | ret = tracing_update_buffers(); | 525 | ret = tracing_update_buffers(); |
@@ -528,7 +548,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
528 | loff_t *ppos) | 548 | loff_t *ppos) |
529 | { | 549 | { |
530 | const char set_to_char[4] = { '?', '0', '1', 'X' }; | 550 | const char set_to_char[4] = { '?', '0', '1', 'X' }; |
531 | const char *system = filp->private_data; | 551 | struct event_subsystem *system = filp->private_data; |
532 | struct ftrace_event_call *call; | 552 | struct ftrace_event_call *call; |
533 | char buf[2]; | 553 | char buf[2]; |
534 | int set = 0; | 554 | int set = 0; |
@@ -539,7 +559,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
539 | if (!call->name || !call->class || !call->class->reg) | 559 | if (!call->name || !call->class || !call->class->reg) |
540 | continue; | 560 | continue; |
541 | 561 | ||
542 | if (system && strcmp(call->class->system, system) != 0) | 562 | if (system && strcmp(call->class->system, system->name) != 0) |
543 | continue; | 563 | continue; |
544 | 564 | ||
545 | /* | 565 | /* |
@@ -569,21 +589,13 @@ static ssize_t | |||
569 | 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, |
570 | loff_t *ppos) | 590 | loff_t *ppos) |
571 | { | 591 | { |
572 | const char *system = filp->private_data; | 592 | struct event_subsystem *system = filp->private_data; |
593 | const char *name = NULL; | ||
573 | unsigned long val; | 594 | unsigned long val; |
574 | char buf[64]; | ||
575 | ssize_t ret; | 595 | ssize_t ret; |
576 | 596 | ||
577 | if (cnt >= sizeof(buf)) | 597 | ret = kstrtoul_from_user(ubuf, cnt, 10, &val); |
578 | return -EINVAL; | 598 | if (ret) |
579 | |||
580 | if (copy_from_user(&buf, ubuf, cnt)) | ||
581 | return -EFAULT; | ||
582 | |||
583 | buf[cnt] = 0; | ||
584 | |||
585 | ret = strict_strtoul(buf, 10, &val); | ||
586 | if (ret < 0) | ||
587 | return ret; | 599 | return ret; |
588 | 600 | ||
589 | ret = tracing_update_buffers(); | 601 | ret = tracing_update_buffers(); |
@@ -593,7 +605,14 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
593 | if (val != 0 && val != 1) | 605 | if (val != 0 && val != 1) |
594 | return -EINVAL; | 606 | return -EINVAL; |
595 | 607 | ||
596 | 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); | ||
597 | if (ret) | 616 | if (ret) |
598 | goto out; | 617 | goto out; |
599 | 618 | ||
@@ -826,6 +845,52 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
826 | return cnt; | 845 | return cnt; |
827 | } | 846 | } |
828 | 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 | |||
829 | static ssize_t | 894 | static ssize_t |
830 | 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, |
831 | loff_t *ppos) | 896 | loff_t *ppos) |
@@ -963,17 +1028,19 @@ static const struct file_operations ftrace_event_filter_fops = { | |||
963 | }; | 1028 | }; |
964 | 1029 | ||
965 | static const struct file_operations ftrace_subsystem_filter_fops = { | 1030 | static const struct file_operations ftrace_subsystem_filter_fops = { |
966 | .open = tracing_open_generic, | 1031 | .open = subsystem_open, |
967 | .read = subsystem_filter_read, | 1032 | .read = subsystem_filter_read, |
968 | .write = subsystem_filter_write, | 1033 | .write = subsystem_filter_write, |
969 | .llseek = default_llseek, | 1034 | .llseek = default_llseek, |
1035 | .release = subsystem_release, | ||
970 | }; | 1036 | }; |
971 | 1037 | ||
972 | static const struct file_operations ftrace_system_enable_fops = { | 1038 | static const struct file_operations ftrace_system_enable_fops = { |
973 | .open = tracing_open_generic, | 1039 | .open = subsystem_open, |
974 | .read = system_enable_read, | 1040 | .read = system_enable_read, |
975 | .write = system_enable_write, | 1041 | .write = system_enable_write, |
976 | .llseek = default_llseek, | 1042 | .llseek = default_llseek, |
1043 | .release = subsystem_release, | ||
977 | }; | 1044 | }; |
978 | 1045 | ||
979 | static const struct file_operations ftrace_show_header_fops = { | 1046 | static const struct file_operations ftrace_show_header_fops = { |
@@ -1002,8 +1069,6 @@ static struct dentry *event_trace_events_dir(void) | |||
1002 | return d_events; | 1069 | return d_events; |
1003 | } | 1070 | } |
1004 | 1071 | ||
1005 | static LIST_HEAD(event_subsystems); | ||
1006 | |||
1007 | static struct dentry * | 1072 | static struct dentry * |
1008 | event_subsystem_dir(const char *name, struct dentry *d_events) | 1073 | event_subsystem_dir(const char *name, struct dentry *d_events) |
1009 | { | 1074 | { |
@@ -1013,6 +1078,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
1013 | /* First see if we did not already create this dir */ | 1078 | /* First see if we did not already create this dir */ |
1014 | list_for_each_entry(system, &event_subsystems, list) { | 1079 | list_for_each_entry(system, &event_subsystems, list) { |
1015 | if (strcmp(system->name, name) == 0) { | 1080 | if (strcmp(system->name, name) == 0) { |
1081 | __get_system(system); | ||
1016 | system->nr_events++; | 1082 | system->nr_events++; |
1017 | return system->entry; | 1083 | return system->entry; |
1018 | } | 1084 | } |
@@ -1035,6 +1101,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
1035 | } | 1101 | } |
1036 | 1102 | ||
1037 | system->nr_events = 1; | 1103 | system->nr_events = 1; |
1104 | system->ref_count = 1; | ||
1038 | system->name = kstrdup(name, GFP_KERNEL); | 1105 | system->name = kstrdup(name, GFP_KERNEL); |
1039 | if (!system->name) { | 1106 | if (!system->name) { |
1040 | debugfs_remove(system->entry); | 1107 | debugfs_remove(system->entry); |
@@ -1062,8 +1129,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
1062 | "'%s/filter' entry\n", name); | 1129 | "'%s/filter' entry\n", name); |
1063 | } | 1130 | } |
1064 | 1131 | ||
1065 | trace_create_file("enable", 0644, system->entry, | 1132 | trace_create_file("enable", 0644, system->entry, system, |
1066 | (void *)system->name, | ||
1067 | &ftrace_system_enable_fops); | 1133 | &ftrace_system_enable_fops); |
1068 | 1134 | ||
1069 | return system->entry; | 1135 | return system->entry; |
@@ -1184,16 +1250,9 @@ static void remove_subsystem_dir(const char *name) | |||
1184 | list_for_each_entry(system, &event_subsystems, list) { | 1250 | list_for_each_entry(system, &event_subsystems, list) { |
1185 | if (strcmp(system->name, name) == 0) { | 1251 | if (strcmp(system->name, name) == 0) { |
1186 | if (!--system->nr_events) { | 1252 | if (!--system->nr_events) { |
1187 | struct event_filter *filter = system->filter; | ||
1188 | |||
1189 | debugfs_remove_recursive(system->entry); | 1253 | debugfs_remove_recursive(system->entry); |
1190 | list_del(&system->list); | 1254 | list_del(&system->list); |
1191 | if (filter) { | 1255 | __put_system(system); |
1192 | kfree(filter->filter_string); | ||
1193 | kfree(filter); | ||
1194 | } | ||
1195 | kfree(system->name); | ||
1196 | kfree(system); | ||
1197 | } | 1256 | } |
1198 | break; | 1257 | break; |
1199 | } | 1258 | } |