Blame linux-kernel-patches/12-bb8c13d61a629276a162c1d2b1a20a815cbcfbb7.patch

Packit Service c9fe53
From bb8c13d61a629276a162c1d2b1a20a815cbcfbb7 Mon Sep 17 00:00:00 2001
Packit Service c9fe53
From: Borislav Petkov <bp@suse.de>
Packit Service c9fe53
Date: Wed, 14 Mar 2018 19:36:15 +0100
Packit Service c9fe53
Subject: x86/microcode: Fix CPU synchronization routine
Packit Service c9fe53

Packit Service c9fe53
Emanuel reported an issue with a hang during microcode update because my
Packit Service c9fe53
dumb idea to use one atomic synchronization variable for both rendezvous
Packit Service c9fe53
- before and after update - was simply bollocks:
Packit Service c9fe53

Packit Service c9fe53
  microcode: microcode_reload_late: late_cpus: 4
Packit Service c9fe53
  microcode: __reload_late: cpu 2 entered
Packit Service c9fe53
  microcode: __reload_late: cpu 1 entered
Packit Service c9fe53
  microcode: __reload_late: cpu 3 entered
Packit Service c9fe53
  microcode: __reload_late: cpu 0 entered
Packit Service c9fe53
  microcode: __reload_late: cpu 1 left
Packit Service c9fe53
  microcode: Timeout while waiting for CPUs rendezvous, remaining: 1
Packit Service c9fe53

Packit Service c9fe53
CPU1 above would finish, leave and the others will still spin waiting for
Packit Service c9fe53
it to join.
Packit Service c9fe53

Packit Service c9fe53
So do two synchronization atomics instead, which makes the code a lot more
Packit Service c9fe53
straightforward.
Packit Service c9fe53

Packit Service c9fe53
Also, since the update is serialized and it also takes quite some time per
Packit Service c9fe53
microcode engine, increase the exit timeout by the number of CPUs on the
Packit Service c9fe53
system.
Packit Service c9fe53

Packit Service c9fe53
That's ok because the moment all CPUs are done, that timeout will be cut
Packit Service c9fe53
short.
Packit Service c9fe53

Packit Service c9fe53
Furthermore, panic when some of the CPUs timeout when returning from a
Packit Service c9fe53
microcode update: we can't allow a system with not all cores updated.
Packit Service c9fe53

Packit Service c9fe53
Also, as an optimization, do not do the exit sync if microcode wasn't
Packit Service c9fe53
updated.
Packit Service c9fe53

Packit Service c9fe53
Reported-by: Emanuel Czirai <xftroxgpx@protonmail.com>
Packit Service c9fe53
Signed-off-by: Borislav Petkov <bp@suse.de>
Packit Service c9fe53
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Packit Service c9fe53
Tested-by: Emanuel Czirai <xftroxgpx@protonmail.com>
Packit Service c9fe53
Tested-by: Ashok Raj <ashok.raj@intel.com>
Packit Service c9fe53
Tested-by: Tom Lendacky <thomas.lendacky@amd.com>
Packit Service c9fe53
Link: https://lkml.kernel.org/r/20180314183615.17629-2-bp@alien8.de
Packit Service c9fe53
---
Packit Service c9fe53
 arch/x86/kernel/cpu/microcode/core.c | 68 ++++++++++++++++++++++--------------
Packit Service c9fe53
 1 file changed, 41 insertions(+), 27 deletions(-)
Packit Service c9fe53

Packit Service c9fe53
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
Packit Service c9fe53
index 9f0fe5b..10c4fc2 100644
Packit Service c9fe53
--- a/arch/x86/kernel/cpu/microcode/core.c
Packit Service c9fe53
+++ b/arch/x86/kernel/cpu/microcode/core.c
Packit Service c9fe53
@@ -517,7 +517,29 @@ static int check_online_cpus(void)
Packit Service c9fe53
 	return -EINVAL;
Packit Service c9fe53
 }
Packit Service c9fe53
 
Packit Service c9fe53
-static atomic_t late_cpus;
Packit Service c9fe53
+static atomic_t late_cpus_in;
Packit Service c9fe53
+static atomic_t late_cpus_out;
Packit Service c9fe53
+
Packit Service c9fe53
+static int __wait_for_cpus(atomic_t *t, long long timeout)
Packit Service c9fe53
+{
Packit Service c9fe53
+	int all_cpus = num_online_cpus();
Packit Service c9fe53
+
Packit Service c9fe53
+	atomic_inc(t);
Packit Service c9fe53
+
Packit Service c9fe53
+	while (atomic_read(t) < all_cpus) {
Packit Service c9fe53
+		if (timeout < SPINUNIT) {
Packit Service c9fe53
+			pr_err("Timeout while waiting for CPUs rendezvous, remaining: %d\n",
Packit Service c9fe53
+				all_cpus - atomic_read(t));
Packit Service c9fe53
+			return 1;
Packit Service c9fe53
+		}
Packit Service c9fe53
+
Packit Service c9fe53
+		ndelay(SPINUNIT);
Packit Service c9fe53
+		timeout -= SPINUNIT;
Packit Service c9fe53
+
Packit Service c9fe53
+		touch_nmi_watchdog();
Packit Service c9fe53
+	}
Packit Service c9fe53
+	return 0;
Packit Service c9fe53
+}
Packit Service c9fe53
 
