diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 69 |
1 files changed, 53 insertions, 16 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 1025f3c99d20..9e581c5cf6d0 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -403,7 +403,8 @@ int x86_pmu_hw_config(struct perf_event *event) | |||
403 | * check that PEBS LBR correction does not conflict with | 403 | * check that PEBS LBR correction does not conflict with |
404 | * whatever the user is asking with attr->branch_sample_type | 404 | * whatever the user is asking with attr->branch_sample_type |
405 | */ | 405 | */ |
406 | if (event->attr.precise_ip > 1) { | 406 | if (event->attr.precise_ip > 1 && |
407 | x86_pmu.intel_cap.pebs_format < 2) { | ||
407 | u64 *br_type = &event->attr.branch_sample_type; | 408 | u64 *br_type = &event->attr.branch_sample_type; |
408 | 409 | ||
409 | if (has_branch_stack(event)) { | 410 | if (has_branch_stack(event)) { |
@@ -568,7 +569,7 @@ struct sched_state { | |||
568 | struct perf_sched { | 569 | struct perf_sched { |
569 | int max_weight; | 570 | int max_weight; |
570 | int max_events; | 571 | int max_events; |
571 | struct event_constraint **constraints; | 572 | struct perf_event **events; |
572 | struct sched_state state; | 573 | struct sched_state state; |
573 | int saved_states; | 574 | int saved_states; |
574 | struct sched_state saved[SCHED_STATES_MAX]; | 575 | struct sched_state saved[SCHED_STATES_MAX]; |
@@ -577,7 +578,7 @@ struct perf_sched { | |||
577 | /* | 578 | /* |
578 | * Initialize interator that runs through all events and counters. | 579 | * Initialize interator that runs through all events and counters. |
579 | */ | 580 | */ |
580 | static void perf_sched_init(struct perf_sched *sched, struct event_constraint **c, | 581 | static void perf_sched_init(struct perf_sched *sched, struct perf_event **events, |
581 | int num, int wmin, int wmax) | 582 | int num, int wmin, int wmax) |
582 | { | 583 | { |
583 | int idx; | 584 | int idx; |
@@ -585,10 +586,10 @@ static void perf_sched_init(struct perf_sched *sched, struct event_constraint ** | |||
585 | memset(sched, 0, sizeof(*sched)); | 586 | memset(sched, 0, sizeof(*sched)); |
586 | sched->max_events = num; | 587 | sched->max_events = num; |
587 | sched->max_weight = wmax; | 588 | sched->max_weight = wmax; |
588 | sched->constraints = c; | 589 | sched->events = events; |
589 | 590 | ||
590 | for (idx = 0; idx < num; idx++) { | 591 | for (idx = 0; idx < num; idx++) { |
591 | if (c[idx]->weight == wmin) | 592 | if (events[idx]->hw.constraint->weight == wmin) |
592 | break; | 593 | break; |
593 | } | 594 | } |
594 | 595 | ||
@@ -635,8 +636,7 @@ static bool __perf_sched_find_counter(struct perf_sched *sched) | |||
635 | if (sched->state.event >= sched->max_events) | 636 | if (sched->state.event >= sched->max_events) |
636 | return false; | 637 | return false; |
637 | 638 | ||
638 | c = sched->constraints[sched->state.event]; | 639 | c = sched->events[sched->state.event]->hw.constraint; |
639 | |||
640 | /* Prefer fixed purpose counters */ | 640 | /* Prefer fixed purpose counters */ |
641 | if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) { | 641 | if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) { |
642 | idx = INTEL_PMC_IDX_FIXED; | 642 | idx = INTEL_PMC_IDX_FIXED; |
@@ -694,7 +694,7 @@ static bool perf_sched_next_event(struct perf_sched *sched) | |||
694 | if (sched->state.weight > sched->max_weight) | 694 | if (sched->state.weight > sched->max_weight) |
695 | return false; | 695 | return false; |
696 | } | 696 | } |
697 | c = sched->constraints[sched->state.event]; | 697 | c = sched->events[sched->state.event]->hw.constraint; |
698 | } while (c->weight != sched->state.weight); | 698 | } while (c->weight != sched->state.weight); |
699 | 699 | ||
700 | sched->state.counter = 0; /* start with first counter */ | 700 | sched->state.counter = 0; /* start with first counter */ |
@@ -705,12 +705,12 @@ static bool perf_sched_next_event(struct perf_sched *sched) | |||
705 | /* | 705 | /* |
706 | * Assign a counter for each event. | 706 | * Assign a counter for each event. |
707 | */ | 707 | */ |
708 | int perf_assign_events(struct event_constraint **constraints, int n, | 708 | int perf_assign_events(struct perf_event **events, int n, |
709 | int wmin, int wmax, int *assign) | 709 | int wmin, int wmax, int *assign) |
710 | { | 710 | { |
711 | struct perf_sched sched; | 711 | struct perf_sched sched; |
712 | 712 | ||
713 | perf_sched_init(&sched, constraints, n, wmin, wmax); | 713 | perf_sched_init(&sched, events, n, wmin, wmax); |
714 | 714 | ||
715 | do { | 715 | do { |
716 | if (!perf_sched_find_counter(&sched)) | 716 | if (!perf_sched_find_counter(&sched)) |
@@ -724,16 +724,19 @@ int perf_assign_events(struct event_constraint **constraints, int n, | |||
724 | 724 | ||
725 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | 725 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) |
726 | { | 726 | { |
727 | struct event_constraint *c, *constraints[X86_PMC_IDX_MAX]; | 727 | struct event_constraint *c; |
728 | unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 728 | unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
729 | struct perf_event *e; | ||
729 | int i, wmin, wmax, num = 0; | 730 | int i, wmin, wmax, num = 0; |
730 | struct hw_perf_event *hwc; | 731 | struct hw_perf_event *hwc; |
731 | 732 | ||
732 | bitmap_zero(used_mask, X86_PMC_IDX_MAX); | 733 | bitmap_zero(used_mask, X86_PMC_IDX_MAX); |
733 | 734 | ||
734 | for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) { | 735 | for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) { |
736 | hwc = &cpuc->event_list[i]->hw; | ||
735 | c = x86_pmu.get_event_constraints(cpuc, cpuc->event_list[i]); | 737 | c = x86_pmu.get_event_constraints(cpuc, cpuc->event_list[i]); |
736 | constraints[i] = c; | 738 | hwc->constraint = c; |
739 | |||
737 | wmin = min(wmin, c->weight); | 740 | wmin = min(wmin, c->weight); |
738 | wmax = max(wmax, c->weight); | 741 | wmax = max(wmax, c->weight); |
739 | } | 742 | } |
@@ -743,7 +746,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | |||
743 | */ | 746 | */ |
744 | for (i = 0; i < n; i++) { | 747 | for (i = 0; i < n; i++) { |
745 | hwc = &cpuc->event_list[i]->hw; | 748 | hwc = &cpuc->event_list[i]->hw; |
746 | c = constraints[i]; | 749 | c = hwc->constraint; |
747 | 750 | ||
748 | /* never assigned */ | 751 | /* never assigned */ |
749 | if (hwc->idx == -1) | 752 | if (hwc->idx == -1) |
@@ -764,16 +767,35 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | |||
764 | 767 | ||
765 | /* slow path */ | 768 | /* slow path */ |
766 | if (i != n) | 769 | if (i != n) |
767 | num = perf_assign_events(constraints, n, wmin, wmax, assign); | 770 | num = perf_assign_events(cpuc->event_list, n, wmin, |
771 | wmax, assign); | ||
768 | 772 | ||
769 | /* | 773 | /* |
774 | * Mark the event as committed, so we do not put_constraint() | ||
775 | * in case new events are added and fail scheduling. | ||
776 | */ | ||
777 | if (!num && assign) { | ||
778 | for (i = 0; i < n; i++) { | ||
779 | e = cpuc->event_list[i]; | ||
780 | e->hw.flags |= PERF_X86_EVENT_COMMITTED; | ||
781 | } | ||
782 | } | ||
783 | /* | ||
770 | * scheduling failed or is just a simulation, | 784 | * scheduling failed or is just a simulation, |
771 | * free resources if necessary | 785 | * free resources if necessary |
772 | */ | 786 | */ |
773 | if (!assign || num) { | 787 | if (!assign || num) { |
774 | for (i = 0; i < n; i++) { | 788 | for (i = 0; i < n; i++) { |
789 | e = cpuc->event_list[i]; | ||
790 | /* | ||
791 | * do not put_constraint() on comitted events, | ||
792 | * because they are good to go | ||
793 | */ | ||
794 | if ((e->hw.flags & PERF_X86_EVENT_COMMITTED)) | ||
795 | continue; | ||
796 | |||
775 | if (x86_pmu.put_event_constraints) | 797 | if (x86_pmu.put_event_constraints) |
776 | x86_pmu.put_event_constraints(cpuc, cpuc->event_list[i]); | 798 | x86_pmu.put_event_constraints(cpuc, e); |
777 | } | 799 | } |
778 | } | 800 | } |
779 | return num ? -EINVAL : 0; | 801 | return num ? -EINVAL : 0; |
@@ -1153,6 +1175,11 @@ static void x86_pmu_del(struct perf_event *event, int flags) | |||
1153 | int i; | 1175 | int i; |
1154 | 1176 | ||
1155 | /* | 1177 | /* |
1178 | * event is descheduled | ||
1179 | */ | ||
1180 | event->hw.flags &= ~PERF_X86_EVENT_COMMITTED; | ||
1181 | |||
1182 | /* | ||
1156 | * If we're called during a txn, we don't need to do anything. | 1183 | * If we're called during a txn, we don't need to do anything. |
1157 | * The events never got scheduled and ->cancel_txn will truncate | 1184 | * The events never got scheduled and ->cancel_txn will truncate |
1158 | * the event_list. | 1185 | * the event_list. |
@@ -1249,10 +1276,20 @@ void perf_events_lapic_init(void) | |||
1249 | static int __kprobes | 1276 | static int __kprobes |
1250 | perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs) | 1277 | perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs) |
1251 | { | 1278 | { |
1279 | int ret; | ||
1280 | u64 start_clock; | ||
1281 | u64 finish_clock; | ||
1282 | |||
1252 | if (!atomic_read(&active_events)) | 1283 | if (!atomic_read(&active_events)) |
1253 | return NMI_DONE; | 1284 | return NMI_DONE; |
1254 | 1285 | ||
1255 | return x86_pmu.handle_irq(regs); | 1286 | start_clock = local_clock(); |
1287 | ret = x86_pmu.handle_irq(regs); | ||
1288 | finish_clock = local_clock(); | ||
1289 | |||
1290 | perf_sample_event_took(finish_clock - start_clock); | ||
1291 | |||
1292 | return ret; | ||
1256 | } | 1293 | } |
1257 | 1294 | ||
1258 | struct event_constraint emptyconstraint; | 1295 | struct event_constraint emptyconstraint; |