diff options
-rw-r--r-- | kernel/trace/trace.c | 2 | ||||
-rw-r--r-- | kernel/trace/trace.h | 3 | ||||
-rw-r--r-- | kernel/trace/trace_events_hist.c | 661 | ||||
-rw-r--r-- | kernel/trace/trace_events_trigger.c | 6 |
4 files changed, 656 insertions, 16 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 82cc8891fda6..68f8702af9fb 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -7783,6 +7783,7 @@ static int instance_mkdir(const char *name) | |||
7783 | 7783 | ||
7784 | INIT_LIST_HEAD(&tr->systems); | 7784 | INIT_LIST_HEAD(&tr->systems); |
7785 | INIT_LIST_HEAD(&tr->events); | 7785 | INIT_LIST_HEAD(&tr->events); |
7786 | INIT_LIST_HEAD(&tr->hist_vars); | ||
7786 | 7787 | ||
7787 | if (allocate_trace_buffers(tr, trace_buf_size) < 0) | 7788 | if (allocate_trace_buffers(tr, trace_buf_size) < 0) |
7788 | goto out_free_tr; | 7789 | goto out_free_tr; |
@@ -8533,6 +8534,7 @@ __init static int tracer_alloc_buffers(void) | |||
8533 | 8534 | ||
8534 | INIT_LIST_HEAD(&global_trace.systems); | 8535 | INIT_LIST_HEAD(&global_trace.systems); |
8535 | INIT_LIST_HEAD(&global_trace.events); | 8536 | INIT_LIST_HEAD(&global_trace.events); |
8537 | INIT_LIST_HEAD(&global_trace.hist_vars); | ||
8536 | list_add(&global_trace.list, &ftrace_trace_arrays); | 8538 | list_add(&global_trace.list, &ftrace_trace_arrays); |
8537 | 8539 | ||
8538 | apply_trace_boot_options(); | 8540 | apply_trace_boot_options(); |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 89771b4f16df..99b7ee7ed127 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -274,6 +274,7 @@ struct trace_array { | |||
274 | int function_enabled; | 274 | int function_enabled; |
275 | #endif | 275 | #endif |
276 | int time_stamp_abs_ref; | 276 | int time_stamp_abs_ref; |
277 | struct list_head hist_vars; | ||
277 | }; | 278 | }; |
278 | 279 | ||
279 | enum { | 280 | enum { |
@@ -1548,6 +1549,8 @@ extern void pause_named_trigger(struct event_trigger_data *data); | |||
1548 | extern void unpause_named_trigger(struct event_trigger_data *data); | 1549 | extern void unpause_named_trigger(struct event_trigger_data *data); |
1549 | extern void set_named_trigger_data(struct event_trigger_data *data, | 1550 | extern void set_named_trigger_data(struct event_trigger_data *data, |
1550 | struct event_trigger_data *named_data); | 1551 | struct event_trigger_data *named_data); |
1552 | extern struct event_trigger_data * | ||
1553 | get_named_trigger_data(struct event_trigger_data *data); | ||
1551 | extern int register_event_command(struct event_command *cmd); | 1554 | extern int register_event_command(struct event_command *cmd); |
1552 | extern int unregister_event_command(struct event_command *cmd); | 1555 | extern int unregister_event_command(struct event_command *cmd); |
1553 | extern int register_trigger_hist_enable_disable_cmds(void); | 1556 | extern int register_trigger_hist_enable_disable_cmds(void); |
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index e30bd86bee8e..dbcdd2ff76a4 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c | |||
@@ -59,7 +59,12 @@ struct hist_field { | |||
59 | struct hist_trigger_data *hist_data; | 59 | struct hist_trigger_data *hist_data; |
60 | struct hist_var var; | 60 | struct hist_var var; |
61 | enum field_op_id operator; | 61 | enum field_op_id operator; |
62 | char *system; | ||
63 | char *event_name; | ||
62 | char *name; | 64 | char *name; |
65 | unsigned int var_idx; | ||
66 | unsigned int var_ref_idx; | ||
67 | bool read_once; | ||
63 | }; | 68 | }; |
64 | 69 | ||
65 | static u64 hist_field_none(struct hist_field *field, | 70 | static u64 hist_field_none(struct hist_field *field, |
@@ -214,6 +219,7 @@ enum hist_field_flags { | |||
214 | HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11, | 219 | HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11, |
215 | HIST_FIELD_FL_VAR = 1 << 12, | 220 | HIST_FIELD_FL_VAR = 1 << 12, |
216 | HIST_FIELD_FL_EXPR = 1 << 13, | 221 | HIST_FIELD_FL_EXPR = 1 << 13, |
222 | HIST_FIELD_FL_VAR_REF = 1 << 14, | ||
217 | }; | 223 | }; |
218 | 224 | ||
219 | struct var_defs { | 225 | struct var_defs { |
@@ -253,6 +259,8 @@ struct hist_trigger_data { | |||
253 | struct tracing_map *map; | 259 | struct tracing_map *map; |
254 | bool enable_timestamps; | 260 | bool enable_timestamps; |
255 | bool remove; | 261 | bool remove; |
262 | struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; | ||
263 | unsigned int n_var_refs; | ||
256 | }; | 264 | }; |
257 | 265 | ||
258 | static u64 hist_field_timestamp(struct hist_field *hist_field, | 266 | static u64 hist_field_timestamp(struct hist_field *hist_field, |
@@ -271,6 +279,214 @@ static u64 hist_field_timestamp(struct hist_field *hist_field, | |||
271 | return ts; | 279 | return ts; |
272 | } | 280 | } |
273 | 281 | ||
282 | struct hist_var_data { | ||
283 | struct list_head list; | ||
284 | struct hist_trigger_data *hist_data; | ||
285 | }; | ||
286 | |||
287 | static struct hist_field * | ||
288 | check_field_for_var_ref(struct hist_field *hist_field, | ||
289 | struct hist_trigger_data *var_data, | ||
290 | unsigned int var_idx) | ||
291 | { | ||
292 | struct hist_field *found = NULL; | ||
293 | |||
294 | if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF) { | ||
295 | if (hist_field->var.idx == var_idx && | ||
296 | hist_field->var.hist_data == var_data) { | ||
297 | found = hist_field; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | return found; | ||
302 | } | ||
303 | |||
304 | static struct hist_field * | ||
305 | check_field_for_var_refs(struct hist_trigger_data *hist_data, | ||
306 | struct hist_field *hist_field, | ||
307 | struct hist_trigger_data *var_data, | ||
308 | unsigned int var_idx, | ||
309 | unsigned int level) | ||
310 | { | ||
311 | struct hist_field *found = NULL; | ||
312 | unsigned int i; | ||
313 | |||
314 | if (level > 3) | ||
315 | return found; | ||
316 | |||
317 | if (!hist_field) | ||
318 | return found; | ||
319 | |||
320 | found = check_field_for_var_ref(hist_field, var_data, var_idx); | ||
321 | if (found) | ||
322 | return found; | ||
323 | |||
324 | for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) { | ||
325 | struct hist_field *operand; | ||
326 | |||
327 | operand = hist_field->operands[i]; | ||
328 | found = check_field_for_var_refs(hist_data, operand, var_data, | ||
329 | var_idx, level + 1); | ||
330 | if (found) | ||
331 | return found; | ||
332 | } | ||
333 | |||
334 | return found; | ||
335 | } | ||
336 | |||
337 | static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data, | ||
338 | struct hist_trigger_data *var_data, | ||
339 | unsigned int var_idx) | ||
340 | { | ||
341 | struct hist_field *hist_field, *found = NULL; | ||
342 | unsigned int i; | ||
343 | |||
344 | for_each_hist_field(i, hist_data) { | ||
345 | hist_field = hist_data->fields[i]; | ||
346 | found = check_field_for_var_refs(hist_data, hist_field, | ||
347 | var_data, var_idx, 0); | ||
348 | if (found) | ||
349 | return found; | ||
350 | } | ||
351 | |||
352 | return found; | ||
353 | } | ||
354 | |||
355 | static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data, | ||
356 | unsigned int var_idx) | ||
357 | { | ||
358 | struct trace_array *tr = hist_data->event_file->tr; | ||
359 | struct hist_field *found = NULL; | ||
360 | struct hist_var_data *var_data; | ||
361 | |||
362 | list_for_each_entry(var_data, &tr->hist_vars, list) { | ||
363 | if (var_data->hist_data == hist_data) | ||
364 | continue; | ||
365 | found = find_var_ref(var_data->hist_data, hist_data, var_idx); | ||
366 | if (found) | ||
367 | break; | ||
368 | } | ||
369 | |||
370 | return found; | ||
371 | } | ||
372 | |||
373 | static bool check_var_refs(struct hist_trigger_data *hist_data) | ||
374 | { | ||
375 | struct hist_field *field; | ||
376 | bool found = false; | ||
377 | int i; | ||
378 | |||
379 | for_each_hist_field(i, hist_data) { | ||
380 | field = hist_data->fields[i]; | ||
381 | if (field && field->flags & HIST_FIELD_FL_VAR) { | ||
382 | if (find_any_var_ref(hist_data, field->var.idx)) { | ||
383 | found = true; | ||
384 | break; | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | |||
389 | return found; | ||
390 | } | ||
391 | |||
392 | static struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data) | ||
393 | { | ||
394 | struct trace_array *tr = hist_data->event_file->tr; | ||
395 | struct hist_var_data *var_data, *found = NULL; | ||
396 | |||
397 | list_for_each_entry(var_data, &tr->hist_vars, list) { | ||
398 | if (var_data->hist_data == hist_data) { | ||
399 | found = var_data; | ||
400 | break; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | return found; | ||
405 | } | ||
406 | |||
407 | static bool field_has_hist_vars(struct hist_field *hist_field, | ||
408 | unsigned int level) | ||
409 | { | ||
410 | int i; | ||
411 | |||
412 | if (level > 3) | ||
413 | return false; | ||
414 | |||
415 | if (!hist_field) | ||
416 | return false; | ||
417 | |||
418 | if (hist_field->flags & HIST_FIELD_FL_VAR || | ||
419 | hist_field->flags & HIST_FIELD_FL_VAR_REF) | ||
420 | return true; | ||
421 | |||
422 | for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) { | ||
423 | struct hist_field *operand; | ||
424 | |||
425 | operand = hist_field->operands[i]; | ||
426 | if (field_has_hist_vars(operand, level + 1)) | ||
427 | return true; | ||
428 | } | ||
429 | |||
430 | return false; | ||
431 | } | ||
432 | |||
433 | static bool has_hist_vars(struct hist_trigger_data *hist_data) | ||
434 | { | ||
435 | struct hist_field *hist_field; | ||
436 | int i; | ||
437 | |||
438 | for_each_hist_field(i, hist_data) { | ||
439 | hist_field = hist_data->fields[i]; | ||
440 | if (field_has_hist_vars(hist_field, 0)) | ||
441 | return true; | ||
442 | } | ||
443 | |||
444 | return false; | ||
445 | } | ||
446 | |||
447 | static int save_hist_vars(struct hist_trigger_data *hist_data) | ||
448 | { | ||
449 | struct trace_array *tr = hist_data->event_file->tr; | ||
450 | struct hist_var_data *var_data; | ||
451 | |||
452 | var_data = find_hist_vars(hist_data); | ||
453 | if (var_data) | ||
454 | return 0; | ||
455 | |||
456 | if (trace_array_get(tr) < 0) | ||
457 | return -ENODEV; | ||
458 | |||
459 | var_data = kzalloc(sizeof(*var_data), GFP_KERNEL); | ||
460 | if (!var_data) { | ||
461 | trace_array_put(tr); | ||
462 | return -ENOMEM; | ||
463 | } | ||
464 | |||
465 | var_data->hist_data = hist_data; | ||
466 | list_add(&var_data->list, &tr->hist_vars); | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static void remove_hist_vars(struct hist_trigger_data *hist_data) | ||
472 | { | ||
473 | struct trace_array *tr = hist_data->event_file->tr; | ||
474 | struct hist_var_data *var_data; | ||
475 | |||
476 | var_data = find_hist_vars(hist_data); | ||
477 | if (!var_data) | ||
478 | return; | ||
479 | |||
480 | if (WARN_ON(check_var_refs(hist_data))) | ||
481 | return; | ||
482 | |||
483 | list_del(&var_data->list); | ||
484 | |||
485 | kfree(var_data); | ||
486 | |||
487 | trace_array_put(tr); | ||
488 | } | ||
489 | |||
274 | static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, | 490 | static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, |
275 | const char *var_name) | 491 | const char *var_name) |
276 | { | 492 | { |
@@ -313,10 +529,137 @@ static struct hist_field *find_var(struct hist_trigger_data *hist_data, | |||
313 | return NULL; | 529 | return NULL; |
314 | } | 530 | } |
315 | 531 | ||
532 | static struct trace_event_file *find_var_file(struct trace_array *tr, | ||
533 | char *system, | ||
534 | char *event_name, | ||
535 | char *var_name) | ||
536 | { | ||
537 | struct hist_trigger_data *var_hist_data; | ||
538 | struct hist_var_data *var_data; | ||
539 | struct trace_event_file *file, *found = NULL; | ||
540 | |||
541 | if (system) | ||
542 | return find_event_file(tr, system, event_name); | ||
543 | |||
544 | list_for_each_entry(var_data, &tr->hist_vars, list) { | ||
545 | var_hist_data = var_data->hist_data; | ||
546 | file = var_hist_data->event_file; | ||
547 | if (file == found) | ||
548 | continue; | ||
549 | |||
550 | if (find_var_field(var_hist_data, var_name)) { | ||
551 | if (found) | ||
552 | return NULL; | ||
553 | |||
554 | found = file; | ||
555 | } | ||
556 | } | ||
557 | |||
558 | return found; | ||
559 | } | ||
560 | |||
561 | static struct hist_field *find_file_var(struct trace_event_file *file, | ||
562 | const char *var_name) | ||
563 | { | ||
564 | struct hist_trigger_data *test_data; | ||
565 | struct event_trigger_data *test; | ||
566 | struct hist_field *hist_field; | ||
567 | |||
568 | list_for_each_entry_rcu(test, &file->triggers, list) { | ||
569 | if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { | ||
570 | test_data = test->private_data; | ||
571 | hist_field = find_var_field(test_data, var_name); | ||
572 | if (hist_field) | ||
573 | return hist_field; | ||
574 | } | ||
575 | } | ||
576 | |||
577 | return NULL; | ||
578 | } | ||
579 | |||
580 | static struct hist_field *find_event_var(struct hist_trigger_data *hist_data, | ||
581 | char *system, | ||
582 | char *event_name, | ||
583 | char *var_name) | ||
584 | { | ||
585 | struct trace_array *tr = hist_data->event_file->tr; | ||
586 | struct hist_field *hist_field = NULL; | ||
587 | struct trace_event_file *file; | ||
588 | |||
589 | file = find_var_file(tr, system, event_name, var_name); | ||
590 | if (!file) | ||
591 | return NULL; | ||
592 | |||
593 | hist_field = find_file_var(file, var_name); | ||
594 | |||
595 | return hist_field; | ||
596 | } | ||
597 | |||
316 | struct hist_elt_data { | 598 | struct hist_elt_data { |
317 | char *comm; | 599 | char *comm; |
600 | u64 *var_ref_vals; | ||
318 | }; | 601 | }; |
319 | 602 | ||
603 | static u64 hist_field_var_ref(struct hist_field *hist_field, | ||
604 | struct tracing_map_elt *elt, | ||
605 | struct ring_buffer_event *rbe, | ||
606 | void *event) | ||
607 | { | ||
608 | struct hist_elt_data *elt_data; | ||
609 | u64 var_val = 0; | ||
610 | |||
611 | elt_data = elt->private_data; | ||
612 | var_val = elt_data->var_ref_vals[hist_field->var_ref_idx]; | ||
613 | |||
614 | return var_val; | ||
615 | } | ||
616 | |||
617 | static bool resolve_var_refs(struct hist_trigger_data *hist_data, void *key, | ||
618 | u64 *var_ref_vals, bool self) | ||
619 | { | ||
620 | struct hist_trigger_data *var_data; | ||
621 | struct tracing_map_elt *var_elt; | ||
622 | struct hist_field *hist_field; | ||
623 | unsigned int i, var_idx; | ||
624 | bool resolved = true; | ||
625 | u64 var_val = 0; | ||
626 | |||
627 | for (i = 0; i < hist_data->n_var_refs; i++) { | ||
628 | hist_field = hist_data->var_refs[i]; | ||
629 | var_idx = hist_field->var.idx; | ||
630 | var_data = hist_field->var.hist_data; | ||
631 | |||
632 | if (var_data == NULL) { | ||
633 | resolved = false; | ||
634 | break; | ||
635 | } | ||
636 | |||
637 | if ((self && var_data != hist_data) || | ||
638 | (!self && var_data == hist_data)) | ||
639 | continue; | ||
640 | |||
641 | var_elt = tracing_map_lookup(var_data->map, key); | ||
642 | if (!var_elt) { | ||
643 | resolved = false; | ||
644 | break; | ||
645 | } | ||
646 | |||
647 | if (!tracing_map_var_set(var_elt, var_idx)) { | ||
648 | resolved = false; | ||
649 | break; | ||
650 | } | ||
651 | |||
652 | if (self || !hist_field->read_once) | ||
653 | var_val = tracing_map_read_var(var_elt, var_idx); | ||
654 | else | ||
655 | var_val = tracing_map_read_var_once(var_elt, var_idx); | ||
656 | |||
657 | var_ref_vals[i] = var_val; | ||
658 | } | ||
659 | |||
660 | return resolved; | ||
661 | } | ||
662 | |||
320 | static const char *hist_field_name(struct hist_field *field, | 663 | static const char *hist_field_name(struct hist_field *field, |
321 | unsigned int level) | 664 | unsigned int level) |
322 | { | 665 | { |
@@ -331,8 +674,20 @@ static const char *hist_field_name(struct hist_field *field, | |||
331 | field_name = hist_field_name(field->operands[0], ++level); | 674 | field_name = hist_field_name(field->operands[0], ++level); |
332 | else if (field->flags & HIST_FIELD_FL_TIMESTAMP) | 675 | else if (field->flags & HIST_FIELD_FL_TIMESTAMP) |
333 | field_name = "common_timestamp"; | 676 | field_name = "common_timestamp"; |
334 | else if (field->flags & HIST_FIELD_FL_EXPR) | 677 | else if (field->flags & HIST_FIELD_FL_EXPR || |
335 | field_name = field->name; | 678 | field->flags & HIST_FIELD_FL_VAR_REF) { |
679 | if (field->system) { | ||
680 | static char full_name[MAX_FILTER_STR_VAL]; | ||
681 | |||
682 | strcat(full_name, field->system); | ||
683 | strcat(full_name, "."); | ||
684 | strcat(full_name, field->event_name); | ||
685 | strcat(full_name, "."); | ||
686 | strcat(full_name, field->name); | ||
687 | field_name = full_name; | ||
688 | } else | ||
689 | field_name = field->name; | ||
690 | } | ||
336 | 691 | ||
337 | if (field_name == NULL) | 692 | if (field_name == NULL) |
338 | field_name = ""; | 693 | field_name = ""; |
@@ -612,6 +967,9 @@ static const char *get_hist_field_flags(struct hist_field *hist_field) | |||
612 | 967 | ||
613 | static void expr_field_str(struct hist_field *field, char *expr) | 968 | static void expr_field_str(struct hist_field *field, char *expr) |
614 | { | 969 | { |
970 | if (field->flags & HIST_FIELD_FL_VAR_REF) | ||
971 | strcat(expr, "$"); | ||
972 | |||
615 | strcat(expr, hist_field_name(field, 0)); | 973 | strcat(expr, hist_field_name(field, 0)); |
616 | 974 | ||
617 | if (field->flags) { | 975 | if (field->flags) { |
@@ -742,6 +1100,11 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data, | |||
742 | if (flags & HIST_FIELD_FL_EXPR) | 1100 | if (flags & HIST_FIELD_FL_EXPR) |
743 | goto out; /* caller will populate */ | 1101 | goto out; /* caller will populate */ |
744 | 1102 | ||
1103 | if (flags & HIST_FIELD_FL_VAR_REF) { | ||
1104 | hist_field->fn = hist_field_var_ref; | ||
1105 | goto out; | ||
1106 | } | ||
1107 | |||
745 | if (flags & HIST_FIELD_FL_HITCOUNT) { | 1108 | if (flags & HIST_FIELD_FL_HITCOUNT) { |
746 | hist_field->fn = hist_field_counter; | 1109 | hist_field->fn = hist_field_counter; |
747 | hist_field->size = sizeof(u64); | 1110 | hist_field->size = sizeof(u64); |
@@ -835,6 +1198,144 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data) | |||
835 | } | 1198 | } |
836 | } | 1199 | } |
837 | 1200 | ||
1201 | static int init_var_ref(struct hist_field *ref_field, | ||
1202 | struct hist_field *var_field, | ||
1203 | char *system, char *event_name) | ||
1204 | { | ||
1205 | int err = 0; | ||
1206 | |||
1207 | ref_field->var.idx = var_field->var.idx; | ||
1208 | ref_field->var.hist_data = var_field->hist_data; | ||
1209 | ref_field->size = var_field->size; | ||
1210 | ref_field->is_signed = var_field->is_signed; | ||
1211 | ref_field->flags |= var_field->flags & | ||
1212 | (HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS); | ||
1213 | |||
1214 | if (system) { | ||
1215 | ref_field->system = kstrdup(system, GFP_KERNEL); | ||
1216 | if (!ref_field->system) | ||
1217 | return -ENOMEM; | ||
1218 | } | ||
1219 | |||
1220 | if (event_name) { | ||
1221 | ref_field->event_name = kstrdup(event_name, GFP_KERNEL); | ||
1222 | if (!ref_field->event_name) { | ||
1223 | err = -ENOMEM; | ||
1224 | goto free; | ||
1225 | } | ||
1226 | } | ||
1227 | |||
1228 | ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL); | ||
1229 | if (!ref_field->name) { | ||
1230 | err = -ENOMEM; | ||
1231 | goto free; | ||
1232 | } | ||
1233 | |||
1234 | ref_field->type = kstrdup(var_field->type, GFP_KERNEL); | ||
1235 | if (!ref_field->type) { | ||
1236 | err = -ENOMEM; | ||
1237 | goto free; | ||
1238 | } | ||
1239 | out: | ||
1240 | return err; | ||
1241 | free: | ||
1242 | kfree(ref_field->system); | ||
1243 | kfree(ref_field->event_name); | ||
1244 | kfree(ref_field->name); | ||
1245 | |||
1246 | goto out; | ||
1247 | } | ||
1248 | |||
1249 | static struct hist_field *create_var_ref(struct hist_field *var_field, | ||
1250 | char *system, char *event_name) | ||
1251 | { | ||
1252 | unsigned long flags = HIST_FIELD_FL_VAR_REF; | ||
1253 | struct hist_field *ref_field; | ||
1254 | |||
1255 | ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL); | ||
1256 | if (ref_field) { | ||
1257 | if (init_var_ref(ref_field, var_field, system, event_name)) { | ||
1258 | destroy_hist_field(ref_field, 0); | ||
1259 | return NULL; | ||
1260 | } | ||
1261 | } | ||
1262 | |||
1263 | return ref_field; | ||
1264 | } | ||
1265 | |||
1266 | static bool is_var_ref(char *var_name) | ||
1267 | { | ||
1268 | if (!var_name || strlen(var_name) < 2 || var_name[0] != '$') | ||
1269 | return false; | ||
1270 | |||
1271 | return true; | ||
1272 | } | ||
1273 | |||
1274 | static char *field_name_from_var(struct hist_trigger_data *hist_data, | ||
1275 | char *var_name) | ||
1276 | { | ||
1277 | char *name, *field; | ||
1278 | unsigned int i; | ||
1279 | |||
1280 | for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) { | ||
1281 | name = hist_data->attrs->var_defs.name[i]; | ||
1282 | |||
1283 | if (strcmp(var_name, name) == 0) { | ||
1284 | field = hist_data->attrs->var_defs.expr[i]; | ||
1285 | if (contains_operator(field) || is_var_ref(field)) | ||
1286 | continue; | ||
1287 | return field; | ||
1288 | } | ||
1289 | } | ||
1290 | |||
1291 | return NULL; | ||
1292 | } | ||
1293 | |||
1294 | static char *local_field_var_ref(struct hist_trigger_data *hist_data, | ||
1295 | char *system, char *event_name, | ||
1296 | char *var_name) | ||
1297 | { | ||
1298 | struct trace_event_call *call; | ||
1299 | |||
1300 | if (system && event_name) { | ||
1301 | call = hist_data->event_file->event_call; | ||
1302 | |||
1303 | if (strcmp(system, call->class->system) != 0) | ||
1304 | return NULL; | ||
1305 | |||
1306 | if (strcmp(event_name, trace_event_name(call)) != 0) | ||
1307 | return NULL; | ||
1308 | } | ||
1309 | |||
1310 | if (!!system != !!event_name) | ||
1311 | return NULL; | ||
1312 | |||
1313 | if (!is_var_ref(var_name)) | ||
1314 | return NULL; | ||
1315 | |||
1316 | var_name++; | ||
1317 | |||
1318 | return field_name_from_var(hist_data, var_name); | ||
1319 | } | ||
1320 | |||
1321 | static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data, | ||
1322 | char *system, char *event_name, | ||
1323 | char *var_name) | ||
1324 | { | ||
1325 | struct hist_field *var_field = NULL, *ref_field = NULL; | ||
1326 | |||
1327 | if (!is_var_ref(var_name)) | ||
1328 | return NULL; | ||
1329 | |||
1330 | var_name++; | ||
1331 | |||
1332 | var_field = find_event_var(hist_data, system, event_name, var_name); | ||
1333 | if (var_field) | ||
1334 | ref_field = create_var_ref(var_field, system, event_name); | ||
1335 | |||
1336 | return ref_field; | ||
1337 | } | ||
1338 | |||
838 | static struct ftrace_event_field * | 1339 | static struct ftrace_event_field * |
839 | parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, | 1340 | parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, |
840 | char *field_str, unsigned long *flags) | 1341 | char *field_str, unsigned long *flags) |
@@ -891,10 +1392,40 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data, | |||
891 | struct trace_event_file *file, char *str, | 1392 | struct trace_event_file *file, char *str, |
892 | unsigned long *flags, char *var_name) | 1393 | unsigned long *flags, char *var_name) |
893 | { | 1394 | { |
1395 | char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str; | ||
894 | struct ftrace_event_field *field = NULL; | 1396 | struct ftrace_event_field *field = NULL; |
895 | struct hist_field *hist_field = NULL; | 1397 | struct hist_field *hist_field = NULL; |
896 | int ret = 0; | 1398 | int ret = 0; |
897 | 1399 | ||
1400 | s = strchr(str, '.'); | ||
1401 | if (s) { | ||
1402 | s = strchr(++s, '.'); | ||
1403 | if (s) { | ||
1404 | ref_system = strsep(&str, "."); | ||
1405 | if (!str) { | ||
1406 | ret = -EINVAL; | ||
1407 | goto out; | ||
1408 | } | ||
1409 | ref_event = strsep(&str, "."); | ||
1410 | if (!str) { | ||
1411 | ret = -EINVAL; | ||
1412 | goto out; | ||
1413 | } | ||
1414 | ref_var = str; | ||
1415 | } | ||
1416 | } | ||
1417 | |||
1418 | s = local_field_var_ref(hist_data, ref_system, ref_event, ref_var); | ||
1419 | if (!s) { | ||
1420 | hist_field = parse_var_ref(hist_data, ref_system, ref_event, ref_var); | ||
1421 | if (hist_field) { | ||
1422 | hist_data->var_refs[hist_data->n_var_refs] = hist_field; | ||
1423 | hist_field->var_ref_idx = hist_data->n_var_refs++; | ||
1424 | return hist_field; | ||
1425 | } | ||
1426 | } else | ||
1427 | str = s; | ||
1428 | |||
898 | field = parse_field(hist_data, file, str, flags); | 1429 | field = parse_field(hist_data, file, str, flags); |
899 | if (IS_ERR(field)) { | 1430 | if (IS_ERR(field)) { |
900 | ret = PTR_ERR(field); | 1431 | ret = PTR_ERR(field); |
@@ -1066,6 +1597,9 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data, | |||
1066 | goto free; | 1597 | goto free; |
1067 | } | 1598 | } |
1068 | 1599 | ||
1600 | operand1->read_once = true; | ||
1601 | operand2->read_once = true; | ||
1602 | |||
1069 | expr->operands[0] = operand1; | 1603 | expr->operands[0] = operand1; |
1070 | expr->operands[1] = operand2; | 1604 | expr->operands[1] = operand2; |
1071 | expr->operator = field_op; | 1605 | expr->operator = field_op; |
@@ -1238,6 +1772,12 @@ static int create_key_field(struct hist_trigger_data *hist_data, | |||
1238 | goto out; | 1772 | goto out; |
1239 | } | 1773 | } |
1240 | 1774 | ||
1775 | if (hist_field->flags & HIST_FIELD_FL_VAR_REF) { | ||
1776 | destroy_hist_field(hist_field, 0); | ||
1777 | ret = -EINVAL; | ||
1778 | goto out; | ||
1779 | } | ||
1780 | |||
1241 | key_size = hist_field->size; | 1781 | key_size = hist_field->size; |
1242 | } | 1782 | } |
1243 | 1783 | ||
@@ -1576,6 +2116,7 @@ create_hist_data(unsigned int map_bits, | |||
1576 | 2116 | ||
1577 | hist_data->attrs = attrs; | 2117 | hist_data->attrs = attrs; |
1578 | hist_data->remove = remove; | 2118 | hist_data->remove = remove; |
2119 | hist_data->event_file = file; | ||
1579 | 2120 | ||
1580 | ret = create_hist_fields(hist_data, file); | 2121 | ret = create_hist_fields(hist_data, file); |
1581 | if (ret) | 2122 | if (ret) |
@@ -1598,12 +2139,6 @@ create_hist_data(unsigned int map_bits, | |||
1598 | ret = create_tracing_map_fields(hist_data); | 2139 | ret = create_tracing_map_fields(hist_data); |
1599 | if (ret) | 2140 | if (ret) |
1600 | goto free; | 2141 | goto free; |
1601 | |||
1602 | ret = tracing_map_init(hist_data->map); | ||
1603 | if (ret) | ||
1604 | goto free; | ||
1605 | |||
1606 | hist_data->event_file = file; | ||
1607 | out: | 2142 | out: |
1608 | return hist_data; | 2143 | return hist_data; |
1609 | free: | 2144 | free: |
@@ -1618,12 +2153,17 @@ create_hist_data(unsigned int map_bits, | |||
1618 | 2153 | ||
1619 | static void hist_trigger_elt_update(struct hist_trigger_data *hist_data, | 2154 | static void hist_trigger_elt_update(struct hist_trigger_data *hist_data, |
1620 | struct tracing_map_elt *elt, void *rec, | 2155 | struct tracing_map_elt *elt, void *rec, |
1621 | struct ring_buffer_event *rbe) | 2156 | struct ring_buffer_event *rbe, |
2157 | u64 *var_ref_vals) | ||
1622 | { | 2158 | { |
2159 | struct hist_elt_data *elt_data; | ||
1623 | struct hist_field *hist_field; | 2160 | struct hist_field *hist_field; |
1624 | unsigned int i, var_idx; | 2161 | unsigned int i, var_idx; |
1625 | u64 hist_val; | 2162 | u64 hist_val; |
1626 | 2163 | ||
2164 | elt_data = elt->private_data; | ||
2165 | elt_data->var_ref_vals = var_ref_vals; | ||
2166 | |||
1627 | for_each_hist_val_field(i, hist_data) { | 2167 | for_each_hist_val_field(i, hist_data) { |
1628 | hist_field = hist_data->fields[i]; | 2168 | hist_field = hist_data->fields[i]; |
1629 | hist_val = hist_field->fn(hist_field, elt, rbe, rec); | 2169 | hist_val = hist_field->fn(hist_field, elt, rbe, rec); |
@@ -1675,6 +2215,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec, | |||
1675 | struct hist_trigger_data *hist_data = data->private_data; | 2215 | struct hist_trigger_data *hist_data = data->private_data; |
1676 | bool use_compound_key = (hist_data->n_keys > 1); | 2216 | bool use_compound_key = (hist_data->n_keys > 1); |
1677 | unsigned long entries[HIST_STACKTRACE_DEPTH]; | 2217 | unsigned long entries[HIST_STACKTRACE_DEPTH]; |
2218 | u64 var_ref_vals[TRACING_MAP_VARS_MAX]; | ||
1678 | char compound_key[HIST_KEY_SIZE_MAX]; | 2219 | char compound_key[HIST_KEY_SIZE_MAX]; |
1679 | struct tracing_map_elt *elt = NULL; | 2220 | struct tracing_map_elt *elt = NULL; |
1680 | struct stack_trace stacktrace; | 2221 | struct stack_trace stacktrace; |
@@ -1714,9 +2255,15 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec, | |||
1714 | if (use_compound_key) | 2255 | if (use_compound_key) |
1715 | key = compound_key; | 2256 | key = compound_key; |
1716 | 2257 | ||
2258 | if (hist_data->n_var_refs && | ||
2259 | !resolve_var_refs(hist_data, key, var_ref_vals, false)) | ||
2260 | return; | ||
2261 | |||
1717 | elt = tracing_map_insert(hist_data->map, key); | 2262 | elt = tracing_map_insert(hist_data->map, key); |
1718 | if (elt) | 2263 | if (!elt) |
1719 | hist_trigger_elt_update(hist_data, elt, rec, rbe); | 2264 | return; |
2265 | |||
2266 | hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals); | ||
1720 | } | 2267 | } |
1721 | 2268 | ||
1722 | static void hist_trigger_stacktrace_print(struct seq_file *m, | 2269 | static void hist_trigger_stacktrace_print(struct seq_file *m, |
@@ -1931,8 +2478,11 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) | |||
1931 | 2478 | ||
1932 | if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) | 2479 | if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) |
1933 | seq_puts(m, "common_timestamp"); | 2480 | seq_puts(m, "common_timestamp"); |
1934 | else if (field_name) | 2481 | else if (field_name) { |
2482 | if (hist_field->flags & HIST_FIELD_FL_VAR_REF) | ||
2483 | seq_putc(m, '$'); | ||
1935 | seq_printf(m, "%s", field_name); | 2484 | seq_printf(m, "%s", field_name); |
2485 | } | ||
1936 | 2486 | ||
1937 | if (hist_field->flags) { | 2487 | if (hist_field->flags) { |
1938 | const char *flags_str = get_hist_field_flags(hist_field); | 2488 | const char *flags_str = get_hist_field_flags(hist_field); |
@@ -2072,7 +2622,11 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops, | |||
2072 | if (!data->ref) { | 2622 | if (!data->ref) { |
2073 | if (data->name) | 2623 | if (data->name) |
2074 | del_named_trigger(data); | 2624 | del_named_trigger(data); |
2625 | |||
2075 | trigger_data_free(data); | 2626 | trigger_data_free(data); |
2627 | |||
2628 | remove_hist_vars(hist_data); | ||
2629 | |||
2076 | destroy_hist_data(hist_data); | 2630 | destroy_hist_data(hist_data); |
2077 | } | 2631 | } |
2078 | } | 2632 | } |
@@ -2285,23 +2839,55 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops, | |||
2285 | goto out; | 2839 | goto out; |
2286 | } | 2840 | } |
2287 | 2841 | ||
2288 | list_add_rcu(&data->list, &file->triggers); | ||
2289 | ret++; | 2842 | ret++; |
2290 | 2843 | ||
2291 | update_cond_flag(file); | ||
2292 | |||
2293 | if (hist_data->enable_timestamps) | 2844 | if (hist_data->enable_timestamps) |
2294 | tracing_set_time_stamp_abs(file->tr, true); | 2845 | tracing_set_time_stamp_abs(file->tr, true); |
2846 | out: | ||
2847 | return ret; | ||
2848 | } | ||
2849 | |||
2850 | static int hist_trigger_enable(struct event_trigger_data *data, | ||
2851 | struct trace_event_file *file) | ||
2852 | { | ||
2853 | int ret = 0; | ||
2854 | |||
2855 | list_add_tail_rcu(&data->list, &file->triggers); | ||
2856 | |||
2857 | update_cond_flag(file); | ||
2295 | 2858 | ||
2296 | if (trace_event_trigger_enable_disable(file, 1) < 0) { | 2859 | if (trace_event_trigger_enable_disable(file, 1) < 0) { |
2297 | list_del_rcu(&data->list); | 2860 | list_del_rcu(&data->list); |
2298 | update_cond_flag(file); | 2861 | update_cond_flag(file); |
2299 | ret--; | 2862 | ret--; |
2300 | } | 2863 | } |
2301 | out: | 2864 | |
2302 | return ret; | 2865 | return ret; |
2303 | } | 2866 | } |
2304 | 2867 | ||
2868 | static bool hist_trigger_check_refs(struct event_trigger_data *data, | ||
2869 | struct trace_event_file *file) | ||
2870 | { | ||
2871 | struct hist_trigger_data *hist_data = data->private_data; | ||
2872 | struct event_trigger_data *test, *named_data = NULL; | ||
2873 | |||
2874 | if (hist_data->attrs->name) | ||
2875 | named_data = find_named_trigger(hist_data->attrs->name); | ||
2876 | |||
2877 | list_for_each_entry_rcu(test, &file->triggers, list) { | ||
2878 | if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { | ||
2879 | if (!hist_trigger_match(data, test, named_data, false)) | ||
2880 | continue; | ||
2881 | hist_data = test->private_data; | ||
2882 | if (check_var_refs(hist_data)) | ||
2883 | return true; | ||
2884 | break; | ||
2885 | } | ||
2886 | } | ||
2887 | |||
2888 | return false; | ||
2889 | } | ||
2890 | |||
2305 | static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, | 2891 | static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, |
2306 | struct event_trigger_data *data, | 2892 | struct event_trigger_data *data, |
2307 | struct trace_event_file *file) | 2893 | struct trace_event_file *file) |
@@ -2334,11 +2920,30 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops, | |||
2334 | } | 2920 | } |
2335 | } | 2921 | } |
2336 | 2922 | ||
2923 | static bool hist_file_check_refs(struct trace_event_file *file) | ||
2924 | { | ||
2925 | struct hist_trigger_data *hist_data; | ||
2926 | struct event_trigger_data *test; | ||
2927 | |||
2928 | list_for_each_entry_rcu(test, &file->triggers, list) { | ||
2929 | if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { | ||
2930 | hist_data = test->private_data; | ||
2931 | if (check_var_refs(hist_data)) | ||
2932 | return true; | ||
2933 | } | ||
2934 | } | ||
2935 | |||
2936 | return false; | ||
2937 | } | ||
2938 | |||
2337 | static void hist_unreg_all(struct trace_event_file *file) | 2939 | static void hist_unreg_all(struct trace_event_file *file) |
2338 | { | 2940 | { |
2339 | struct event_trigger_data *test, *n; | 2941 | struct event_trigger_data *test, *n; |
2340 | struct hist_trigger_data *hist_data; | 2942 | struct hist_trigger_data *hist_data; |
2341 | 2943 | ||
2944 | if (hist_file_check_refs(file)) | ||
2945 | return; | ||
2946 | |||
2342 | list_for_each_entry_safe(test, n, &file->triggers, list) { | 2947 | list_for_each_entry_safe(test, n, &file->triggers, list) { |
2343 | if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { | 2948 | if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { |
2344 | hist_data = test->private_data; | 2949 | hist_data = test->private_data; |
@@ -2414,6 +3019,11 @@ static int event_hist_trigger_func(struct event_command *cmd_ops, | |||
2414 | } | 3019 | } |
2415 | 3020 | ||
2416 | if (remove) { | 3021 | if (remove) { |
3022 | if (hist_trigger_check_refs(trigger_data, file)) { | ||
3023 | ret = -EBUSY; | ||
3024 | goto out_free; | ||
3025 | } | ||
3026 | |||
2417 | cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); | 3027 | cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); |
2418 | ret = 0; | 3028 | ret = 0; |
2419 | goto out_free; | 3029 | goto out_free; |
@@ -2431,14 +3041,33 @@ static int event_hist_trigger_func(struct event_command *cmd_ops, | |||
2431 | goto out_free; | 3041 | goto out_free; |
2432 | } else if (ret < 0) | 3042 | } else if (ret < 0) |
2433 | goto out_free; | 3043 | goto out_free; |
3044 | |||
3045 | if (get_named_trigger_data(trigger_data)) | ||
3046 | goto enable; | ||
3047 | |||
3048 | if (has_hist_vars(hist_data)) | ||
3049 | save_hist_vars(hist_data); | ||
3050 | |||
3051 | ret = tracing_map_init(hist_data->map); | ||
3052 | if (ret) | ||
3053 | goto out_unreg; | ||
3054 | enable: | ||
3055 | ret = hist_trigger_enable(trigger_data, file); | ||
3056 | if (ret) | ||
3057 | goto out_unreg; | ||
3058 | |||
2434 | /* Just return zero, not the number of registered triggers */ | 3059 | /* Just return zero, not the number of registered triggers */ |
2435 | ret = 0; | 3060 | ret = 0; |
2436 | out: | 3061 | out: |
2437 | return ret; | 3062 | return ret; |
3063 | out_unreg: | ||
3064 | cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); | ||
2438 | out_free: | 3065 | out_free: |
2439 | if (cmd_ops->set_filter) | 3066 | if (cmd_ops->set_filter) |
2440 | cmd_ops->set_filter(NULL, trigger_data, NULL); | 3067 | cmd_ops->set_filter(NULL, trigger_data, NULL); |
2441 | 3068 | ||
3069 | remove_hist_vars(hist_data); | ||
3070 | |||
2442 | kfree(trigger_data); | 3071 | kfree(trigger_data); |
2443 | 3072 | ||
2444 | destroy_hist_data(hist_data); | 3073 | destroy_hist_data(hist_data); |
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 632471692462..d251cabcf69a 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c | |||
@@ -909,6 +909,12 @@ void set_named_trigger_data(struct event_trigger_data *data, | |||
909 | data->named_data = named_data; | 909 | data->named_data = named_data; |
910 | } | 910 | } |
911 | 911 | ||
912 | struct event_trigger_data * | ||
913 | get_named_trigger_data(struct event_trigger_data *data) | ||
914 | { | ||
915 | return data->named_data; | ||
916 | } | ||
917 | |||
912 | static void | 918 | static void |
913 | traceon_trigger(struct event_trigger_data *data, void *rec, | 919 | traceon_trigger(struct event_trigger_data *data, void *rec, |
914 | struct ring_buffer_event *event) | 920 | struct ring_buffer_event *event) |