Packit Service c9fe53
 /*
Packit Service c9fe53
  * Returns:
Packit Service c9fe53
@@ -527,30 +549,16 @@ static atomic_t late_cpus;
Packit Service c9fe53
  */
Packit Service c9fe53
 static int __reload_late(void *info)
Packit Service c9fe53
 {
Packit Service c9fe53
-	unsigned int timeout = NSEC_PER_SEC;
Packit Service c9fe53
-	int all_cpus = num_online_cpus();
Packit Service c9fe53
 	int cpu = smp_processor_id();
Packit Service c9fe53
 	enum ucode_state err;
Packit Service c9fe53
 	int ret = 0;
Packit Service c9fe53
 
Packit Service c9fe53
-	atomic_dec(&late_cpus);
Packit Service c9fe53
-
Packit Service c9fe53
 	/*
Packit Service c9fe53
 	 * Wait for all CPUs to arrive. A load will not be attempted unless all
Packit Service c9fe53
 	 * CPUs show up.
Packit Service c9fe53
 	 * */
Packit Service c9fe53
-	while (atomic_read(&late_cpus)) {
Packit Service c9fe53
-		if (timeout < SPINUNIT) {
Packit Service c9fe53
-			pr_err("Timeout while waiting for CPUs rendezvous, remaining: %d\n",
Packit Service c9fe53
-				atomic_read(&late_cpus));
Packit Service c9fe53
-			return -1;
Packit Service c9fe53
-		}
Packit Service c9fe53
-
Packit Service c9fe53
-		ndelay(SPINUNIT);
Packit Service c9fe53
-		timeout -= SPINUNIT;
Packit Service c9fe53
-
Packit Service c9fe53
-		touch_nmi_watchdog();
Packit Service c9fe53
-	}
Packit Service c9fe53
+	if (__wait_for_cpus(&late_cpus_in, NSEC_PER_SEC))
Packit Service c9fe53
+		return -1;
Packit Service c9fe53
 
Packit Service c9fe53
 	spin_lock(&update_lock);
Packit Service c9fe53
 	apply_microcode_local(&err;;
Packit Service c9fe53
@@ -558,15 +566,22 @@ static int __reload_late(void *info)
Packit Service c9fe53
 
Packit Service c9fe53
 	if (err > UCODE_NFOUND) {
Packit Service c9fe53
 		pr_warn("Error reloading microcode on CPU %d\n", cpu);
Packit Service c9fe53
-		ret = -1;
Packit Service c9fe53
-	} else if (err == UCODE_UPDATED) {
Packit Service c9fe53
+		return -1;
Packit Service c9fe53
+	/* siblings return UCODE_OK because their engine got updated already */
Packit Service c9fe53
+	} else if (err == UCODE_UPDATED || err == UCODE_OK) {
Packit Service c9fe53
 		ret = 1;
Packit Service c9fe53
+	} else {
Packit Service c9fe53
+		return ret;
Packit Service c9fe53
 	}
Packit Service c9fe53
 
Packit Service c9fe53
-	atomic_inc(&late_cpus);
Packit Service c9fe53
-
Packit Service c9fe53
-	while (atomic_read(&late_cpus) != all_cpus)
Packit Service c9fe53
-		cpu_relax();
Packit Service c9fe53
+	/*
Packit Service c9fe53
+	 * Increase the wait timeout to a safe value here since we're
Packit Service c9fe53
+	 * serializing the microcode update and that could take a while on a
Packit Service c9fe53
+	 * large number of CPUs. And that is fine as the *actual* timeout will
Packit Service c9fe53
+	 * be determined by the last CPU finished updating and thus cut short.
Packit Service c9fe53
+	 */
Packit Service c9fe53
+	if (__wait_for_cpus(&late_cpus_out, NSEC_PER_SEC * num_online_cpus()))
Packit Service c9fe53
+		panic("Timeout during microcode update!\n");
Packit Service c9fe53
 
Packit Service c9fe53
 	return ret;
Packit Service c9fe53
 }
Packit Service c9fe53
@@ -579,12 +594,11 @@ static int microcode_reload_late(void)
Packit Service c9fe53
 {
Packit Service c9fe53
 	int ret;
Packit Service c9fe53
 
Packit Service c9fe53
-	atomic_set(&late_cpus, num_online_cpus());
Packit Service c9fe53
+	atomic_set(&late_cpus_in,  0);
Packit Service c9fe53
+	atomic_set(&late_cpus_out, 0);
Packit Service c9fe53
 
Packit Service c9fe53
 	ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask);
Packit Service c9fe53
-	if (ret < 0)
Packit Service c9fe53
-		return ret;
Packit Service c9fe53
-	else if (ret > 0)
Packit Service c9fe53
+	if (ret > 0)
Packit Service c9fe53
 		microcode_check();
Packit Service c9fe53
 
Packit Service c9fe53
 	return ret;
Packit Service c9fe53
-- 
Packit Service c9fe53
cgit v1.1
Packit Service c9fe53