Blame linux-kernel-patches/10-a5321aec6412b20b5ad15db2d6b916c05349dbff.patch

Packit 863535
From a5321aec6412b20b5ad15db2d6b916c05349dbff Mon Sep 17 00:00:00 2001
Packit 863535
From: Ashok Raj <ashok.raj@intel.com>
Packit 863535
Date: Wed, 28 Feb 2018 11:28:46 +0100
Packit 863535
Subject: x86/microcode: Synchronize late microcode loading
Packit 863535

Packit 863535
Original idea by Ashok, completely rewritten by Borislav.
Packit 863535

Packit 863535
Before you read any further: the early loading method is still the
Packit 863535
preferred one and you should always do that. The following patch is
Packit 863535
improving the late loading mechanism for long running jobs and cloud use
Packit 863535
cases.
Packit 863535

Packit 863535
Gather all cores and serialize the microcode update on them by doing it
Packit 863535
one-by-one to make the late update process as reliable as possible and
Packit 863535
avoid potential issues caused by the microcode update.
Packit 863535

Packit 863535
[ Borislav: Rewrite completely. ]
Packit 863535

Packit 863535
Co-developed-by: Borislav Petkov <bp@suse.de>
Packit 863535
Signed-off-by: Ashok Raj <ashok.raj@intel.com>
Packit 863535
Signed-off-by: Borislav Petkov <bp@suse.de>
Packit 863535
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Packit 863535
Tested-by: Tom Lendacky <thomas.lendacky@amd.com>
Packit 863535
Tested-by: Ashok Raj <ashok.raj@intel.com>
Packit 863535
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
Packit 863535
Cc: Arjan Van De Ven <arjan.van.de.ven@intel.com>
Packit 863535
Link: https://lkml.kernel.org/r/20180228102846.13447-8-bp@alien8.de
Packit 863535
---
Packit 863535
 arch/x86/kernel/cpu/microcode/core.c | 118 +++++++++++++++++++++++++++--------
Packit 863535
 1 file changed, 92 insertions(+), 26 deletions(-)
Packit 863535

Packit 863535
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
Packit 863535
index 5dd157d..70ecbc8 100644
Packit 863535
--- a/arch/x86/kernel/cpu/microcode/core.c
Packit 863535
+++ b/arch/x86/kernel/cpu/microcode/core.c
Packit 863535
@@ -22,13 +22,16 @@
Packit 863535
 #define pr_fmt(fmt) "microcode: " fmt
Packit 863535
 
Packit 863535
 #include <linux/platform_device.h>
Packit 863535
+#include <linux/stop_machine.h>
Packit 863535
 #include <linux/syscore_ops.h>
Packit 863535
 #include <linux/miscdevice.h>
Packit 863535
 #include <linux/capability.h>
Packit 863535
 #include <linux/firmware.h>
Packit 863535
 #include <linux/kernel.h>
Packit 863535
+#include <linux/delay.h>
Packit 863535
 #include <linux/mutex.h>
Packit 863535
 #include <linux/cpu.h>
Packit 863535
+#include <linux/nmi.h>
Packit 863535
 #include <linux/fs.h>
Packit 863535
 #include <linux/mm.h>
Packit 863535
 
Packit 863535
@@ -64,6 +67,11 @@ LIST_HEAD(microcode_cache);
Packit 863535
  */
Packit 863535
 static DEFINE_MUTEX(microcode_mutex);
Packit 863535
 
Packit 863535
+/*
Packit 863535
+ * Serialize late loading so that CPUs get updated one-by-one.
Packit 863535
+ */
Packit 863535
+static DEFINE_SPINLOCK(update_lock);
Packit 863535
+
Packit 863535
 struct ucode_cpu_info		ucode_cpu_info[NR_CPUS];
Packit 863535
 
