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 | } |
