The (SNB/IVB/HSW) HT bug only affects events that can be programmed onto GP counters, therefore we should only limit the number of GP counters that can be used per cpu -- iow we should not constrain the FP counters. Furthermore, we should only enfore such a limit when there are in fact exclusive events being scheduled on either sibling.
Unified · Split
| @@ -611,6 +611,7 @@ struct sched_state { · 610 unmodified lines | ||
| 611 | 611 | int event; /* event index */ |
| 612 | 612 | int counter; /* counter index */ |
| 613 | 613 | int unassigned; /* number of events to be assigned left */ |
| 614 | + int nr_gp; /* number of GP counters used */ | |
| 614 | 615 | unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
| 615 | 616 | }; |
| 616 | 617 | |
| @@ -620,9 +621,10 @@ struct sched_state { · 3 unmodified lines | ||
| 620 | 621 | struct perf_sched { |
| 621 | 622 | int max_weight; |
| 622 | 623 | int max_events; |
| 624 | + int max_gp; | |
| 625 | + int saved_states; | |
| 623 | 626 | struct event_constraint **constraints; |
| 624 | 627 | struct sched_state state; |
| 625 | − int saved_states; | |
| 626 | 628 | struct sched_state saved[SCHED_STATES_MAX]; |
| 627 | 629 | }; |
| 628 | 630 | |
| @@ -630,13 +632,14 @@ struct perf_sched { · 1 unmodified line | ||
| 630 | 632 | * Initialize interator that runs through all events and counters. |
| 631 | 633 | */ |
| 632 | 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 | 637 | int idx; |
| 636 | 638 | |
| 637 | 639 | memset(sched, 0, sizeof(*sched)); |
| 638 | 640 | sched->max_events = num; |
| 639 | 641 | sched->max_weight = wmax; |
| 642 | + sched->max_gp = gpmax; | |
| 640 | 643 | sched->constraints = constraints; |
| 641 | 644 | |
| 642 | 645 | for (idx = 0; idx < num; idx++) { |
| @@ -696,11 +699,16 @@ static bool __perf_sched_find_counter(struct perf_sched *sched) · 53 unmodified lines | ||
| 696 | 699 | goto done; |
| 697 | 700 | } |
| 698 | 701 | } |
| 702 | + | |
| 699 | 703 | /* Grab the first unused counter starting with idx */ |
| 700 | 704 | idx = sched->state.counter; |
| 701 | 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 | 710 | goto done; |
| 711 | + } | |
| 704 | 712 | } |
| 705 | 713 | |
| 706 | 714 | return false; |
| @@ -757,11 +765,11 @@ static bool perf_sched_next_event(struct perf_sched *sched) · 50 unmodified lines | ||
| 757 | 765 | * Assign a counter for each event. |
| 758 | 766 | */ |
| 759 | 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 | 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 | 774 | do { |
| 767 | 775 | if (!perf_sched_find_counter(&sched)) |
| @@ -822,8 +830,24 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) · 54 unmodified lines | ||
| 822 | 830 | |
| 823 | 831 | /* slow path */ |
| 824 | 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 | 849 | unsched = perf_assign_events(cpuc->event_constraint, n, wmin, |
| 826 | − wmax, assign); | |
| 850 | + wmax, gpmax, assign); | |
| 827 | 851 | } |
| 828 | 852 | |
| 829 | 853 | /* |
| @@ -74,6 +74,7 @@ struct event_constraint { · 73 unmodified lines | ||
| 74 | 74 | #define PERF_X86_EVENT_EXCL 0x0040 /* HT exclusivity on counter */ |
| 75 | 75 | #define PERF_X86_EVENT_DYNAMIC 0x0080 /* dynamic alloc'd constraint */ |
| 76 | 76 | #define PERF_X86_EVENT_RDPMC_ALLOWED 0x0100 /* grant rdpmc permission */ |
| 77 | +#define PERF_X86_EVENT_EXCL_ACCT 0x0200 /* accounted EXCL event */ | |
| 77 | 78 | |
| 78 | 79 | |
| 79 | 80 | struct amd_nb { |
| @@ -134,8 +135,6 @@ enum intel_excl_state_type { · 54 unmodified lines | ||
| 134 | 135 | struct intel_excl_states { |
| 135 | 136 | enum intel_excl_state_type init_state[X86_PMC_IDX_MAX]; |
| 136 | 137 | enum intel_excl_state_type state[X86_PMC_IDX_MAX]; |
| 137 | − int num_alloc_cntrs;/* #counters allocated */ | |
| 138 | − int max_alloc_cntrs;/* max #counters allowed */ | |
| 139 | 138 | bool sched_started; /* true if scheduling has started */ |
| 140 | 139 | }; |
| 141 | 140 | |
| @@ -144,6 +143,11 @@ struct intel_excl_cntrs { · 2 unmodified lines | ||
| 144 | 143 | |
| 145 | 144 | struct intel_excl_states states[2]; |
| 146 | 145 | |
| 146 | + union { | |
| 147 | + u16 has_exclusive[2]; | |
| 148 | + u32 exclusive_present; | |
| 149 | + }; | |
| 150 | + | |
| 147 | 151 | int refcnt; /* per-core: #HT threads */ |
| 148 | 152 | unsigned core_id; /* per-core: core id */ |
| 149 | 153 | }; |
| @@ -176,6 +180,7 @@ struct cpu_hw_events { · 26 unmodified lines | ||
| 176 | 180 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ |
| 177 | 181 | struct event_constraint *event_constraint[X86_PMC_IDX_MAX]; |
| 178 | 182 | |
| 183 | + int n_excl; /* the number of exclusive events */ | |
| 179 | 184 | |
| 180 | 185 | unsigned int group_flag; |
| 181 | 186 | int is_fake; |
| @@ -719,7 +724,7 @@ static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, · 537 unmodified lines | ||
| 719 | 724 | void x86_pmu_enable_all(int added); |
| 720 | 725 | |
| 721 | 726 | int perf_assign_events(struct event_constraint **constraints, int n, |
| 722 | − int wmin, int wmax, int *assign); | |
| 727 | + int wmin, int wmax, int gpmax, int *assign); | |
| 723 | 728 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign); |
| 724 | 729 | |
| 725 | 730 | void x86_pmu_stop(struct perf_event *event, int flags); |
| @@ -930,4 +935,8 @@ static inline struct intel_shared_regs *allocate_shared_regs(int cpu) · 204 unmodified lines | ||
| 930 | 935 | return NULL; |
| 931 | 936 | } |
| 932 | 937 | |
| 938 | +static inline int is_ht_workaround_enabled(void) | |
| 939 | +{ | |
| 940 | + return 0; | |
| 941 | +} | |
| 933 | 942 | #endif /* CONFIG_CPU_SUP_INTEL */ |
| @@ -1923,7 +1923,6 @@ intel_start_scheduling(struct cpu_hw_events *cpuc) · 1922 unmodified lines | ||
| 1923 | 1923 | xl = &excl_cntrs->states[tid]; |
| 1924 | 1924 | |
| 1925 | 1925 | xl->sched_started = true; |
| 1926 | − xl->num_alloc_cntrs = 0; | |
| 1927 | 1926 | /* |
| 1928 | 1927 | * lock shared state until we are done scheduling |
| 1929 | 1928 | * in stop_event_scheduling() |
| @@ -2000,6 +1999,11 @@ intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event, · 70 unmodified lines | ||
| 2000 | 1999 | * across HT threads |
| 2001 | 2000 | */ |
| 2002 | 2001 | is_excl = c->flags & PERF_X86_EVENT_EXCL; |
| 2002 | + if (is_excl && !(event->hw.flags & PERF_X86_EVENT_EXCL_ACCT)) { | |
| 2003 | + event->hw.flags |= PERF_X86_EVENT_EXCL_ACCT; | |
| 2004 | + if (!cpuc->n_excl++) | |
| 2005 | + WRITE_ONCE(excl_cntrs->has_exclusive[tid], 1); | |
| 2006 | + } | |
| 2003 | 2007 | |
| 2004 | 2008 | /* |
| 2005 | 2009 | * xl = state of current HT |
| @@ -2008,18 +2012,6 @@ intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event, · 2 unmodified lines | ||
| 2008 | 2012 | xl = &excl_cntrs->states[tid]; |
| 2009 | 2013 | xlo = &excl_cntrs->states[o_tid]; |
| 2010 | 2014 | |
| 2011 | − /* | |
| 2012 | − * do not allow scheduling of more than max_alloc_cntrs | |
| 2013 | − * which is set to half the available generic counters. | |
| 2014 | − * this helps avoid counter starvation of sibling thread | |
| 2015 | − * by ensuring at most half the counters cannot be in | |
| 2016 | − * exclusive mode. There is not designated counters for the | |
| 2017 | − * limits. Any N/2 counters can be used. This helps with | |
| 2018 | − * events with specifix counter constraints | |
| 2019 | − */ | |
| 2020 | − if (xl->num_alloc_cntrs++ == xl->max_alloc_cntrs) | |
| 2021 | − return &emptyconstraint; | |
| 2022 | − | |
| 2023 | 2015 | cx = c; |
| 2024 | 2016 | |
| 2025 | 2017 | /* |
| @@ -2150,6 +2142,11 @@ static void intel_put_excl_constraints(struct cpu_hw_events *cpuc, · 124 unmodified lines | ||
| 2150 | 2142 | |
| 2151 | 2143 | xl = &excl_cntrs->states[tid]; |
| 2152 | 2144 | xlo = &excl_cntrs->states[o_tid]; |
| 2145 | + if (hwc->flags & PERF_X86_EVENT_EXCL_ACCT) { | |
| 2146 | + hwc->flags &= ~PERF_X86_EVENT_EXCL_ACCT; | |
| 2147 | + if (!--cpuc->n_excl) | |
| 2148 | + WRITE_ONCE(excl_cntrs->has_exclusive[tid], 0); | |
| 2149 | + } | |
| 2153 | 2150 | |
| 2154 | 2151 | /* |
| 2155 | 2152 | * put_constraint may be called from x86_schedule_events() |
| @@ -2632,8 +2629,6 @@ static void intel_pmu_cpu_starting(int cpu) · 476 unmodified lines | ||
| 2632 | 2629 | cpuc->lbr_sel = &cpuc->shared_regs->regs[EXTRA_REG_LBR]; |
| 2633 | 2630 | |
| 2634 | 2631 | if (x86_pmu.flags & PMU_FL_EXCL_CNTRS) { |
| 2635 | − int h = x86_pmu.num_counters >> 1; | |
| 2636 | − | |
| 2637 | 2632 | for_each_cpu(i, topology_thread_cpumask(cpu)) { |
| 2638 | 2633 | struct intel_excl_cntrs *c; |
| 2639 | 2634 | |
| @@ -2647,11 +2642,6 @@ static void intel_pmu_cpu_starting(int cpu) · 7 unmodified lines | ||
| 2647 | 2642 | } |
| 2648 | 2643 | cpuc->excl_cntrs->core_id = core_id; |
| 2649 | 2644 | cpuc->excl_cntrs->refcnt++; |
| 2650 | − /* | |
| 2651 | − * set hard limit to half the number of generic counters | |
| 2652 | − */ | |
| 2653 | − cpuc->excl_cntrs->states[0].max_alloc_cntrs = h; | |
| 2654 | − cpuc->excl_cntrs->states[1].max_alloc_cntrs = h; | |
| 2655 | 2645 | } |
| 2656 | 2646 | } |
| 2657 | 2647 | |
| @@ -395,7 +395,7 @@ static int uncore_assign_events(struct intel_uncore_box *box, int assign[], int · 394 unmodified lines | ||
| 395 | 395 | /* slow path */ |
| 396 | 396 | if (i != n) |
| 397 | 397 | ret = perf_assign_events(box->event_constraint, n, |
| 398 | − wmin, wmax, assign); | |
| 398 | + wmin, wmax, n, assign); | |
| 399 | 399 | |
| 400 | 400 | if (!assign || ret) { |
| 401 | 401 | for (i = 0; i < n; i++) |