mirror of
https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git
synced 2024-09-21 09:57:26 +00:00
214 lines
5.7 KiB
Diff
214 lines
5.7 KiB
Diff
From a5321aec6412b20b5ad15db2d6b916c05349dbff Mon Sep 17 00:00:00 2001
|
|
From: Ashok Raj <ashok.raj@intel.com>
|
|
Date: Wed, 28 Feb 2018 11:28:46 +0100
|
|
Subject: x86/microcode: Synchronize late microcode loading
|
|
|
|
Original idea by Ashok, completely rewritten by Borislav.
|
|
|
|
Before you read any further: the early loading method is still the
|
|
preferred one and you should always do that. The following patch is
|
|
improving the late loading mechanism for long running jobs and cloud use
|
|
cases.
|
|
|
|
Gather all cores and serialize the microcode update on them by doing it
|
|
one-by-one to make the late update process as reliable as possible and
|
|
avoid potential issues caused by the microcode update.
|
|
|
|
[ Borislav: Rewrite completely. ]
|
|
|
|
Co-developed-by: Borislav Petkov <bp@suse.de>
|
|
Signed-off-by: Ashok Raj <ashok.raj@intel.com>
|
|
Signed-off-by: Borislav Petkov <bp@suse.de>
|
|
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
|
Tested-by: Tom Lendacky <thomas.lendacky@amd.com>
|
|
Tested-by: Ashok Raj <ashok.raj@intel.com>
|
|
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
|
|
Cc: Arjan Van De Ven <arjan.van.de.ven@intel.com>
|
|
Link: https://lkml.kernel.org/r/20180228102846.13447-8-bp@alien8.de
|
|
---
|
|
arch/x86/kernel/cpu/microcode/core.c | 118 +++++++++++++++++++++++++++--------
|
|
1 file changed, 92 insertions(+), 26 deletions(-)
|
|
|
|
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
|
|
index 5dd157d..70ecbc8 100644
|
|
--- a/arch/x86/kernel/cpu/microcode/core.c
|
|
+++ b/arch/x86/kernel/cpu/microcode/core.c
|
|
@@ -22,13 +22,16 @@
|
|
#define pr_fmt(fmt) "microcode: " fmt
|
|
|
|
#include <linux/platform_device.h>
|
|
+#include <linux/stop_machine.h>
|
|
#include <linux/syscore_ops.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/kernel.h>
|
|
+#include <linux/delay.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/cpu.h>
|
|
+#include <linux/nmi.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
|
|
@@ -64,6 +67,11 @@ LIST_HEAD(microcode_cache);
|
|
*/
|
|
static DEFINE_MUTEX(microcode_mutex);
|
|
|
|
+/*
|
|
+ * Serialize late loading so that CPUs get updated one-by-one.
|
|
+ */
|
|
+static DEFINE_SPINLOCK(update_lock);
|
|
+
|
|
struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
|
|
|
|
struct cpu_info_ctx {
|
|
@@ -486,6 +494,19 @@ static void __exit microcode_dev_exit(void)
|
|
/* fake device for request_firmware */
|
|
static struct platform_device *microcode_pdev;
|
|
|
|
+/*
|
|
+ * Late loading dance. Why the heavy-handed stomp_machine effort?
|
|
+ *
|
|
+ * - HT siblings must be idle and not execute other code while the other sibling
|
|
+ * is loading microcode in order to avoid any negative interactions caused by
|
|
+ * the loading.
|
|
+ *
|
|
+ * - In addition, microcode update on the cores must be serialized until this
|
|
+ * requirement can be relaxed in the future. Right now, this is conservative
|
|
+ * and good.
|
|
+ */
|
|
+#define SPINUNIT 100 /* 100 nsec */
|
|
+
|
|
static int check_online_cpus(void)
|
|
{
|
|
if (num_online_cpus() == num_present_cpus())
|
|
@@ -496,23 +517,85 @@ static int check_online_cpus(void)
|
|
return -EINVAL;
|
|
}
|
|
|
|
-static enum ucode_state reload_for_cpu(int cpu)
|
|
+static atomic_t late_cpus;
|
|
+
|
|
+/*
|
|
+ * Returns:
|
|
+ * < 0 - on error
|
|
+ * 0 - no update done
|
|
+ * 1 - microcode was updated
|
|
+ */
|
|
+static int __reload_late(void *info)
|
|
{
|
|
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
|
+ unsigned int timeout = NSEC_PER_SEC;
|
|
+ int all_cpus = num_online_cpus();
|
|
+ int cpu = smp_processor_id();
|
|
+ enum ucode_state err;
|
|
+ int ret = 0;
|
|
|
|
- if (!uci->valid)
|
|
- return UCODE_OK;
|
|
+ atomic_dec(&late_cpus);
|
|
+
|
|
+ /*
|
|
+ * Wait for all CPUs to arrive. A load will not be attempted unless all
|
|
+ * CPUs show up.
|
|
+ * */
|
|
+ while (atomic_read(&late_cpus)) {
|
|
+ if (timeout < SPINUNIT) {
|
|
+ pr_err("Timeout while waiting for CPUs rendezvous, remaining: %d\n",
|
|
+ atomic_read(&late_cpus));
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ ndelay(SPINUNIT);
|
|
+ timeout -= SPINUNIT;
|
|
+
|
|
+ touch_nmi_watchdog();
|
|
+ }
|
|
+
|
|
+ spin_lock(&update_lock);
|
|
+ apply_microcode_local(&err);
|
|
+ spin_unlock(&update_lock);
|
|
+
|
|
+ if (err > UCODE_NFOUND) {
|
|
+ pr_warn("Error reloading microcode on CPU %d\n", cpu);
|
|
+ ret = -1;
|
|
+ } else if (err == UCODE_UPDATED) {
|
|
+ ret = 1;
|
|
+ }
|
|
|
|
- return apply_microcode_on_target(cpu);
|
|
+ atomic_inc(&late_cpus);
|
|
+
|
|
+ while (atomic_read(&late_cpus) != all_cpus)
|
|
+ cpu_relax();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Reload microcode late on all CPUs. Wait for a sec until they
|
|
+ * all gather together.
|
|
+ */
|
|
+static int microcode_reload_late(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ atomic_set(&late_cpus, num_online_cpus());
|
|
+
|
|
+ ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ else if (ret > 0)
|
|
+ microcode_check();
|
|
+
|
|
+ return ret;
|
|
}
|
|
|
|
static ssize_t reload_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
- int cpu, bsp = boot_cpu_data.cpu_index;
|
|
enum ucode_state tmp_ret = UCODE_OK;
|
|
- bool do_callback = false;
|
|
+ int bsp = boot_cpu_data.cpu_index;
|
|
unsigned long val;
|
|
ssize_t ret = 0;
|
|
|
|
@@ -534,30 +617,13 @@ static ssize_t reload_store(struct device *dev,
|
|
goto put;
|
|
|
|
mutex_lock(µcode_mutex);
|
|
-
|
|
- for_each_online_cpu(cpu) {
|
|
- tmp_ret = reload_for_cpu(cpu);
|
|
- if (tmp_ret > UCODE_NFOUND) {
|
|
- pr_warn("Error reloading microcode on CPU %d\n", cpu);
|
|
-
|
|
- /* set retval for the first encountered reload error */
|
|
- if (!ret)
|
|
- ret = -EINVAL;
|
|
- }
|
|
-
|
|
- if (tmp_ret == UCODE_UPDATED)
|
|
- do_callback = true;
|
|
- }
|
|
-
|
|
- if (!ret && do_callback)
|
|
- microcode_check();
|
|
-
|
|
+ ret = microcode_reload_late();
|
|
mutex_unlock(µcode_mutex);
|
|
|
|
put:
|
|
put_online_cpus();
|
|
|
|
- if (!ret)
|
|
+ if (ret >= 0)
|
|
ret = size;
|
|
|
|
return ret;
|
|
--
|
|
cgit v1.1
|
|
|