diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2012-06-08 08:50:50 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-07-05 15:55:57 -0400 |
commit | c93dc84cbe32435be3ffa2fbde355eff94955c32 (patch) | |
tree | b650cfcea486aa0dd1612f187156c7b84fb97661 /arch | |
parent | f285f92f7e4c9af20149130c8fd5027131b39b0e (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.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 21 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel.c | 51 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_core.c | 10 |
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 | ||
233 | extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); | 233 | extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); |
234 | extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap); | 234 | extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap); |
235 | extern void perf_check_microcode(void); | ||
235 | #else | 236 | #else |
236 | static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr) | 237 | static 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 | ||
247 | static inline void perf_events_lapic_init(void) { } | 248 | static inline void perf_events_lapic_init(void) { } |
249 | static 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 | ||
1653 | void perf_check_microcode(void) | ||
1654 | { | ||
1655 | if (x86_pmu.check_microcode) | ||
1656 | x86_pmu.check_microcode(); | ||
1657 | } | ||
1658 | EXPORT_SYMBOL_GPL(perf_check_microcode); | ||
1659 | |||
1653 | static struct pmu pmu = { | 1660 | static 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 | ||
1715 | static 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 | |||
1734 | static 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 | |||
1715 | static __init void intel_sandybridge_quirk(void) | 1761 | static __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 | ||
1722 | static const struct { int id; char *name; } intel_arch_events_map[] __initconst = { | 1767 | static 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 | ||
91 | MODULE_DESCRIPTION("Microcode Update Driver"); | 92 | MODULE_DESCRIPTION("Microcode Update Driver"); |
92 | MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); | 93 | MODULE_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(µcode_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(µcode_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(µcode_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(µcode_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(µcode_mutex); | 560 | mutex_lock(µcode_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(µcode_mutex); | 565 | mutex_unlock(µcode_mutex); |
562 | put_online_cpus(); | 566 | put_online_cpus(); |
563 | 567 | ||