aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/cpu/perf_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
-rw-r--r--arch/x86/kernel/cpu/perf_event.c36
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 {
620struct perf_sched { 621struct 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 */
632static void perf_sched_init(struct perf_sched *sched, struct event_constraint **constraints, 634static 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 */
759int perf_assign_events(struct event_constraint **constraints, int n, 767int 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 /*