aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2012-06-08 08:50:50 -0400
committerIngo Molnar <mingo@kernel.org>2012-07-05 15:55:57 -0400
commitc93dc84cbe32435be3ffa2fbde355eff94955c32 (patch)
treeb650cfcea486aa0dd1612f187156c7b84fb97661 /arch
parentf285f92f7e4c9af20149130c8fd5027131b39b0e (diff)
perf/x86: Add a microcode revision check for SNB-PEBS
Recent Intel microcode resolved the SNB-PEBS issues, so conditionally enable PEBS on SNB hardware depending on the microcode revision. Thanks to Stephane for figuring out the various microcode revisions. Suggested-by: Stephane Eranian <eranian@google.com> Acked-by: Borislav Petkov <borislav.petkov@amd.com> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/n/tip-v3672ziwh9damwqwh1uz3krm@git.kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/include/asm/perf_event.h2
-rw-r--r--arch/x86/kernel/cpu/perf_event.c21
-rw-r--r--arch/x86/kernel/cpu/perf_event.h4
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel.c51
-rw-r--r--arch/x86/kernel/microcode_core.c10
5 files changed, 74 insertions, 14 deletions
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index ffdf5e0991c..c78f14a0df0 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -232,6 +232,7 @@ struct perf_guest_switch_msr {
232 232
233extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); 233extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr);
234extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap); 234extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap);
235extern void perf_check_microcode(void);
235#else 236#else
236static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr) 237static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr)
237{ 238{
@@ -245,6 +246,7 @@ static inline void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
245} 246}
246 247
247static inline void perf_events_lapic_init(void) { } 248static inline void perf_events_lapic_init(void) { }
249static inline void perf_check_microcode(void) { }
248#endif 250#endif
249 251
250#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) 252#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 3eb88ebcec5..29557aa06dd 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -379,7 +379,7 @@ int x86_pmu_hw_config(struct perf_event *event)
379 int precise = 0; 379 int precise = 0;
380 380
381 /* Support for constant skid */ 381 /* Support for constant skid */
382 if (x86_pmu.pebs_active) { 382 if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) {
383 precise++; 383 precise++;
384 384
385 /* Support for IP fixup */ 385 /* Support for IP fixup */
@@ -1650,13 +1650,20 @@ static void x86_pmu_flush_branch_stack(void)
1650 x86_pmu.flush_branch_stack(); 1650 x86_pmu.flush_branch_stack();
1651} 1651}
1652 1652
1653void perf_check_microcode(void)
1654{
1655 if (x86_pmu.check_microcode)
1656 x86_pmu.check_microcode();
1657}
1658EXPORT_SYMBOL_GPL(perf_check_microcode);
1659
1653static struct pmu pmu = { 1660static struct pmu pmu = {
1654 .pmu_enable = x86_pmu_enable, 1661 .pmu_enable = x86_pmu_enable,
1655 .pmu_disable = x86_pmu_disable, 1662 .pmu_disable = x86_pmu_disable,
1656 1663
1657 .attr_groups = x86_pmu_attr_groups, 1664 .attr_groups = x86_pmu_attr_groups,
1658 1665
1659 .event_init = x86_pmu_event_init, 1666 .event_init = x86_pmu_event_init,
1660 1667
1661 .add = x86_pmu_add, 1668 .add = x86_pmu_add,
1662 .del = x86_pmu_del, 1669 .del = x86_pmu_del,
@@ -1664,11 +1671,11 @@ static struct pmu pmu = {
1664 .stop = x86_pmu_stop, 1671 .stop = x86_pmu_stop,
1665 .read = x86_pmu_read, 1672 .read = x86_pmu_read,
1666 1673
1667 .start_txn = x86_pmu_start_txn, 1674 .start_txn = x86_pmu_start_txn,
1668 .cancel_txn = x86_pmu_cancel_txn, 1675 .cancel_txn = x86_pmu_cancel_txn,
1669 .commit_txn = x86_pmu_commit_txn, 1676 .commit_txn = x86_pmu_commit_txn,
1670 1677
1671 .event_idx = x86_pmu_event_idx, 1678 .event_idx = x86_pmu_event_idx,
1672 .flush_branch_stack = x86_pmu_flush_branch_stack, 1679 .flush_branch_stack = x86_pmu_flush_branch_stack,
1673}; 1680};
1674 1681
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index 83238f2a12b..3f5c6690435 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -361,6 +361,8 @@ struct x86_pmu {
361 void (*cpu_starting)(int cpu); 361 void (*cpu_starting)(int cpu);
362 void (*cpu_dying)(int cpu); 362 void (*cpu_dying)(int cpu);
363 void (*cpu_dead)(int cpu); 363 void (*cpu_dead)(int cpu);
364
365 void (*check_microcode)(void);
364 void (*flush_branch_stack)(void); 366 void (*flush_branch_stack)(void);
365 367
366 /* 368 /*
@@ -373,7 +375,7 @@ struct x86_pmu {
373 * Intel DebugStore bits 375 * Intel DebugStore bits
374 */ 376 */
375 int bts, pebs; 377 int bts, pebs;
376 int bts_active, pebs_active; 378 int bts_active, pebs_active, pebs_broken;
377 int pebs_record_size; 379 int pebs_record_size;
378 void (*drain_pebs)(struct pt_regs *regs); 380 void (*drain_pebs)(struct pt_regs *regs);
379 struct event_constraint *pebs_constraints; 381 struct event_constraint *pebs_constraints;
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index 2e9444c8014..5fdedb4bc3f 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -1712,11 +1712,56 @@ static __init void intel_clovertown_quirk(void)
1712 x86_pmu.pebs_constraints = NULL; 1712 x86_pmu.pebs_constraints = NULL;
1713} 1713}
1714 1714
1715static int intel_snb_pebs_broken(int cpu)
1716{
1717 u32 rev = UINT_MAX; /* default to broken for unknown models */
1718
1719 switch (cpu_data(cpu).x86_model) {
1720 case 42: /* SNB */
1721 rev = 0x28;
1722 break;
1723
1724 case 45: /* SNB-EP */
1725 switch (cpu_data(cpu).x86_mask) {
1726 case 6: rev = 0x618; break;
1727 case 7: rev = 0x70c; break;
1728 }
1729 }
1730
1731 return (cpu_data(cpu).microcode < rev);
1732}
1733
1734static void intel_snb_check_microcode(void)
1735{
1736 int pebs_broken = 0;
1737 int cpu;
1738
1739 get_online_cpus();
1740 for_each_online_cpu(cpu) {
1741 if ((pebs_broken = intel_snb_pebs_broken(cpu)))
1742 break;
1743 }
1744 put_online_cpus();
1745
1746 if (pebs_broken == x86_pmu.pebs_broken)
1747 return;
1748
1749 /*
1750 * Serialized by the microcode lock..
1751 */
1752 if (x86_pmu.pebs_broken) {
1753 pr_info("PEBS enabled due to microcode update\n");
1754 x86_pmu.pebs_broken = 0;
1755 } else {
1756 pr_info("PEBS disabled due to CPU errata, please upgrade microcode\n");
1757 x86_pmu.pebs_broken = 1;
1758 }
1759}
1760
1715static __init void intel_sandybridge_quirk(void) 1761static __init void intel_sandybridge_quirk(void)
1716{ 1762{
1717 printk(KERN_WARNING "PEBS disabled due to CPU errata.\n"); 1763 x86_pmu.check_microcode = intel_snb_check_microcode;
1718 x86_pmu.pebs = 0; 1764 intel_snb_check_microcode();
1719 x86_pmu.pebs_constraints = NULL;
1720} 1765}
1721 1766
1722static const struct { int id; char *name; } intel_arch_events_map[] __initconst = { 1767static const struct { int id; char *name; } intel_arch_events_map[] __initconst = {
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
index 947e4c64b1d..1649cf899ad 100644
--- a/arch/x86/kernel/microcode_core.c
+++ b/arch/x86/kernel/microcode_core.c
@@ -87,6 +87,7 @@
87#include <asm/microcode.h> 87#include <asm/microcode.h>
88#include <asm/processor.h> 88#include <asm/processor.h>
89#include <asm/cpu_device_id.h> 89#include <asm/cpu_device_id.h>
90#include <asm/perf_event.h>
90 91
91MODULE_DESCRIPTION("Microcode Update Driver"); 92MODULE_DESCRIPTION("Microcode Update Driver");
92MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); 93MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
@@ -277,7 +278,6 @@ static int reload_for_cpu(int cpu)
277 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 278 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
278 int err = 0; 279 int err = 0;
279 280
280 mutex_lock(&microcode_mutex);
281 if (uci->valid) { 281 if (uci->valid) {
282 enum ucode_state ustate; 282 enum ucode_state ustate;
283 283
@@ -288,7 +288,6 @@ static int reload_for_cpu(int cpu)
288 if (ustate == UCODE_ERROR) 288 if (ustate == UCODE_ERROR)
289 err = -EINVAL; 289 err = -EINVAL;
290 } 290 }
291 mutex_unlock(&microcode_mutex);
292 291
293 return err; 292 return err;
294} 293}
@@ -309,6 +308,7 @@ static ssize_t reload_store(struct device *dev,
309 return size; 308 return size;
310 309
311 get_online_cpus(); 310 get_online_cpus();
311 mutex_lock(&microcode_mutex);
312 for_each_online_cpu(cpu) { 312 for_each_online_cpu(cpu) {
313 tmp_ret = reload_for_cpu(cpu); 313 tmp_ret = reload_for_cpu(cpu);
314 if (tmp_ret != 0) 314 if (tmp_ret != 0)
@@ -318,6 +318,9 @@ static ssize_t reload_store(struct device *dev,
318 if (!ret) 318 if (!ret)
319 ret = tmp_ret; 319 ret = tmp_ret;
320 } 320 }
321 if (!ret)
322 perf_check_microcode();
323 mutex_unlock(&microcode_mutex);
321 put_online_cpus(); 324 put_online_cpus();
322 325
323 if (!ret) 326 if (!ret)
@@ -557,7 +560,8 @@ static int __init microcode_init(void)
557 mutex_lock(&microcode_mutex); 560 mutex_lock(&microcode_mutex);
558 561
559 error = subsys_interface_register(&mc_cpu_interface); 562 error = subsys_interface_register(&mc_cpu_interface);
560 563 if (!error)
564 perf_check_microcode();
561 mutex_unlock(&microcode_mutex); 565 mutex_unlock(&microcode_mutex);
562 put_online_cpus(); 566 put_online_cpus();
563 567