From: Peter Zijlstra <peterz@infradead.org>
To: Ingo Molnar <mingo@kernel.org>
Cc: Stephane Eranian <eranian@google.com>,
linux-kernel@vger.kernel.org, andi@firstfloor.org, mingo@elte.hu,
ming.m.lin@intel.com,
Andreas Herrmann <andreas.herrmann3@amd.com>,
Borislav Petkov <borislav.petkov@amd.com>,
Dimitri Sivanich <sivanich@sgi.com>,
Dmitry Adamushko <dmitry.adamushko@gmail.com>
Subject: Re: [PATCH] perf/x86: check ucode before disabling PEBS on SandyBridge
Date: Fri, 08 Jun 2012 15:26:12 +0200 [thread overview]
Message-ID: <1339161972.2507.13.camel@laptop> (raw)
In-Reply-To: <1339149613.23343.52.camel@twins>
On Fri, 2012-06-08 at 12:00 +0200, Peter Zijlstra wrote:
> > > Or we could put a hook in the ucode loader.
> >
> > I'd really suggest the latter - I doubt this will be our only
> > ucode dependent quirk, going forward ...
>
> Yeah, am in the middle of writing that..
OK so the micro-code stuff is a complete trainwreck.
The very worst is that it does per-cpu micro-code updates, not machine
wide. This results in it being able to have different revisions on
different cpus. This in turn makes the below O(n^2) :/
The biggest problem is finding when the minimum revision changes, at
best this is a n log n sorting problem due to the per-cpu setup, but I
couldn't be arsed to implement a tree or anything fancy since it all
stinks anyway.
Why do we have per-cpu firmware anyway?
Anyway, I have the below in two patches, but I thought to first post the
entire mess since if anybody has a better idea I'm all ears.
It does work though..
---
arch/x86/include/asm/microcode.h | 12 ++++++
arch/x86/kernel/cpu/perf_event.c | 14 +++----
arch/x86/kernel/cpu/perf_event.h | 2 +-
arch/x86/kernel/cpu/perf_event_intel.c | 63 ++++++++++++++++++++++++++++++--
arch/x86/kernel/microcode_amd.c | 1 +
arch/x86/kernel/microcode_core.c | 53 +++++++++++++++++++++++----
arch/x86/kernel/microcode_intel.c | 1 +
arch/x86/kernel/setup.c | 5 +++
8 files changed, 132 insertions(+), 19 deletions(-)
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h
index 4ebe157..1cd2dc5 100644
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -32,6 +32,7 @@ struct microcode_ops {
struct ucode_cpu_info {
struct cpu_signature cpu_sig;
+ int new_rev;
int valid;
void *mc;
};
@@ -63,4 +64,15 @@ static inline struct microcode_ops * __init init_amd_microcode(void)
static inline void __exit exit_amd_microcode(void) {}
#endif
+extern struct blocking_notifier_head microcode_notifier;
+
+#define MICROCODE_CAN_UPDATE 0x01
+#define MICROCODE_UPDATED 0x02
+
+#define microcode_notifier(fn) \
+do { \
+ static struct notifier_block fn##_nb ={ .notifier_call = fn }; \
+ blocking_notifier_chain_register(µcode_notifier, &fn##_nb);\
+} while (0)
+
#endif /* _ASM_X86_MICROCODE_H */
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 000a474..d3ef86c1 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -377,7 +377,7 @@ int x86_pmu_hw_config(struct perf_event *event)
int precise = 0;
/* Support for constant skid */
- if (x86_pmu.pebs_active) {
+ if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) {
precise++;
/* Support for IP fixup */
@@ -1677,9 +1677,9 @@ static struct pmu pmu = {
.pmu_enable = x86_pmu_enable,
.pmu_disable = x86_pmu_disable,
- .attr_groups = x86_pmu_attr_groups,
+ .attr_groups = x86_pmu_attr_groups,
- .event_init = x86_pmu_event_init,
+ .event_init = x86_pmu_event_init,
.add = x86_pmu_add,
.del = x86_pmu_del,
@@ -1687,11 +1687,11 @@ static struct pmu pmu = {
.stop = x86_pmu_stop,
.read = x86_pmu_read,
- .start_txn = x86_pmu_start_txn,
- .cancel_txn = x86_pmu_cancel_txn,
- .commit_txn = x86_pmu_commit_txn,
+ .start_txn = x86_pmu_start_txn,
+ .cancel_txn = x86_pmu_cancel_txn,
+ .commit_txn = x86_pmu_commit_txn,
- .event_idx = x86_pmu_event_idx,
+ .event_idx = x86_pmu_event_idx,
.flush_branch_stack = x86_pmu_flush_branch_stack,
};
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index 3df3de9..a5ecfe8 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -373,7 +373,7 @@ struct x86_pmu {
* Intel DebugStore bits
*/
int bts, pebs;
- int bts_active, pebs_active;
+ int bts_active, pebs_active, pebs_broken;
int pebs_record_size;
void (*drain_pebs)(struct pt_regs *regs);
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 5ec146c..bde86cf 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -15,6 +15,7 @@
#include <asm/hardirq.h>
#include <asm/apic.h>
+#include <asm/microcode.h>
#include "perf_event.h"
@@ -1714,11 +1715,67 @@ static __init void intel_clovertown_quirk(void)
x86_pmu.pebs_constraints = NULL;
}
+static const u32 snb_ucode_rev = 0x28;
+
+static void intel_snb_verify_ucode(void)
+{
+ u32 rev = UINT_MAX;
+ int pebs_broken = 0;
+ int cpu;
+
+ get_online_cpus();
+ /*
+ * Because the microcode loader is bloody stupid and allows different
+ * revisions per cpu and does strictly per-cpu loading, we now have to
+ * check all cpus to determine the minimally installed revision.
+ *
+ * This makes updating the microcode O(n^2) in the number of CPUs :/
+ */
+ for_each_online_cpu(cpu)
+ rev = min(cpu_data(cpu).microcode, rev);
+ put_online_cpus();
+
+ pebs_broken = (rev < snb_ucode_rev);
+
+ if (pebs_broken == x86_pmu.pebs_broken)
+ return;
+
+ /*
+ * Serialized by the microcode lock..
+ */
+ if (x86_pmu.pebs_broken) {
+ pr_info("PEBS enabled due to micro-code update\n");
+ x86_pmu.pebs_broken = 0;
+ } else {
+ pr_info("PEBS disabled due to CPU errata, "
+ "please upgrade micro-code to at least %x (current: %x)\n",
+ snb_ucode_rev, rev);
+ x86_pmu.pebs_broken = 1;
+ }
+}
+
+static int intel_snb_ucode_notifier(struct notifier_block *self,
+ unsigned long action, void *_uci)
+{
+ /*
+ * Since ucode cannot be down-graded, and no future ucode revision
+ * is known to break PEBS again, we're ok with MICROCODE_CAN_UPDATE.
+ */
+
+ if (action == MICROCODE_UPDATED)
+ intel_snb_verify_ucode();
+
+ return NOTIFY_DONE;
+}
+
static __init void intel_sandybridge_quirk(void)
{
- pr_warn("PEBS disabled due to CPU errata\n");
- x86_pmu.pebs = 0;
- x86_pmu.pebs_constraints = NULL;
+ intel_snb_verify_ucode();
+ /*
+ * we're still single threaded, so while there's a hole here,
+ * you can't trigger it.
+ */
+ microcode_notifier(intel_snb_ucode_notifier);
}
static const struct { int id; char *name; } intel_arch_events_map[] __initconst = {
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index 8a2ce8f..265831e 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -294,6 +294,7 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
}
out_ok:
+ uci->new_rev = new_rev;
uci->mc = new_mc;
state = UCODE_OK;
pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n",
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
index fbdfc69..a6cad81 100644
--- a/arch/x86/kernel/microcode_core.c
+++ b/arch/x86/kernel/microcode_core.c
@@ -167,15 +167,43 @@ static void apply_microcode_local(void *arg)
ctx->err = microcode_ops->apply_microcode(smp_processor_id());
}
+/*
+ * Call the notifier chain before we update to verify we can indeed
+ * update to the desired revision.
+ */
+
+static int microcode_notifier_check(struct ucode_cpu_info *uci)
+{
+ int ret = blocking_notifier_call_chain(µcode_notifier,
+ MICROCODE_CAN_UPDATE, uci);
+ return notifier_to_errno(ret);
+}
+
+/*
+ * Call the notifier chain after we've updated to
+ */
+
+static void microcode_notifier_done(void)
+{
+ blocking_notifier_call_chain(µcode_notifier, MICROCODE_UPDATED, NULL);
+}
+
static int apply_microcode_on_target(int cpu)
{
struct apply_microcode_ctx ctx = { .err = 0 };
int ret;
+ ret = microcode_notifier_check(ucode_cpu_info + cpu);
+ if (ret)
+ return ret;
+
ret = smp_call_function_single(cpu, apply_microcode_local, &ctx, 1);
if (!ret)
ret = ctx.err;
+ if (!ret)
+ microcode_notifier_done();
+
return ret;
}
@@ -196,8 +224,11 @@ static int do_microcode_update(const void __user *buf, size_t size)
if (ustate == UCODE_ERROR) {
error = -1;
break;
- } else if (ustate == UCODE_OK)
- apply_microcode_on_target(cpu);
+ } else if (ustate == UCODE_OK) {
+ error = apply_microcode_on_target(cpu);
+ if (error)
+ break;
+ }
}
return error;
@@ -282,11 +313,12 @@ static int reload_for_cpu(int cpu)
enum ucode_state ustate;
ustate = microcode_ops->request_microcode_fw(cpu, µcode_pdev->dev);
- if (ustate == UCODE_OK)
- apply_microcode_on_target(cpu);
- else
+ if (ustate == UCODE_OK) {
+ err = apply_microcode_on_target(cpu);
+ } else {
if (ustate == UCODE_ERROR)
err = -EINVAL;
+ }
}
mutex_unlock(µcode_mutex);
@@ -361,14 +393,15 @@ static void microcode_fini_cpu(int cpu)
static enum ucode_state microcode_resume_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ int err;
if (!uci->mc)
return UCODE_NFOUND;
pr_debug("CPU%d updated upon resume\n", cpu);
- apply_microcode_on_target(cpu);
+ err = apply_microcode_on_target(cpu);
- return UCODE_OK;
+ return !err ? UCODE_OK : UCODE_ERROR;
}
static enum ucode_state microcode_init_cpu(int cpu)
@@ -385,8 +418,12 @@ static enum ucode_state microcode_init_cpu(int cpu)
ustate = microcode_ops->request_microcode_fw(cpu, µcode_pdev->dev);
if (ustate == UCODE_OK) {
+ int err;
+
pr_debug("CPU%d updated upon init\n", cpu);
- apply_microcode_on_target(cpu);
+ err = apply_microcode_on_target(cpu);
+ if (err)
+ ustate = UCODE_ERROR;
}
return ustate;
diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c
index 0327e2b..6b87bd5 100644
--- a/arch/x86/kernel/microcode_intel.c
+++ b/arch/x86/kernel/microcode_intel.c
@@ -391,6 +391,7 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,
}
vfree(uci->mc);
+ uci->new_rev = new_rev;
uci->mc = (struct microcode_intel *)new_mc;
pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index f4b9b80..98b5b5c 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -68,6 +68,7 @@
#include <linux/percpu.h>
#include <linux/crash_dump.h>
#include <linux/tboot.h>
+#include <linux/notifier.h>
#include <video/edid.h>
@@ -205,6 +206,10 @@ struct cpuinfo_x86 boot_cpu_data __read_mostly = {
EXPORT_SYMBOL(boot_cpu_data);
#endif
+/*
+ * Lives here because the microcode stuff is modular.
+ */
+struct atomic_notifier_head microcode_notifier;
#if !defined(CONFIG_X86_PAE) || defined(CONFIG_X86_64)
unsigned long mmu_cr4_features;
next prev parent reply other threads:[~2012-06-08 13:26 UTC|newest]
Thread overview: 107+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-06-07 7:15 [PATCH] perf/x86: check ucode before disabling PEBS on SandyBridge Stephane Eranian
2012-06-07 10:18 ` Peter Zijlstra
2012-06-07 10:35 ` Stephane Eranian
2012-06-07 10:45 ` Peter Zijlstra
2012-06-07 10:48 ` Stephane Eranian
2012-06-07 11:15 ` Peter Zijlstra
2012-06-08 9:35 ` Ingo Molnar
2012-06-08 10:00 ` Peter Zijlstra
2012-06-08 10:03 ` Stephane Eranian
2012-06-08 13:26 ` Peter Zijlstra [this message]
2012-06-08 13:51 ` Borislav Petkov
2012-06-08 13:54 ` Peter Zijlstra
2012-06-08 14:15 ` Borislav Petkov
2012-06-08 14:20 ` Peter Zijlstra
2012-06-08 14:36 ` Borislav Petkov
2012-06-08 14:45 ` Peter Zijlstra
2012-06-08 15:09 ` Borislav Petkov
2012-06-08 13:56 ` Peter Zijlstra
2012-06-08 14:03 ` Borislav Petkov
2012-06-08 13:59 ` Peter Zijlstra
2012-06-08 14:03 ` Stephane Eranian
2012-06-08 14:17 ` Borislav Petkov
2012-06-08 16:02 ` H. Peter Anvin
2012-06-08 21:02 ` Henrique de Moraes Holschuh
2012-06-08 21:16 ` Borislav Petkov
2012-06-08 14:07 ` Stephane Eranian
2012-06-08 14:13 ` Peter Zijlstra
2012-06-08 14:19 ` Borislav Petkov
2012-06-08 14:23 ` Peter Zijlstra
2012-06-08 14:26 ` Stephane Eranian
2012-06-08 14:25 ` Peter Zijlstra
2012-06-08 14:26 ` Stephane Eranian
2012-06-12 8:14 ` Stephane Eranian
2012-06-08 16:28 ` Stephane Eranian
2012-06-08 18:49 ` Peter Zijlstra
2012-06-08 16:50 ` Andi Kleen
2012-06-08 18:05 ` Borislav Petkov
2012-06-08 18:52 ` Peter Zijlstra
2012-06-08 20:38 ` Borislav Petkov
2012-06-12 17:07 ` Robert Richter
2012-06-12 17:09 ` Stephane Eranian
2012-06-12 17:13 ` Peter Zijlstra
2012-06-12 17:17 ` Borislav Petkov
2012-06-12 17:18 ` Peter Zijlstra
2012-06-12 17:23 ` Borislav Petkov
2012-06-12 17:26 ` Peter Zijlstra
2012-06-12 17:35 ` Borislav Petkov
2012-06-12 17:40 ` Peter Zijlstra
2012-06-12 18:04 ` Andi Kleen
2012-06-12 20:10 ` Borislav Petkov
2012-06-13 1:04 ` Henrique de Moraes Holschuh
2012-06-13 6:51 ` Borislav Petkov
2012-06-13 12:36 ` Henrique de Moraes Holschuh
2012-06-13 16:11 ` Borislav Petkov
2012-06-13 21:41 ` Henrique de Moraes Holschuh
2012-06-15 12:37 ` Borislav Petkov
2012-06-15 12:42 ` Peter Zijlstra
2012-06-15 12:52 ` Borislav Petkov
2012-06-15 16:52 ` [PATCH] x86, microcode: Make reload interface per system Borislav Petkov
2012-06-15 17:16 ` Peter Zijlstra
2012-06-15 17:49 ` Borislav Petkov
2012-06-19 2:46 ` Henrique de Moraes Holschuh
2012-06-19 3:31 ` H. Peter Anvin
2012-06-19 5:11 ` Borislav Petkov
2012-06-19 8:17 ` Peter Zijlstra
2012-06-19 10:22 ` Borislav Petkov
2012-06-19 10:26 ` Peter Zijlstra
2012-06-19 15:06 ` Borislav Petkov
2012-06-19 15:57 ` H. Peter Anvin
2012-06-19 18:22 ` Henrique de Moraes Holschuh
2012-06-19 20:22 ` H. Peter Anvin
2012-06-20 23:46 ` Henrique de Moraes Holschuh
2012-06-20 23:49 ` H. Peter Anvin
2012-06-21 0:06 ` Henrique de Moraes Holschuh
2012-06-21 0:18 ` H. Peter Anvin
2012-06-21 2:33 ` Henrique de Moraes Holschuh
2012-06-19 5:03 ` Borislav Petkov
2012-06-19 18:57 ` Henrique de Moraes Holschuh
2012-06-19 22:19 ` Borislav Petkov
2012-06-19 2:42 ` [PATCH] perf/x86: check ucode before disabling PEBS on SandyBridge Henrique de Moraes Holschuh
2012-06-12 17:23 ` Robert Richter
2012-06-12 17:15 ` Borislav Petkov
2012-06-07 13:28 ` Andi Kleen
2012-06-07 13:27 ` Andi Kleen
2012-06-12 18:35 ` H. Peter Anvin
2012-06-12 19:07 ` Borislav Petkov
2012-06-12 19:33 ` Peter Zijlstra
2012-06-12 19:35 ` Andi Kleen
2012-06-12 19:49 ` Borislav Petkov
2012-06-12 20:28 ` H. Peter Anvin
2012-06-12 20:37 ` Borislav Petkov
2012-06-12 20:42 ` H. Peter Anvin
2012-06-12 20:56 ` Borislav Petkov
2012-06-12 20:58 ` H. Peter Anvin
2012-06-12 21:04 ` Borislav Petkov
2012-06-13 8:38 ` Peter Zijlstra
2012-06-13 14:00 ` H. Peter Anvin
2012-06-13 15:37 ` Peter Zijlstra
2012-06-13 15:46 ` H. Peter Anvin
2012-06-13 12:39 ` Peter Zijlstra
2012-06-13 13:22 ` H. Peter Anvin
2012-06-13 14:01 ` Henrique de Moraes Holschuh
2012-06-13 8:32 ` Peter Zijlstra
2012-06-13 13:59 ` H. Peter Anvin
2012-06-13 15:32 ` Peter Zijlstra
2012-06-13 15:38 ` H. Peter Anvin
2012-06-12 20:27 ` H. Peter Anvin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1339161972.2507.13.camel@laptop \
--to=peterz@infradead.org \
--cc=andi@firstfloor.org \
--cc=andreas.herrmann3@amd.com \
--cc=borislav.petkov@amd.com \
--cc=dmitry.adamushko@gmail.com \
--cc=eranian@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=ming.m.lin@intel.com \
--cc=mingo@elte.hu \
--cc=mingo@kernel.org \
--cc=sivanich@sgi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.