diff options
-rw-r--r-- | kernel/trace/trace.h | 1 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 86 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 6 |
3 files changed, 82 insertions, 11 deletions
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 229f8591f61d..f8074072d111 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -677,6 +677,7 @@ struct event_subsystem { | |||
677 | struct dentry *entry; | 677 | struct dentry *entry; |
678 | struct event_filter *filter; | 678 | struct event_filter *filter; |
679 | int nr_events; | 679 | int nr_events; |
680 | int ref_count; | ||
680 | }; | 681 | }; |
681 | 682 | ||
682 | #define FILTER_PRED_INVALID ((unsigned short)-1) | 683 | #define FILTER_PRED_INVALID ((unsigned short)-1) |
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 686ec399f2a8..ffc5b2884af1 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 | */ |
@@ -826,6 +855,47 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, | |||
826 | return cnt; | 855 | return cnt; |
827 | } | 856 | } |
828 | 857 | ||
858 | static LIST_HEAD(event_subsystems); | ||
859 | |||
860 | static int subsystem_open(struct inode *inode, struct file *filp) | ||
861 | { | ||
862 | struct event_subsystem *system = NULL; | ||
863 | int ret; | ||
864 | |||
865 | /* Make sure the system still exists */ | ||
866 | mutex_lock(&event_mutex); | ||
867 | list_for_each_entry(system, &event_subsystems, list) { | ||
868 | if (system == inode->i_private) { | ||
869 | /* Don't open systems with no events */ | ||
870 | if (!system->nr_events) { | ||
871 | system = NULL; | ||
872 | break; | ||
873 | } | ||
874 | __get_system(system); | ||
875 | break; | ||
876 | } | ||
877 | } | ||
878 | mutex_unlock(&event_mutex); | ||
879 | |||
880 | if (system != inode->i_private) | ||
881 | return -ENODEV; | ||
882 | |||
883 | ret = tracing_open_generic(inode, filp); | ||
884 | if (ret < 0) | ||
885 | put_system(system); | ||
886 | |||
887 | return ret; | ||
888 | } | ||
889 | |||
890 | static int subsystem_release(struct inode *inode, struct file *file) | ||
891 | { | ||
892 | struct event_subsystem *system = inode->i_private; | ||
893 | |||
894 | put_system(system); | ||
895 | |||
896 | return 0; | ||
897 | } | ||
898 | |||
829 | static ssize_t | 899 | static ssize_t |
830 | subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | 900 | subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, |
831 | loff_t *ppos) | 901 | loff_t *ppos) |
@@ -963,10 +1033,11 @@ static const struct file_operations ftrace_event_filter_fops = { | |||
963 | }; | 1033 | }; |
964 | 1034 | ||
965 | static const struct file_operations ftrace_subsystem_filter_fops = { | 1035 | static const struct file_operations ftrace_subsystem_filter_fops = { |
966 | .open = tracing_open_generic, | 1036 | .open = subsystem_open, |
967 | .read = subsystem_filter_read, | 1037 | .read = subsystem_filter_read, |
968 | .write = subsystem_filter_write, | 1038 | .write = subsystem_filter_write, |
969 | .llseek = default_llseek, | 1039 | .llseek = default_llseek, |
1040 | .release = subsystem_release, | ||
970 | }; | 1041 | }; |
971 | 1042 | ||
972 | static const struct file_operations ftrace_system_enable_fops = { | 1043 | static const struct file_operations ftrace_system_enable_fops = { |
@@ -1002,8 +1073,6 @@ static struct dentry *event_trace_events_dir(void) | |||
1002 | return d_events; | 1073 | return d_events; |
1003 | } | 1074 | } |
1004 | 1075 | ||
1005 | static LIST_HEAD(event_subsystems); | ||
1006 | |||
1007 | static struct dentry * | 1076 | static struct dentry * |
1008 | event_subsystem_dir(const char *name, struct dentry *d_events) | 1077 | event_subsystem_dir(const char *name, struct dentry *d_events) |
1009 | { | 1078 | { |
@@ -1013,6 +1082,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
1013 | /* First see if we did not already create this dir */ | 1082 | /* First see if we did not already create this dir */ |
1014 | list_for_each_entry(system, &event_subsystems, list) { | 1083 | list_for_each_entry(system, &event_subsystems, list) { |
1015 | if (strcmp(system->name, name) == 0) { | 1084 | if (strcmp(system->name, name) == 0) { |
1085 | __get_system(system); | ||
1016 | system->nr_events++; | 1086 | system->nr_events++; |
1017 | return system->entry; | 1087 | return system->entry; |
1018 | } | 1088 | } |
@@ -1035,6 +1105,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
1035 | } | 1105 | } |
1036 | 1106 | ||
1037 | system->nr_events = 1; | 1107 | system->nr_events = 1; |
1108 | system->ref_count = 1; | ||
1038 | system->name = kstrdup(name, GFP_KERNEL); | 1109 | system->name = kstrdup(name, GFP_KERNEL); |
1039 | if (!system->name) { | 1110 | if (!system->name) { |
1040 | debugfs_remove(system->entry); | 1111 | debugfs_remove(system->entry); |
@@ -1184,16 +1255,9 @@ static void remove_subsystem_dir(const char *name) | |||
1184 | list_for_each_entry(system, &event_subsystems, list) { | 1255 | list_for_each_entry(system, &event_subsystems, list) { |
1185 | if (strcmp(system->name, name) == 0) { | 1256 | if (strcmp(system->name, name) == 0) { |
1186 | if (!--system->nr_events) { | 1257 | if (!--system->nr_events) { |
1187 | struct event_filter *filter = system->filter; | ||
1188 | |||
1189 | debugfs_remove_recursive(system->entry); | 1258 | debugfs_remove_recursive(system->entry); |
1190 | list_del(&system->list); | 1259 | list_del(&system->list); |
1191 | if (filter) { | 1260 | __put_system(system); |
1192 | kfree(filter->filter_string); | ||
1193 | kfree(filter); | ||
1194 | } | ||
1195 | kfree(system->name); | ||
1196 | kfree(system); | ||
1197 | } | 1261 | } |
1198 | break; | 1262 | break; |
1199 | } | 1263 | } |
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); |