diff options
author | Tom Zanussi <tzanussi@gmail.com> | 2009-04-17 01:27:08 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-04-17 12:28:27 -0400 |
commit | ac1adc55fc71c7515caa2eb0e63e49b3d1c6a47c (patch) | |
tree | 7a97cf9512572c53d0802725f8e0ad6a2d4b2204 | |
parent | 46de405f25f1d9fa73b657ffbb752aa0cc87a91d (diff) |
tracing/filters: add filter_mutex to protect filter predicates
This patch adds a filter_mutex to prevent the filter predicates from
being accessed concurrently by various external functions.
It's based on a previous patch by Li Zefan:
"[PATCH 7/7] tracing/filters: make filter preds RCU safe"
v2 changes:
- fixed wrong value returned in a add_subsystem_pred() failure case
noticed by Li Zefan.
[ Impact: fix trace filter corruption/crashes on parallel access ]
Signed-off-by: Tom Zanussi <tzanussi@gmail.com>
Reviewed-by: Li Zefan <lizf@cn.fujitsu.com>
Tested-by: Li Zefan <lizf@cn.fujitsu.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: paulmck@linux.vnet.ibm.com
LKML-Reference: <1239946028.6639.13.camel@tropicana>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | kernel/trace/trace.h | 4 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 4 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 90 |
3 files changed, 75 insertions, 23 deletions
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 8817c18ef97a..247948e81b08 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -757,13 +757,15 @@ struct filter_pred { | |||
757 | }; | 757 | }; |
758 | 758 | ||
759 | extern void filter_free_pred(struct filter_pred *pred); | 759 | extern void filter_free_pred(struct filter_pred *pred); |
760 | extern void filter_print_preds(struct filter_pred **preds, int n_preds, | 760 | extern void filter_print_preds(struct ftrace_event_call *call, |
761 | struct trace_seq *s); | 761 | struct trace_seq *s); |
762 | extern int filter_parse(char **pbuf, struct filter_pred *pred); | 762 | extern int filter_parse(char **pbuf, struct filter_pred *pred); |
763 | extern int filter_add_pred(struct ftrace_event_call *call, | 763 | extern int filter_add_pred(struct ftrace_event_call *call, |
764 | struct filter_pred *pred); | 764 | struct filter_pred *pred); |
765 | extern void filter_disable_preds(struct ftrace_event_call *call); | 765 | extern void filter_disable_preds(struct ftrace_event_call *call); |
766 | extern void filter_free_subsystem_preds(struct event_subsystem *system); | 766 | extern void filter_free_subsystem_preds(struct event_subsystem *system); |
767 | extern void filter_print_subsystem_preds(struct event_subsystem *system, | ||
768 | struct trace_seq *s); | ||
767 | extern int filter_add_subsystem_pred(struct event_subsystem *system, | 769 | extern int filter_add_subsystem_pred(struct event_subsystem *system, |
768 | struct filter_pred *pred); | 770 | struct filter_pred *pred); |
769 | 771 | ||
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 1137f951be42..64f9d6d2735b 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -488,7 +488,7 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
488 | 488 | ||
489 | trace_seq_init(s); | 489 | trace_seq_init(s); |
490 | 490 | ||
491 | filter_print_preds(call->preds, call->n_preds, s); | 491 | filter_print_preds(call, s); |
492 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); | 492 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); |
493 | 493 | ||
494 | kfree(s); | 494 | kfree(s); |
@@ -558,7 +558,7 @@ subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
558 | 558 | ||
559 | trace_seq_init(s); | 559 | trace_seq_init(s); |
560 | 560 | ||
561 | filter_print_preds(system->preds, system->n_preds, s); | 561 | filter_print_subsystem_preds(system, s); |
562 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); | 562 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); |
563 | 563 | ||
564 | kfree(s); | 564 | kfree(s); |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index f8e5eab0424c..e0fcfd2a16d6 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -22,10 +22,13 @@ | |||
22 | #include <linux/uaccess.h> | 22 | #include <linux/uaccess.h> |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/ctype.h> | 24 | #include <linux/ctype.h> |
25 | #include <linux/mutex.h> | ||
25 | 26 | ||
26 | #include "trace.h" | 27 | #include "trace.h" |
27 | #include "trace_output.h" | 28 | #include "trace_output.h" |
28 | 29 | ||
30 | static DEFINE_MUTEX(filter_mutex); | ||
31 | |||
29 | static int filter_pred_64(struct filter_pred *pred, void *event) | 32 | static int filter_pred_64(struct filter_pred *pred, void *event) |
30 | { | 33 | { |
31 | u64 *addr = (u64 *)(event + pred->offset); | 34 | u64 *addr = (u64 *)(event + pred->offset); |
@@ -112,8 +115,8 @@ int filter_match_preds(struct ftrace_event_call *call, void *rec) | |||
112 | } | 115 | } |
113 | EXPORT_SYMBOL_GPL(filter_match_preds); | 116 | EXPORT_SYMBOL_GPL(filter_match_preds); |
114 | 117 | ||
115 | void filter_print_preds(struct filter_pred **preds, int n_preds, | 118 | static void __filter_print_preds(struct filter_pred **preds, int n_preds, |
116 | struct trace_seq *s) | 119 | struct trace_seq *s) |
117 | { | 120 | { |
118 | char *field_name; | 121 | char *field_name; |
119 | struct filter_pred *pred; | 122 | struct filter_pred *pred; |
@@ -138,6 +141,21 @@ void filter_print_preds(struct filter_pred **preds, int n_preds, | |||
138 | } | 141 | } |
139 | } | 142 | } |
140 | 143 | ||
144 | void filter_print_preds(struct ftrace_event_call *call, struct trace_seq *s) | ||
145 | { | ||
146 | mutex_lock(&filter_mutex); | ||
147 | __filter_print_preds(call->preds, call->n_preds, s); | ||
148 | mutex_unlock(&filter_mutex); | ||
149 | } | ||
150 | |||
151 | void filter_print_subsystem_preds(struct event_subsystem *system, | ||
152 | struct trace_seq *s) | ||
153 | { | ||
154 | mutex_lock(&filter_mutex); | ||
155 | __filter_print_preds(system->preds, system->n_preds, s); | ||
156 | mutex_unlock(&filter_mutex); | ||
157 | } | ||
158 | |||
141 | static struct ftrace_event_field * | 159 | static struct ftrace_event_field * |
142 | find_event_field(struct ftrace_event_call *call, char *name) | 160 | find_event_field(struct ftrace_event_call *call, char *name) |
143 | { | 161 | { |
@@ -180,7 +198,7 @@ static int filter_set_pred(struct filter_pred *dest, | |||
180 | return 0; | 198 | return 0; |
181 | } | 199 | } |
182 | 200 | ||
183 | void filter_disable_preds(struct ftrace_event_call *call) | 201 | static void __filter_disable_preds(struct ftrace_event_call *call) |
184 | { | 202 | { |
185 | int i; | 203 | int i; |
186 | 204 | ||
@@ -190,6 +208,13 @@ void filter_disable_preds(struct ftrace_event_call *call) | |||
190 | call->preds[i]->fn = filter_pred_none; | 208 | call->preds[i]->fn = filter_pred_none; |
191 | } | 209 | } |
192 | 210 | ||
211 | void filter_disable_preds(struct ftrace_event_call *call) | ||
212 | { | ||
213 | mutex_lock(&filter_mutex); | ||
214 | __filter_disable_preds(call); | ||
215 | mutex_unlock(&filter_mutex); | ||
216 | } | ||
217 | |||
193 | int init_preds(struct ftrace_event_call *call) | 218 | int init_preds(struct ftrace_event_call *call) |
194 | { | 219 | { |
195 | struct filter_pred *pred; | 220 | struct filter_pred *pred; |
@@ -223,7 +248,7 @@ oom: | |||
223 | } | 248 | } |
224 | EXPORT_SYMBOL_GPL(init_preds); | 249 | EXPORT_SYMBOL_GPL(init_preds); |
225 | 250 | ||
226 | void filter_free_subsystem_preds(struct event_subsystem *system) | 251 | static void __filter_free_subsystem_preds(struct event_subsystem *system) |
227 | { | 252 | { |
228 | struct ftrace_event_call *call; | 253 | struct ftrace_event_call *call; |
229 | int i; | 254 | int i; |
@@ -241,18 +266,25 @@ void filter_free_subsystem_preds(struct event_subsystem *system) | |||
241 | continue; | 266 | continue; |
242 | 267 | ||
243 | if (!strcmp(call->system, system->name)) | 268 | if (!strcmp(call->system, system->name)) |
244 | filter_disable_preds(call); | 269 | __filter_disable_preds(call); |
245 | } | 270 | } |
246 | } | 271 | } |
247 | 272 | ||
248 | static int __filter_add_pred(struct ftrace_event_call *call, | 273 | void filter_free_subsystem_preds(struct event_subsystem *system) |
249 | struct filter_pred *pred, | 274 | { |
250 | filter_pred_fn_t fn) | 275 | mutex_lock(&filter_mutex); |
276 | __filter_free_subsystem_preds(system); | ||
277 | mutex_unlock(&filter_mutex); | ||
278 | } | ||
279 | |||
280 | static int filter_add_pred_fn(struct ftrace_event_call *call, | ||
281 | struct filter_pred *pred, | ||
282 | filter_pred_fn_t fn) | ||
251 | { | 283 | { |
252 | int idx, err; | 284 | int idx, err; |
253 | 285 | ||
254 | if (call->n_preds && !pred->compound) | 286 | if (call->n_preds && !pred->compound) |
255 | filter_disable_preds(call); | 287 | __filter_disable_preds(call); |
256 | 288 | ||
257 | if (call->n_preds == MAX_FILTER_PRED) | 289 | if (call->n_preds == MAX_FILTER_PRED) |
258 | return -ENOSPC; | 290 | return -ENOSPC; |
@@ -276,7 +308,8 @@ static int is_string_field(const char *type) | |||
276 | return 0; | 308 | return 0; |
277 | } | 309 | } |
278 | 310 | ||
279 | int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) | 311 | static int __filter_add_pred(struct ftrace_event_call *call, |
312 | struct filter_pred *pred) | ||
280 | { | 313 | { |
281 | struct ftrace_event_field *field; | 314 | struct ftrace_event_field *field; |
282 | filter_pred_fn_t fn; | 315 | filter_pred_fn_t fn; |
@@ -293,7 +326,7 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) | |||
293 | return -EINVAL; | 326 | return -EINVAL; |
294 | fn = filter_pred_string; | 327 | fn = filter_pred_string; |
295 | pred->str_len = field->size; | 328 | pred->str_len = field->size; |
296 | return __filter_add_pred(call, pred, fn); | 329 | return filter_add_pred_fn(call, pred, fn); |
297 | } else { | 330 | } else { |
298 | if (pred->str_len) | 331 | if (pred->str_len) |
299 | return -EINVAL; | 332 | return -EINVAL; |
@@ -316,7 +349,18 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) | |||
316 | return -EINVAL; | 349 | return -EINVAL; |
317 | } | 350 | } |
318 | 351 | ||
319 | return __filter_add_pred(call, pred, fn); | 352 | return filter_add_pred_fn(call, pred, fn); |
353 | } | ||
354 | |||
355 | int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) | ||
356 | { | ||
357 | int err; | ||
358 | |||
359 | mutex_lock(&filter_mutex); | ||
360 | err = __filter_add_pred(call, pred); | ||
361 | mutex_unlock(&filter_mutex); | ||
362 | |||
363 | return err; | ||
320 | } | 364 | } |
321 | 365 | ||
322 | int filter_add_subsystem_pred(struct event_subsystem *system, | 366 | int filter_add_subsystem_pred(struct event_subsystem *system, |
@@ -324,20 +368,27 @@ int filter_add_subsystem_pred(struct event_subsystem *system, | |||
324 | { | 368 | { |
325 | struct ftrace_event_call *call; | 369 | struct ftrace_event_call *call; |
326 | 370 | ||
371 | mutex_lock(&filter_mutex); | ||
372 | |||
327 | if (system->n_preds && !pred->compound) | 373 | if (system->n_preds && !pred->compound) |
328 | filter_free_subsystem_preds(system); | 374 | __filter_free_subsystem_preds(system); |
329 | 375 | ||
330 | if (!system->n_preds) { | 376 | if (!system->n_preds) { |
331 | system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), | 377 | system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), |
332 | GFP_KERNEL); | 378 | GFP_KERNEL); |
333 | if (!system->preds) | 379 | if (!system->preds) { |
380 | mutex_unlock(&filter_mutex); | ||
334 | return -ENOMEM; | 381 | return -ENOMEM; |
382 | } | ||
335 | } | 383 | } |
336 | 384 | ||
337 | if (system->n_preds == MAX_FILTER_PRED) | 385 | if (system->n_preds == MAX_FILTER_PRED) { |
386 | mutex_unlock(&filter_mutex); | ||
338 | return -ENOSPC; | 387 | return -ENOSPC; |
388 | } | ||
339 | 389 | ||
340 | system->preds[system->n_preds] = pred; | 390 | system->preds[system->n_preds] = pred; |
391 | system->n_preds++; | ||
341 | 392 | ||
342 | list_for_each_entry(call, &ftrace_events, list) { | 393 | list_for_each_entry(call, &ftrace_events, list) { |
343 | int err; | 394 | int err; |
@@ -348,17 +399,16 @@ int filter_add_subsystem_pred(struct event_subsystem *system, | |||
348 | if (strcmp(call->system, system->name)) | 399 | if (strcmp(call->system, system->name)) |
349 | continue; | 400 | continue; |
350 | 401 | ||
351 | if (!find_event_field(call, pred->field_name)) | 402 | err = __filter_add_pred(call, pred); |
352 | continue; | ||
353 | |||
354 | err = filter_add_pred(call, pred); | ||
355 | if (err == -ENOMEM) { | 403 | if (err == -ENOMEM) { |
356 | system->preds[system->n_preds] = NULL; | 404 | system->preds[system->n_preds] = NULL; |
405 | system->n_preds--; | ||
406 | mutex_unlock(&filter_mutex); | ||
357 | return err; | 407 | return err; |
358 | } | 408 | } |
359 | } | 409 | } |
360 | 410 | ||
361 | system->n_preds++; | 411 | mutex_unlock(&filter_mutex); |
362 | 412 | ||
363 | return 0; | 413 | return 0; |
364 | } | 414 | } |