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 | |
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>
-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 | ||
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) | |||
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 */ |
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 7a58fb5df15c..a1e35c9f06b9 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c | |||
@@ -1923,7 +1923,6 @@ intel_start_scheduling(struct cpu_hw_events *cpuc) | |||
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, | |||
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, | |||
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, | |||
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) | |||
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) | |||
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 | ||
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index ec2ba578d286..dd319e59246b 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c | |||
@@ -395,7 +395,7 @@ static int uncore_assign_events(struct intel_uncore_box *box, int assign[], int | |||
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++) |