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 | 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 { · 3 unmodified lines | |||
| 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 { · 1 unmodified line | |||
| 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) · 53 unmodified lines | |||
| 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) · 50 unmodified lines | |||
| 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) · 54 unmodified lines | |||
| 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 | /* |
| @@ -74,6 +74,7 @@ struct event_constraint { · 73 unmodified lines | |||
| 74 | #define PERF_X86_EVENT_EXCL 0x0040 /* HT exclusivity on counter */ | 74 | #define PERF_X86_EVENT_EXCL 0x0040 /* HT exclusivity on counter */ |
| 75 | #define PERF_X86_EVENT_DYNAMIC 0x0080 /* dynamic alloc'd constraint */ | 75 | #define PERF_X86_EVENT_DYNAMIC 0x0080 /* dynamic alloc'd constraint */ |
| 76 | #define PERF_X86_EVENT_RDPMC_ALLOWED 0x0100 /* grant rdpmc permission */ | 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 | struct amd_nb { | 80 | struct amd_nb { |
| @@ -134,8 +135,6 @@ enum intel_excl_state_type { · 54 unmodified lines | |||
| 134 | struct intel_excl_states { | 135 | struct intel_excl_states { |
| 135 | enum intel_excl_state_type init_state[X86_PMC_IDX_MAX]; | 136 | enum intel_excl_state_type init_state[X86_PMC_IDX_MAX]; |
| 136 | enum intel_excl_state_type state[X86_PMC_IDX_MAX]; | 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 | bool sched_started; /* true if scheduling has started */ | 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 | struct intel_excl_states states[2]; | 144 | struct intel_excl_states states[2]; |
| 146 | 145 | ||
| 146 | union { | ||
| 147 | u16 has_exclusive[2]; | ||
| 148 | u32 exclusive_present; | ||
| 149 | }; | ||
| 150 | |||
| 147 | int refcnt; /* per-core: #HT threads */ | 151 | int refcnt; /* per-core: #HT threads */ |
| 148 | unsigned core_id; /* per-core: core id */ | 152 | unsigned core_id; /* per-core: core id */ |
| 149 | }; | 153 | }; |
| @@ -176,6 +180,7 @@ struct cpu_hw_events { · 26 unmodified lines | |||
| 176 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ | 180 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ |
| 177 | struct event_constraint *event_constraint[X86_PMC_IDX_MAX]; | 181 | struct event_constraint *event_constraint[X86_PMC_IDX_MAX]; |
| 178 | 182 | ||
| 183 | int n_excl; /* the number of exclusive events */ | ||
| 179 | 184 | ||
| 180 | unsigned int group_flag; | 185 | unsigned int group_flag; |
| 181 | int is_fake; | 186 | int is_fake; |
| @@ -719,7 +724,7 @@ static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc, · 537 unmodified lines | |||
| 719 | void x86_pmu_enable_all(int added); | 724 | void x86_pmu_enable_all(int added); |
| 720 | 725 | ||
| 721 | int perf_assign_events(struct event_constraint **constraints, int n, | 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 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign); | 728 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign); |
| 724 | 729 | ||
| 725 | void x86_pmu_stop(struct perf_event *event, int flags); | 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 | return NULL; | 935 | return NULL; |
| 931 | } | 936 | } |
| 932 | 937 | ||
| 938 | static inline int is_ht_workaround_enabled(void) | ||
| 939 | { | ||
| 940 | return 0; | ||
| 941 | } | ||
| 933 | #endif /* CONFIG_CPU_SUP_INTEL */ | 942 | #endif /* CONFIG_CPU_SUP_INTEL */ |
| @@ -1923,7 +1923,6 @@ intel_start_scheduling(struct cpu_hw_events *cpuc) · 1922 unmodified lines | |||
| 1923 | xl = &excl_cntrs->states[tid]; | 1923 | xl = &excl_cntrs->states[tid]; |
| 1924 | 1924 | ||
| 1925 | xl->sched_started = true; | 1925 | xl->sched_started = true; |
| 1926 | xl->num_alloc_cntrs = 0; | ||
| 1927 | /* | 1926 | /* |
| 1928 | * lock shared state until we are done scheduling | 1927 | * lock shared state until we are done scheduling |
| 1929 | * in stop_event_scheduling() | 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 | * across HT threads | 1999 | * across HT threads |
| 2001 | */ | 2000 | */ |
| 2002 | is_excl = c->flags & PERF_X86_EVENT_EXCL; | 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 | * xl = state of current HT | 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 | xl = &excl_cntrs->states[tid]; | 2012 | xl = &excl_cntrs->states[tid]; |
| 2009 | xlo = &excl_cntrs->states[o_tid]; | 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 | cx = c; | 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 | xl = &excl_cntrs->states[tid]; | 2143 | xl = &excl_cntrs->states[tid]; |
| 2152 | xlo = &excl_cntrs->states[o_tid]; | 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 | * put_constraint may be called from x86_schedule_events() | 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 | cpuc->lbr_sel = &cpuc->shared_regs->regs[EXTRA_REG_LBR]; | 2629 | cpuc->lbr_sel = &cpuc->shared_regs->regs[EXTRA_REG_LBR]; |
| 2633 | 2630 | ||
| 2634 | if (x86_pmu.flags & PMU_FL_EXCL_CNTRS) { | 2631 | if (x86_pmu.flags & PMU_FL_EXCL_CNTRS) { |
| 2635 | int h = x86_pmu.num_counters >> 1; | ||
| 2636 | |||
| 2637 | for_each_cpu(i, topology_thread_cpumask(cpu)) { | 2632 | for_each_cpu(i, topology_thread_cpumask(cpu)) { |
| 2638 | struct intel_excl_cntrs *c; | 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 | cpuc->excl_cntrs->core_id = core_id; | 2643 | cpuc->excl_cntrs->core_id = core_id; |
| 2649 | cpuc->excl_cntrs->refcnt++; | 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 | /* slow path */ | 395 | /* slow path */ |
| 396 | if (i != n) | 396 | if (i != n) |
| 397 | ret = perf_assign_events(box->event_constraint, n, | 397 | ret = perf_assign_events(box->event_constraint, n, |
| 398 | wmin, wmax, assign); | 398 | wmin, wmax, n, assign); |
| 399 | 399 | ||
| 400 | if (!assign || ret) { | 400 | if (!assign || ret) { |
| 401 | for (i = 0; i < n; i++) | 401 | for (i = 0; i < n; i++) |