diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 83 |
1 files changed, 62 insertions, 21 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 87848ebe2bb7..4f7001f28936 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -190,6 +190,7 @@ static bool check_hw_exists(void) | |||
190 | u64 val, val_fail, val_new= ~0; | 190 | u64 val, val_fail, val_new= ~0; |
191 | int i, reg, reg_fail, ret = 0; | 191 | int i, reg, reg_fail, ret = 0; |
192 | int bios_fail = 0; | 192 | int bios_fail = 0; |
193 | int reg_safe = -1; | ||
193 | 194 | ||
194 | /* | 195 | /* |
195 | * Check to see if the BIOS enabled any of the counters, if so | 196 | * Check to see if the BIOS enabled any of the counters, if so |
@@ -204,6 +205,8 @@ static bool check_hw_exists(void) | |||
204 | bios_fail = 1; | 205 | bios_fail = 1; |
205 | val_fail = val; | 206 | val_fail = val; |
206 | reg_fail = reg; | 207 | reg_fail = reg; |
208 | } else { | ||
209 | reg_safe = i; | ||
207 | } | 210 | } |
208 | } | 211 | } |
209 | 212 | ||
@@ -222,11 +225,22 @@ static bool check_hw_exists(void) | |||
222 | } | 225 | } |
223 | 226 | ||
224 | /* | 227 | /* |
228 | * If all the counters are enabled, the below test will always | ||
229 | * fail. The tools will also become useless in this scenario. | ||
230 | * Just fail and disable the hardware counters. | ||
231 | */ | ||
232 | |||
233 | if (reg_safe == -1) { | ||
234 | reg = reg_safe; | ||
235 | goto msr_fail; | ||
236 | } | ||
237 | |||
238 | /* | ||
225 | * Read the current value, change it and read it back to see if it | 239 | * Read the current value, change it and read it back to see if it |
226 | * matches, this is needed to detect certain hardware emulators | 240 | * matches, this is needed to detect certain hardware emulators |
227 | * (qemu/kvm) that don't trap on the MSR access and always return 0s. | 241 | * (qemu/kvm) that don't trap on the MSR access and always return 0s. |
228 | */ | 242 | */ |
229 | reg = x86_pmu_event_addr(0); | 243 | reg = x86_pmu_event_addr(reg_safe); |
230 | if (rdmsrl_safe(reg, &val)) | 244 | if (rdmsrl_safe(reg, &val)) |
231 | goto msr_fail; | 245 | goto msr_fail; |
232 | val ^= 0xffffUL; | 246 | val ^= 0xffffUL; |
@@ -611,6 +625,7 @@ struct sched_state { | |||
611 | int event; /* event index */ | 625 | int event; /* event index */ |
612 | int counter; /* counter index */ | 626 | int counter; /* counter index */ |
613 | int unassigned; /* number of events to be assigned left */ | 627 | int unassigned; /* number of events to be assigned left */ |
628 | int nr_gp; /* number of GP counters used */ | ||
614 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 629 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
615 | }; | 630 | }; |
616 | 631 | ||
@@ -620,27 +635,29 @@ struct sched_state { | |||
620 | struct perf_sched { | 635 | struct perf_sched { |
621 | int max_weight; | 636 | int max_weight; |
622 | int max_events; | 637 | int max_events; |
623 | struct perf_event **events; | 638 | int max_gp; |
624 | struct sched_state state; | ||
625 | int saved_states; | 639 | int saved_states; |
640 | struct event_constraint **constraints; | ||
641 | struct sched_state state; | ||
626 | struct sched_state saved[SCHED_STATES_MAX]; | 642 | struct sched_state saved[SCHED_STATES_MAX]; |
627 | }; | 643 | }; |
628 | 644 | ||
629 | /* | 645 | /* |
630 | * Initialize interator that runs through all events and counters. | 646 | * Initialize interator that runs through all events and counters. |
631 | */ | 647 | */ |
632 | static void perf_sched_init(struct perf_sched *sched, struct perf_event **events, | 648 | static void perf_sched_init(struct perf_sched *sched, struct event_constraint **constraints, |
633 | int num, int wmin, int wmax) | 649 | int num, int wmin, int wmax, int gpmax) |
634 | { | 650 | { |
635 | int idx; | 651 | int idx; |
636 | 652 | ||
637 | memset(sched, 0, sizeof(*sched)); | 653 | memset(sched, 0, sizeof(*sched)); |
638 | sched->max_events = num; | 654 | sched->max_events = num; |
639 | sched->max_weight = wmax; | 655 | sched->max_weight = wmax; |
640 | sched->events = events; | 656 | sched->max_gp = gpmax; |
657 | sched->constraints = constraints; | ||
641 | 658 | ||
642 | for (idx = 0; idx < num; idx++) { | 659 | for (idx = 0; idx < num; idx++) { |
643 | if (events[idx]->hw.constraint->weight == wmin) | 660 | if (constraints[idx]->weight == wmin) |
644 | break; | 661 | break; |
645 | } | 662 | } |
646 | 663 | ||
@@ -687,7 +704,7 @@ static bool __perf_sched_find_counter(struct perf_sched *sched) | |||
687 | if (sched->state.event >= sched->max_events) | 704 | if (sched->state.event >= sched->max_events) |
688 | return false; | 705 | return false; |
689 | 706 | ||
690 | c = sched->events[sched->state.event]->hw.constraint; | 707 | c = sched->constraints[sched->state.event]; |
691 | /* Prefer fixed purpose counters */ | 708 | /* Prefer fixed purpose counters */ |
692 | if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) { | 709 | if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) { |
693 | idx = INTEL_PMC_IDX_FIXED; | 710 | idx = INTEL_PMC_IDX_FIXED; |
@@ -696,11 +713,16 @@ static bool __perf_sched_find_counter(struct perf_sched *sched) | |||
696 | goto done; | 713 | goto done; |
697 | } | 714 | } |
698 | } | 715 | } |
716 | |||
699 | /* Grab the first unused counter starting with idx */ | 717 | /* Grab the first unused counter starting with idx */ |
700 | idx = sched->state.counter; | 718 | idx = sched->state.counter; |
701 | for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) { | 719 | for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) { |
702 | if (!__test_and_set_bit(idx, sched->state.used)) | 720 | if (!__test_and_set_bit(idx, sched->state.used)) { |
721 | if (sched->state.nr_gp++ >= sched->max_gp) | ||
722 | return false; | ||
723 | |||
703 | goto done; | 724 | goto done; |
725 | } | ||
704 | } | 726 | } |
705 | 727 | ||
706 | return false; | 728 | return false; |
@@ -745,7 +767,7 @@ static bool perf_sched_next_event(struct perf_sched *sched) | |||
745 | if (sched->state.weight > sched->max_weight) | 767 | if (sched->state.weight > sched->max_weight) |
746 | return false; | 768 | return false; |
747 | } | 769 | } |
748 | c = sched->events[sched->state.event]->hw.constraint; | 770 | c = sched->constraints[sched->state.event]; |
749 | } while (c->weight != sched->state.weight); | 771 | } while (c->weight != sched->state.weight); |
750 | 772 | ||
751 | sched->state.counter = 0; /* start with first counter */ | 773 | sched->state.counter = 0; /* start with first counter */ |
@@ -756,12 +778,12 @@ static bool perf_sched_next_event(struct perf_sched *sched) | |||
756 | /* | 778 | /* |
757 | * Assign a counter for each event. | 779 | * Assign a counter for each event. |
758 | */ | 780 | */ |
759 | int perf_assign_events(struct perf_event **events, int n, | 781 | int perf_assign_events(struct event_constraint **constraints, int n, |
760 | int wmin, int wmax, int *assign) | 782 | int wmin, int wmax, int gpmax, int *assign) |
761 | { | 783 | { |
762 | struct perf_sched sched; | 784 | struct perf_sched sched; |
763 | 785 | ||
764 | perf_sched_init(&sched, events, n, wmin, wmax); | 786 | perf_sched_init(&sched, constraints, n, wmin, wmax, gpmax); |
765 | 787 | ||
766 | do { | 788 | do { |
767 | if (!perf_sched_find_counter(&sched)) | 789 | if (!perf_sched_find_counter(&sched)) |
@@ -788,9 +810,9 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | |||
788 | x86_pmu.start_scheduling(cpuc); | 810 | x86_pmu.start_scheduling(cpuc); |
789 | 811 | ||
790 | for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) { | 812 | for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) { |
791 | hwc = &cpuc->event_list[i]->hw; | 813 | cpuc->event_constraint[i] = NULL; |
792 | c = x86_pmu.get_event_constraints(cpuc, i, cpuc->event_list[i]); | 814 | c = x86_pmu.get_event_constraints(cpuc, i, cpuc->event_list[i]); |
793 | hwc->constraint = c; | 815 | cpuc->event_constraint[i] = c; |
794 | 816 | ||
795 | wmin = min(wmin, c->weight); | 817 | wmin = min(wmin, c->weight); |
796 | wmax = max(wmax, c->weight); | 818 | wmax = max(wmax, c->weight); |
@@ -801,7 +823,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | |||
801 | */ | 823 | */ |
802 | for (i = 0; i < n; i++) { | 824 | for (i = 0; i < n; i++) { |
803 | hwc = &cpuc->event_list[i]->hw; | 825 | hwc = &cpuc->event_list[i]->hw; |
804 | c = hwc->constraint; | 826 | c = cpuc->event_constraint[i]; |
805 | 827 | ||
806 | /* never assigned */ | 828 | /* never assigned */ |
807 | if (hwc->idx == -1) | 829 | if (hwc->idx == -1) |
@@ -821,9 +843,26 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | |||
821 | } | 843 | } |
822 | 844 | ||
823 | /* slow path */ | 845 | /* slow path */ |
824 | if (i != n) | 846 | if (i != n) { |
825 | unsched = perf_assign_events(cpuc->event_list, n, wmin, | 847 | int gpmax = x86_pmu.num_counters; |
826 | wmax, assign); | 848 | |
849 | /* | ||
850 | * Do not allow scheduling of more than half the available | ||
851 | * generic counters. | ||
852 | * | ||
853 | * This helps avoid counter starvation of sibling thread by | ||
854 | * ensuring at most half the counters cannot be in exclusive | ||
855 | * mode. There is no designated counters for the limits. Any | ||
856 | * N/2 counters can be used. This helps with events with | ||
857 | * specific counter constraints. | ||
858 | */ | ||
859 | if (is_ht_workaround_enabled() && !cpuc->is_fake && | ||
860 | READ_ONCE(cpuc->excl_cntrs->exclusive_present)) | ||
861 | gpmax /= 2; | ||
862 | |||
863 | unsched = perf_assign_events(cpuc->event_constraint, n, wmin, | ||
864 | wmax, gpmax, assign); | ||
865 | } | ||
827 | 866 | ||
828 | /* | 867 | /* |
829 | * In case of success (unsched = 0), mark events as committed, | 868 | * In case of success (unsched = 0), mark events as committed, |
@@ -840,7 +879,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | |||
840 | e = cpuc->event_list[i]; | 879 | e = cpuc->event_list[i]; |
841 | e->hw.flags |= PERF_X86_EVENT_COMMITTED; | 880 | e->hw.flags |= PERF_X86_EVENT_COMMITTED; |
842 | if (x86_pmu.commit_scheduling) | 881 | if (x86_pmu.commit_scheduling) |
843 | x86_pmu.commit_scheduling(cpuc, e, assign[i]); | 882 | x86_pmu.commit_scheduling(cpuc, i, assign[i]); |
844 | } | 883 | } |
845 | } | 884 | } |
846 | 885 | ||
@@ -1292,8 +1331,10 @@ static void x86_pmu_del(struct perf_event *event, int flags) | |||
1292 | x86_pmu.put_event_constraints(cpuc, event); | 1331 | x86_pmu.put_event_constraints(cpuc, event); |
1293 | 1332 | ||
1294 | /* Delete the array entry. */ | 1333 | /* Delete the array entry. */ |
1295 | while (++i < cpuc->n_events) | 1334 | while (++i < cpuc->n_events) { |
1296 | cpuc->event_list[i-1] = cpuc->event_list[i]; | 1335 | cpuc->event_list[i-1] = cpuc->event_list[i]; |
1336 | cpuc->event_constraint[i-1] = cpuc->event_constraint[i]; | ||
1337 | } | ||
1297 | --cpuc->n_events; | 1338 | --cpuc->n_events; |
1298 | 1339 | ||
1299 | perf_event_update_userpage(event); | 1340 | perf_event_update_userpage(event); |