Packit 863535
 struct cpu_info_ctx {
Packit 863535
@@ -486,6 +494,19 @@ static void __exit microcode_dev_exit(void)
Packit 863535
 /* fake device for request_firmware */
Packit 863535
 static struct platform_device	*microcode_pdev;
Packit 863535
 
Packit 863535
+/*
Packit 863535
+ * Late loading dance. Why the heavy-handed stomp_machine effort?
Packit 863535
+ *
Packit 863535
+ * - HT siblings must be idle and not execute other code while the other sibling
Packit 863535
+ *   is loading microcode in order to avoid any negative interactions caused by
Packit 863535
+ *   the loading.
Packit 863535
+ *
Packit 863535
+ * - In addition, microcode update on the cores must be serialized until this
Packit 863535
+ *   requirement can be relaxed in the future. Right now, this is conservative
Packit 863535
+ *   and good.
Packit 863535
+ */
Packit 863535
+#define SPINUNIT 100 /* 100 nsec */
Packit 863535
+
Packit 863535
 static int check_online_cpus(void)
Packit 863535
 {
Packit 863535
 	if (num_online_cpus() == num_present_cpus())
Packit 863535
@@ -496,23 +517,85 @@ static int check_online_cpus(void)
Packit 863535
 	return -EINVAL;
Packit 863535
 }
Packit 863535
 
Packit 863535
-static enum ucode_state reload_for_cpu(int cpu)
Packit 863535
+static atomic_t late_cpus;
Packit 863535
+
Packit 863535
+/*
Packit 863535
+ * Returns:
Packit 863535
+ * < 0 - on error
Packit 863535
+ *   0 - no update done
Packit 863535
+ *   1 - microcode was updated
Packit 863535
+ */
Packit 863535
+static int __reload_late(void *info)
Packit 863535
 {
Packit 863535
-	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
Packit 863535
+	unsigned int timeout = NSEC_PER_SEC;
Packit 863535
+	int all_cpus = num_online_cpus();
Packit 863535
+	int cpu = smp_processor_id();
Packit 863535
+	enum ucode_state err;
Packit 863535
+	int ret = 0;
Packit 863535
 
Packit 863535
-	if (!uci->valid)
Packit 863535
-		return UCODE_OK;
Packit 863535
+	atomic_dec(&late_cpus);
Packit 863535
+
Packit 863535
+	/*
Packit 863535
+	 * Wait for all CPUs to arrive. A load will not be attempted unless all
Packit 863535
+	 * CPUs show up.
Packit 863535
+	 * */
Packit 863535
+	while (atomic_read(&late_cpus)) {
Packit 863535
+		if (timeout < SPINUNIT) {
Packit 863535
+			pr_err("Timeout while waiting for CPUs rendezvous, remaining: %d\n",
Packit 863535
+				atomic_read(&late_cpus));
Packit 863535
+			return -1;
Packit 863535
+		}
Packit 863535
+
Packit 863535
+		ndelay(SPINUNIT);
Packit 863535
+		timeout -= SPINUNIT;
Packit 863535
+
Packit 863535
+		touch_nmi_watchdog();
Packit 863535
+	}
Packit 863535
+
Packit 863535
+	spin_lock(&update_lock);
Packit 863535
+	apply_microcode_local(&err;;
Packit 863535
+	spin_unlock(&update_lock);
Packit 863535
+
Packit 863535
+	if (err > UCODE_NFOUND) {
Packit 863535
+		pr_warn("Error reloading microcode on CPU %d\n", cpu);
Packit 863535
+		ret = -1;
Packit 863535
+	} else if (err == UCODE_UPDATED) {
Packit 863535
+		ret = 1;
Packit 863535
+	}
Packit 863535
 
Packit 863535
-	return apply_microcode_on_target(cpu);
Packit 863535
+	atomic_inc(&late_cpus);
Packit 863535
+
Packit 863535
+	while (atomic_read(&late_cpus) != all_cpus)
Packit 863535
+		cpu_relax();
Packit 863535
+
Packit 863535
+	return ret;
Packit 863535
+}
Packit 863535
+
Packit 863535
+/*
Packit 863535
+ * Reload microcode late on all CPUs. Wait for a sec until they
Packit 863535
+ * all gather together.
Packit 863535
+ */
Packit 863535
+static int microcode_reload_late(void)
Packit 863535
+{
Packit 863535
+	int ret;
Packit 863535
+
Packit 863535
+	atomic_set(&late_cpus, num_online_cpus());
Packit 863535
+
Packit 863535
+	ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask);
Packit 863535
+	if (ret < 0)
Packit 863535
+		return ret;
Packit 863535
+	else if (ret > 0)
Packit 863535
+		microcode_check();
Packit 863535
+
Packit 863535
+	return ret;
Packit 863535
 }
Packit 863535
 
Packit 863535
 static ssize_t reload_store(struct device *dev,
Packit 863535
 			    struct device_attribute *attr,
Packit 863535
 			    const char *buf, size_t size)
Packit 863535
 {
Packit 863535
-	int cpu, bsp = boot_cpu_data.cpu_index;
Packit 863535
 	enum ucode_state tmp_ret = UCODE_OK;
Packit 863535
-	bool do_callback = false;
Packit 863535
+	int bsp = boot_cpu_data.cpu_index;
Packit 863535
 	unsigned long val;
Packit 863535
 	ssize_t ret = 0;
Packit 863535
 
Packit 863535
@@ -534,30 +617,13 @@ static ssize_t reload_store(struct device *dev,
Packit 863535
 		goto put;
Packit 863535
 
Packit 863535
 	mutex_lock(&microcode_mutex);
Packit 863535
-
Packit 863535
-	for_each_online_cpu(cpu) {
Packit 863535
-		tmp_ret = reload_for_cpu(cpu);
Packit 863535
-		if (tmp_ret > UCODE_NFOUND) {
Packit 863535
-			pr_warn("Error reloading microcode on CPU %d\n", cpu);
Packit 863535
-
Packit 863535
-			/* set retval for the first encountered reload error */
Packit 863535
-			if (!ret)
Packit 863535
-				ret = -EINVAL;
Packit 863535
-		}
Packit 863535
-
Packit 863535
-		if (tmp_ret == UCODE_UPDATED)
Packit 863535
-			do_callback = true;
Packit 863535
-	}
Packit 863535
-
Packit 863535
-	if (!ret && do_callback)
Packit 863535
-		microcode_check();
Packit 863535
-
Packit 863535
+	ret = microcode_reload_late();
Packit 863535
 	mutex_unlock(&microcode_mutex);
Packit 863535
 
Packit 863535
 put:
Packit 863535
 	put_online_cpus();
Packit 863535
 
Packit 863535
-	if (!ret)
Packit 863535
+	if (ret >= 0)
Packit 863535
 		ret = size;
Packit 863535
 
Packit 863535
 	return ret;
Packit 863535
-- 
Packit 863535
cgit v1.1
Packit 863535