diff options
| author | Peter Zijlstra <peterz@infradead.org> | 2015-05-21 04:57:17 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2015-05-27 03:16:03 -0400 |
| commit | cc1790cf541553263bda024295d7600c7cd7c45d (patch) | |
| tree | 9ddcfe2e7d2805060986c30aef6d0db8361603d9 /arch/x86/kernel/cpu | |
| parent | b371b594317869971af326adcf7cd65cabdb4087 (diff) | |
perf/x86: Improve HT workaround GP counter constraint
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.
Reported-by: Vince Weaver <vincent.weaver@maine.edu>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
[ Fixed build fail for the !CONFIG_CPU_SUP_INTEL case. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/cpu')
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 36 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event.h | 15 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel.c | 30 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_uncore.c | 2 |
4 files changed, 53 insertions, 30 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 | /* |
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index fdfaab7c5e55..ef78516850fb 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h | |||
| @@ -74,6 +74,7 @@ struct event_constraint { | |||
| 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 { | |||
| 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 { | |||
| 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 { | |||
| 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, | |||
| 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 | ||
