diff options
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 136 |
1 files changed, 65 insertions, 71 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index f309e8014c78..cb6c0d2af68f 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -417,8 +417,8 @@ event_filter_match(struct perf_event *event) | |||
417 | return event->cpu == -1 || event->cpu == smp_processor_id(); | 417 | return event->cpu == -1 || event->cpu == smp_processor_id(); |
418 | } | 418 | } |
419 | 419 | ||
420 | static int | 420 | static void |
421 | __event_sched_out(struct perf_event *event, | 421 | event_sched_out(struct perf_event *event, |
422 | struct perf_cpu_context *cpuctx, | 422 | struct perf_cpu_context *cpuctx, |
423 | struct perf_event_context *ctx) | 423 | struct perf_event_context *ctx) |
424 | { | 424 | { |
@@ -437,13 +437,14 @@ __event_sched_out(struct perf_event *event, | |||
437 | } | 437 | } |
438 | 438 | ||
439 | if (event->state != PERF_EVENT_STATE_ACTIVE) | 439 | if (event->state != PERF_EVENT_STATE_ACTIVE) |
440 | return 0; | 440 | return; |
441 | 441 | ||
442 | event->state = PERF_EVENT_STATE_INACTIVE; | 442 | event->state = PERF_EVENT_STATE_INACTIVE; |
443 | if (event->pending_disable) { | 443 | if (event->pending_disable) { |
444 | event->pending_disable = 0; | 444 | event->pending_disable = 0; |
445 | event->state = PERF_EVENT_STATE_OFF; | 445 | event->state = PERF_EVENT_STATE_OFF; |
446 | } | 446 | } |
447 | event->tstamp_stopped = ctx->time; | ||
447 | event->pmu->del(event, 0); | 448 | event->pmu->del(event, 0); |
448 | event->oncpu = -1; | 449 | event->oncpu = -1; |
449 | 450 | ||
@@ -452,19 +453,6 @@ __event_sched_out(struct perf_event *event, | |||
452 | ctx->nr_active--; | 453 | ctx->nr_active--; |
453 | if (event->attr.exclusive || !cpuctx->active_oncpu) | 454 | if (event->attr.exclusive || !cpuctx->active_oncpu) |
454 | cpuctx->exclusive = 0; | 455 | cpuctx->exclusive = 0; |
455 | return 1; | ||
456 | } | ||
457 | |||
458 | static void | ||
459 | event_sched_out(struct perf_event *event, | ||
460 | struct perf_cpu_context *cpuctx, | ||
461 | struct perf_event_context *ctx) | ||
462 | { | ||
463 | int ret; | ||
464 | |||
465 | ret = __event_sched_out(event, cpuctx, ctx); | ||
466 | if (ret) | ||
467 | event->tstamp_stopped = ctx->time; | ||
468 | } | 456 | } |
469 | 457 | ||
470 | static void | 458 | static void |
@@ -664,7 +652,7 @@ retry: | |||
664 | } | 652 | } |
665 | 653 | ||
666 | static int | 654 | static int |
667 | __event_sched_in(struct perf_event *event, | 655 | event_sched_in(struct perf_event *event, |
668 | struct perf_cpu_context *cpuctx, | 656 | struct perf_cpu_context *cpuctx, |
669 | struct perf_event_context *ctx) | 657 | struct perf_event_context *ctx) |
670 | { | 658 | { |
@@ -684,6 +672,10 @@ __event_sched_in(struct perf_event *event, | |||
684 | return -EAGAIN; | 672 | return -EAGAIN; |
685 | } | 673 | } |
686 | 674 | ||
675 | event->tstamp_running += ctx->time - event->tstamp_stopped; | ||
676 | |||
677 | event->shadow_ctx_time = ctx->time - ctx->timestamp; | ||
678 | |||
687 | if (!is_software_event(event)) | 679 | if (!is_software_event(event)) |
688 | cpuctx->active_oncpu++; | 680 | cpuctx->active_oncpu++; |
689 | ctx->nr_active++; | 681 | ctx->nr_active++; |
@@ -694,35 +686,6 @@ __event_sched_in(struct perf_event *event, | |||
694 | return 0; | 686 | return 0; |
695 | } | 687 | } |
696 | 688 | ||
697 | static inline int | ||
698 | event_sched_in(struct perf_event *event, | ||
699 | struct perf_cpu_context *cpuctx, | ||
700 | struct perf_event_context *ctx) | ||
701 | { | ||
702 | int ret = __event_sched_in(event, cpuctx, ctx); | ||
703 | if (ret) | ||
704 | return ret; | ||
705 | event->tstamp_running += ctx->time - event->tstamp_stopped; | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | static void | ||
710 | group_commit_event_sched_in(struct perf_event *group_event, | ||
711 | struct perf_cpu_context *cpuctx, | ||
712 | struct perf_event_context *ctx) | ||
713 | { | ||
714 | struct perf_event *event; | ||
715 | u64 now = ctx->time; | ||
716 | |||
717 | group_event->tstamp_running += now - group_event->tstamp_stopped; | ||
718 | /* | ||
719 | * Schedule in siblings as one group (if any): | ||
720 | */ | ||
721 | list_for_each_entry(event, &group_event->sibling_list, group_entry) { | ||
722 | event->tstamp_running += now - event->tstamp_stopped; | ||
723 | } | ||
724 | } | ||
725 | |||
726 | static int | 689 | static int |
727 | group_sched_in(struct perf_event *group_event, | 690 | group_sched_in(struct perf_event *group_event, |
728 | struct perf_cpu_context *cpuctx, | 691 | struct perf_cpu_context *cpuctx, |
@@ -730,19 +693,15 @@ group_sched_in(struct perf_event *group_event, | |||
730 | { | 693 | { |
731 | struct perf_event *event, *partial_group = NULL; | 694 | struct perf_event *event, *partial_group = NULL; |
732 | struct pmu *pmu = group_event->pmu; | 695 | struct pmu *pmu = group_event->pmu; |
696 | u64 now = ctx->time; | ||
697 | bool simulate = false; | ||
733 | 698 | ||
734 | if (group_event->state == PERF_EVENT_STATE_OFF) | 699 | if (group_event->state == PERF_EVENT_STATE_OFF) |
735 | return 0; | 700 | return 0; |
736 | 701 | ||
737 | pmu->start_txn(pmu); | 702 | pmu->start_txn(pmu); |
738 | 703 | ||
739 | /* | 704 | if (event_sched_in(group_event, cpuctx, ctx)) { |
740 | * use __event_sched_in() to delay updating tstamp_running | ||
741 | * until the transaction is committed. In case of failure | ||
742 | * we will keep an unmodified tstamp_running which is a | ||
743 | * requirement to get correct timing information | ||
744 | */ | ||
745 | if (__event_sched_in(group_event, cpuctx, ctx)) { | ||
746 | pmu->cancel_txn(pmu); | 705 | pmu->cancel_txn(pmu); |
747 | return -EAGAIN; | 706 | return -EAGAIN; |
748 | } | 707 | } |
@@ -751,31 +710,42 @@ group_sched_in(struct perf_event *group_event, | |||
751 | * Schedule in siblings as one group (if any): | 710 | * Schedule in siblings as one group (if any): |
752 | */ | 711 | */ |
753 | list_for_each_entry(event, &group_event->sibling_list, group_entry) { | 712 | list_for_each_entry(event, &group_event->sibling_list, group_entry) { |
754 | if (__event_sched_in(event, cpuctx, ctx)) { | 713 | if (event_sched_in(event, cpuctx, ctx)) { |
755 | partial_group = event; | 714 | partial_group = event; |
756 | goto group_error; | 715 | goto group_error; |
757 | } | 716 | } |
758 | } | 717 | } |
759 | 718 | ||
760 | if (!pmu->commit_txn(pmu)) { | 719 | if (!pmu->commit_txn(pmu)) |
761 | /* commit tstamp_running */ | ||
762 | group_commit_event_sched_in(group_event, cpuctx, ctx); | ||
763 | return 0; | 720 | return 0; |
764 | } | 721 | |
765 | group_error: | 722 | group_error: |
766 | /* | 723 | /* |
767 | * Groups can be scheduled in as one unit only, so undo any | 724 | * Groups can be scheduled in as one unit only, so undo any |
768 | * partial group before returning: | 725 | * partial group before returning: |
726 | * The events up to the failed event are scheduled out normally, | ||
727 | * tstamp_stopped will be updated. | ||
769 | * | 728 | * |
770 | * use __event_sched_out() to avoid updating tstamp_stopped | 729 | * The failed events and the remaining siblings need to have |
771 | * because the event never actually ran | 730 | * their timings updated as if they had gone thru event_sched_in() |
731 | * and event_sched_out(). This is required to get consistent timings | ||
732 | * across the group. This also takes care of the case where the group | ||
733 | * could never be scheduled by ensuring tstamp_stopped is set to mark | ||
734 | * the time the event was actually stopped, such that time delta | ||
735 | * calculation in update_event_times() is correct. | ||
772 | */ | 736 | */ |
773 | list_for_each_entry(event, &group_event->sibling_list, group_entry) { | 737 | list_for_each_entry(event, &group_event->sibling_list, group_entry) { |
774 | if (event == partial_group) | 738 | if (event == partial_group) |
775 | break; | 739 | simulate = true; |
776 | __event_sched_out(event, cpuctx, ctx); | 740 | |
741 | if (simulate) { | ||
742 | event->tstamp_running += now - event->tstamp_stopped; | ||
743 | event->tstamp_stopped = now; | ||
744 | } else { | ||
745 | event_sched_out(event, cpuctx, ctx); | ||
746 | } | ||
777 | } | 747 | } |
778 | __event_sched_out(group_event, cpuctx, ctx); | 748 | event_sched_out(group_event, cpuctx, ctx); |
779 | 749 | ||
780 | pmu->cancel_txn(pmu); | 750 | pmu->cancel_txn(pmu); |
781 | 751 | ||
@@ -3428,7 +3398,8 @@ static u32 perf_event_tid(struct perf_event *event, struct task_struct *p) | |||
3428 | } | 3398 | } |
3429 | 3399 | ||
3430 | static void perf_output_read_one(struct perf_output_handle *handle, | 3400 | static void perf_output_read_one(struct perf_output_handle *handle, |
3431 | struct perf_event *event) | 3401 | struct perf_event *event, |
3402 | u64 enabled, u64 running) | ||
3432 | { | 3403 | { |
3433 | u64 read_format = event->attr.read_format; | 3404 | u64 read_format = event->attr.read_format; |
3434 | u64 values[4]; | 3405 | u64 values[4]; |
@@ -3436,11 +3407,11 @@ static void perf_output_read_one(struct perf_output_handle *handle, | |||
3436 | 3407 | ||
3437 | values[n++] = perf_event_count(event); | 3408 | values[n++] = perf_event_count(event); |
3438 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { | 3409 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { |
3439 | values[n++] = event->total_time_enabled + | 3410 | values[n++] = enabled + |
3440 | atomic64_read(&event->child_total_time_enabled); | 3411 | atomic64_read(&event->child_total_time_enabled); |
3441 | } | 3412 | } |
3442 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { | 3413 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { |
3443 | values[n++] = event->total_time_running + | 3414 | values[n++] = running + |
3444 | atomic64_read(&event->child_total_time_running); | 3415 | atomic64_read(&event->child_total_time_running); |
3445 | } | 3416 | } |
3446 | if (read_format & PERF_FORMAT_ID) | 3417 | if (read_format & PERF_FORMAT_ID) |
@@ -3453,7 +3424,8 @@ static void perf_output_read_one(struct perf_output_handle *handle, | |||
3453 | * XXX PERF_FORMAT_GROUP vs inherited events seems difficult. | 3424 | * XXX PERF_FORMAT_GROUP vs inherited events seems difficult. |
3454 | */ | 3425 | */ |
3455 | static void perf_output_read_group(struct perf_output_handle *handle, | 3426 | static void perf_output_read_group(struct perf_output_handle *handle, |
3456 | struct perf_event *event) | 3427 | struct perf_event *event, |
3428 | u64 enabled, u64 running) | ||
3457 | { | 3429 | { |
3458 | struct perf_event *leader = event->group_leader, *sub; | 3430 | struct perf_event *leader = event->group_leader, *sub; |
3459 | u64 read_format = event->attr.read_format; | 3431 | u64 read_format = event->attr.read_format; |
@@ -3463,10 +3435,10 @@ static void perf_output_read_group(struct perf_output_handle *handle, | |||
3463 | values[n++] = 1 + leader->nr_siblings; | 3435 | values[n++] = 1 + leader->nr_siblings; |
3464 | 3436 | ||
3465 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | 3437 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) |
3466 | values[n++] = leader->total_time_enabled; | 3438 | values[n++] = enabled; |
3467 | 3439 | ||
3468 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | 3440 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) |
3469 | values[n++] = leader->total_time_running; | 3441 | values[n++] = running; |
3470 | 3442 | ||
3471 | if (leader != event) | 3443 | if (leader != event) |
3472 | leader->pmu->read(leader); | 3444 | leader->pmu->read(leader); |
@@ -3491,13 +3463,35 @@ static void perf_output_read_group(struct perf_output_handle *handle, | |||
3491 | } | 3463 | } |
3492 | } | 3464 | } |
3493 | 3465 | ||
3466 | #define PERF_FORMAT_TOTAL_TIMES (PERF_FORMAT_TOTAL_TIME_ENABLED|\ | ||
3467 | PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
3468 | |||
3494 | static void perf_output_read(struct perf_output_handle *handle, | 3469 | static void perf_output_read(struct perf_output_handle *handle, |
3495 | struct perf_event *event) | 3470 | struct perf_event *event) |
3496 | { | 3471 | { |
3472 | u64 enabled = 0, running = 0, now, ctx_time; | ||
3473 | u64 read_format = event->attr.read_format; | ||
3474 | |||
3475 | /* | ||
3476 | * compute total_time_enabled, total_time_running | ||
3477 | * based on snapshot values taken when the event | ||
3478 | * was last scheduled in. | ||
3479 | * | ||
3480 | * we cannot simply called update_context_time() | ||
3481 | * because of locking issue as we are called in | ||
3482 | * NMI context | ||
3483 | */ | ||
3484 | if (read_format & PERF_FORMAT_TOTAL_TIMES) { | ||
3485 | now = perf_clock(); | ||
3486 | ctx_time = event->shadow_ctx_time + now; | ||
3487 | enabled = ctx_time - event->tstamp_enabled; | ||
3488 | running = ctx_time - event->tstamp_running; | ||
3489 | } | ||
3490 | |||
3497 | if (event->attr.read_format & PERF_FORMAT_GROUP) | 3491 | if (event->attr.read_format & PERF_FORMAT_GROUP) |
3498 | perf_output_read_group(handle, event); | 3492 | perf_output_read_group(handle, event, enabled, running); |
3499 | else | 3493 | else |
3500 | perf_output_read_one(handle, event); | 3494 | perf_output_read_one(handle, event, enabled, running); |
3501 | } | 3495 | } |
3502 | 3496 | ||
3503 | void perf_output_sample(struct perf_output_handle *handle, | 3497 | void perf_output_sample(struct perf_output_handle *handle, |