diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 36 |
1 files changed, 30 insertions, 6 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 1664eeea65e0..2eca19422454 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -611,6 +611,7 @@ struct sched_state { | |||
611 | int event; /* event index */ | 611 | int event; /* event index */ |
612 | int counter; /* counter index */ | 612 | int counter; /* counter index */ |
613 | int unassigned; /* number of events to be assigned left */ | 613 | int unassigned; /* number of events to be assigned left */ |
614 | int nr_gp; /* number of GP counters used */ | ||
614 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 615 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
615 | }; | 616 | }; |
616 | 617 | ||
@@ -620,9 +621,10 @@ struct sched_state { | |||
620 | struct perf_sched { | 621 | struct perf_sched { |
621 | int max_weight; | 622 | int max_weight; |
622 | int max_events; | 623 | int max_events; |
624 | int max_gp; | ||
625 | int saved_states; | ||
623 | struct event_constraint **constraints; | 626 | struct event_constraint **constraints; |
624 | struct sched_state state; | 627 | struct sched_state state; |
625 | int saved_states; | ||
626 | struct sched_state saved[SCHED_STATES_MAX]; | 628 | struct sched_state saved[SCHED_STATES_MAX]; |
627 | }; | 629 | }; |
628 | 630 | ||
@@ -630,13 +632,14 @@ struct perf_sched { | |||
630 | * Initialize interator that runs through all events and counters. | 632 | * Initialize interator that runs through all events and counters. |
631 | */ | 633 | */ |
632 | static void perf_sched_init(struct perf_sched *sched, struct event_constraint **constraints, | 634 | static void perf_sched_init(struct perf_sched *sched, struct event_constraint **constraints, |
633 | int num, int wmin, int wmax) | 635 | int num, int wmin, int wmax, int gpmax) |
634 | { | 636 | { |
635 | int idx; | 637 | int idx; |
636 | 638 | ||
637 | memset(sched, 0, sizeof(*sched)); | 639 | memset(sched, 0, sizeof(*sched)); |
638 | sched->max_events = num; | 640 | sched->max_events = num; |
639 | sched->max_weight = wmax; | 641 | sched->max_weight = wmax; |
642 | sched->max_gp = gpmax; | ||
640 | sched->constraints = constraints; | 643 | sched->constraints = constraints; |
641 | 644 | ||
642 | for (idx = 0; idx < num; idx++) { | 645 | for (idx = 0; idx < num; idx++) { |
@@ -696,11 +699,16 @@ static bool __perf_sched_find_counter(struct perf_sched *sched) | |||
696 | goto done; | 699 | goto done; |
697 | } | 700 | } |
698 | } | 701 | } |
702 | |||
699 | /* Grab the first unused counter starting with idx */ | 703 | /* Grab the first unused counter starting with idx */ |
700 | idx = sched->state.counter; | 704 | idx = sched->state.counter; |
701 | for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) { | 705 | for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) { |
702 | if (!__test_and_set_bit(idx, sched->state.used)) | 706 | if (!__test_and_set_bit(idx, sched->state.used)) { |
707 | if (sched->state.nr_gp++ >= sched->max_gp) | ||
708 | return false; | ||
709 | |||
703 | goto done; | 710 | goto done; |
711 | } | ||
704 | } | 712 | } |
705 | 713 | ||
706 | return false; | 714 | return false; |
@@ -757,11 +765,11 @@ static bool perf_sched_next_event(struct perf_sched *sched) | |||
757 | * Assign a counter for each event. | 765 | * Assign a counter for each event. |
758 | */ | 766 | */ |
759 | int perf_assign_events(struct event_constraint **constraints, int n, | 767 | int perf_assign_events(struct event_constraint **constraints, int n, |
760 | int wmin, int wmax, int *assign) | 768 | int wmin, int wmax, int gpmax, int *assign) |
761 | { | 769 | { |
762 | struct perf_sched sched; | 770 | struct perf_sched sched; |
763 | 771 | ||
764 | perf_sched_init(&sched, constraints, n, wmin, wmax); | 772 | perf_sched_init(&sched, constraints, n, wmin, wmax, gpmax); |
765 | 773 | ||
766 | do { | 774 | do { |
767 | if (!perf_sched_find_counter(&sched)) | 775 | if (!perf_sched_find_counter(&sched)) |
@@ -822,8 +830,24 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | |||
822 | 830 | ||
823 | /* slow path */ | 831 | /* slow path */ |
824 | if (i != n) { | 832 | if (i != n) { |
833 | int gpmax = x86_pmu.num_counters; | ||
834 | |||
835 | /* | ||
836 | * Do not allow scheduling of more than half the available | ||
837 | * generic counters. | ||
838 | * | ||
839 | * This helps avoid counter starvation of sibling thread by | ||
840 | * ensuring at most half the counters cannot be in exclusive | ||
841 | * mode. There is no designated counters for the limits. Any | ||
842 | * N/2 counters can be used. This helps with events with | ||
843 | * specific counter constraints. | ||
844 | */ | ||
845 | if (is_ht_workaround_enabled() && !cpuc->is_fake && | ||
846 | READ_ONCE(cpuc->excl_cntrs->exclusive_present)) | ||
847 | gpmax /= 2; | ||
848 | |||
825 | unsched = perf_assign_events(cpuc->event_constraint, n, wmin, | 849 | unsched = perf_assign_events(cpuc->event_constraint, n, wmin, |
826 | wmax, assign); | 850 | wmax, gpmax, assign); |
827 | } | 851 | } |
828 | 852 | ||
829 | /* | 853 | /* |