As microcode patch sizes continue to grow, the latency during
late-loading can spike, leading to timeouts and interruptions in running
workloads. This trend of increasing patch sizes is expected to continue,
so a foundational solution is needed to address the issue.
To mitigate the problem, a new staging feature is introduced. This option
processes most of the microcode update (excluding activation) on a
non-critical path, allowing CPUs to remain operational during the
majority of the update. By moving most of the work off the critical path,
the latency spike can be significantly reduced.
Integrate the staging process as an additional step in the late-loading
flow. Introduce a new callback for staging, which is invoked after the
microcode patch image is prepared but before entering the CPU rendezvous
for triggering the update.
Staging follows an opportunistic model: it is attempted when available.
If successful, it reduces CPU rendezvous time; if not, the process falls
back to the legacy loading, potentially exposing the system to higher
latency.
Extend struct microcode_ops to incorporate staging properties, which will
be updated in the vendor code from subsequent patches.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
RFC-V1 -> V1: Rename the function name to the do_something() style (Boris).
Note.
Whether staging should be mandatory is a policy decision that is beyond
the scope of this patch at the moment. For now, the focus is on
establishing a basic flow, with the intention of attracting focused
reviews, while deferring the discussion on staging policy later.
In terms of the flow, an alternative approach could be to integrate
staging as part of microcode preparation on the vendor code side.
However, this was deemed too implicit, as staging involves loading and
validating the microcode image, which differs from typical microcode file
handling.
---
arch/x86/kernel/cpu/microcode/core.c | 12 ++++++++++--
arch/x86/kernel/cpu/microcode/internal.h | 4 +++-
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index b3658d11e7b6..0967fd15be6e 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -676,19 +676,27 @@ static bool setup_cpus(void)
static int load_late_locked(void)
{
+ bool is_safe = false;
+
if (!setup_cpus())
return -EBUSY;
switch (microcode_ops->request_microcode_fw(0, µcode_pdev->dev)) {
case UCODE_NEW:
- return load_late_stop_cpus(false);
+ break;
case UCODE_NEW_SAFE:
- return load_late_stop_cpus(true);
+ is_safe = true;
+ break;
case UCODE_NFOUND:
return -ENOENT;
default:
return -EBADFD;
}
+
+ if (microcode_ops->use_staging)
+ microcode_ops->stage_microcode();
+
+ return load_late_stop_cpus(is_safe);
}
static ssize_t reload_store(struct device *dev,
diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index 21776c529fa9..b27cb8e1228d 100644
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -31,10 +31,12 @@ struct microcode_ops {
* See also the "Synchronization" section in microcode_core.c.
*/
enum ucode_state (*apply_microcode)(int cpu);
+ void (*stage_microcode)(void);
int (*collect_cpu_info)(int cpu, struct cpu_signature *csig);
void (*finalize_late_load)(int result);
unsigned int nmi_safe : 1,
- use_nmi : 1;
+ use_nmi : 1,
+ use_staging : 1;
};
struct early_load_data {
--
2.45.2
On 12/10/24 17:42, Chang S. Bae wrote: > + if (microcode_ops->use_staging) > + microcode_ops->stage_microcode(); I don't know if any non-Intel vendors will implement one of these, but could we please comment this a _bit_? Somebody is going to come along at some point and ask themselves whether they should add a new staging handler or dump some new code in an existing one. The key aspects of the existing staging handler are: 1. Helps the future actual microcode load in some way 2. Has little impact on the rest of the system 3. Can succeed or fail without affecting functionality Did I miss any? Could we add a comment here, or maybe even at the Intel stage_microcode() function explaining this intent?
On Tue, Dec 10, 2024 at 05:42:07PM -0800, Chang S. Bae wrote:
> static int load_late_locked(void)
> {
> + bool is_safe = false;
> +
> if (!setup_cpus())
> return -EBUSY;
>
> switch (microcode_ops->request_microcode_fw(0, µcode_pdev->dev)) {
> case UCODE_NEW:
> - return load_late_stop_cpus(false);
> + break;
> case UCODE_NEW_SAFE:
> - return load_late_stop_cpus(true);
> + is_safe = true;
> + break;
> case UCODE_NFOUND:
> return -ENOENT;
> default:
> return -EBADFD;
> }
> +
> + if (microcode_ops->use_staging)
> + microcode_ops->stage_microcode();
> +
> + return load_late_stop_cpus(is_safe);
> }
Why are you even touching this function instead of doing the staging in the
beginning of load_late_stop_cpus()?
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
On 2/17/2025 5:33 AM, Borislav Petkov wrote:
>
> Why are you even touching this function instead of doing the staging in the
> beginning of load_late_stop_cpus()?
I thought staging is logically distinguishable. While
load_late_stop_cpus() currently performs loading when CPUs are
_stopped_, staging occurs on a non-critical path and remains
interruptible. So, the function name itself seems misaligned with the
staging process.
It looks like commit 4b753955e915 ("x86/microcode: Add per CPU result
state") renamed microcode_reload_late() to the current name:
-/*
- * Reload microcode late on all CPUs. Wait for a sec until they
- * all gather together.
- */
-static int microcode_reload_late(void)
+static int load_late_stop_cpus(void)
which primarily narrowed the function’s scope to microcode rendezvous
for late loading.
Given them all, maybe another option is to introduce a wrapper, instead
of modifying load_late_locked() directly, like:
@@ -536,11 +536,6 @@ static int load_late_stop_cpus(bool is_safe)
int old_rev = boot_cpu_data.microcode;
struct cpuinfo_x86 prev_info;
- if (!is_safe) {
- pr_err("Late microcode loading without minimal revision
check.\n");
- pr_err("You should switch to early loading, if
possible.\n");
- }
-
atomic_set(&late_cpus_in, num_online_cpus());
atomic_set(&offline_in_nmi, 0);
loops_per_usec = loops_per_jiffy / (TICK_NSEC / 1000);
@@ -674,6 +669,20 @@ static bool setup_cpus(void)
return true;
}
+static int load_late_apply(bool is_safe)
+{
+ if (!is_safe) {
+ pr_err("Late microcode loading without minimal revision
check.\n");
+ pr_err("You should switch to early loading, if
possible.\n");
+ }
+
+ /* Stage microcode without stopping CPUs */
+ if (microcode_ops->use_staging)
+ microcode_ops->stage_microcode();
+
+ return load_late_stop_cpus(is_safe);
+}
+
static int load_late_locked(void)
{
if (!setup_cpus())
@@ -681,9 +690,9 @@ static int load_late_locked(void)
switch (microcode_ops->request_microcode_fw(0,
µcode_pdev->dev)) {
case UCODE_NEW:
- return load_late_stop_cpus(false);
+ return load_late_apply(false);
case UCODE_NEW_SAFE:
- return load_late_stop_cpus(true);
+ return load_late_apply(true);
case UCODE_NFOUND:
return -ENOENT;
default:
Thanks,
Chang
On Mon, Feb 17, 2025 at 11:51:28PM -0800, Chang S. Bae wrote:
> On 2/17/2025 5:33 AM, Borislav Petkov wrote:
> >
> > Why are you even touching this function instead of doing the staging in the
> > beginning of load_late_stop_cpus()?
>
> I thought staging is logically distinguishable.
What does that even mean?
> While load_late_stop_cpus() currently performs loading when CPUs are
> _stopped_,
Not entirely - it does preparatory work and then stops the CPUs. Staging could
be part of that prep work.
> staging occurs on a non-critical path and remains interruptible.
So if you really wanna do that and be really "free", then you should do it
outside of load_late_locked() because that runs with the hotplug lock taken.
But then the only thing that matters is, when *exactly* you should stage. If
->request_microcode_fw() fails, staging would be unnecessary work.
So instead of trying to too hard, just stick the staging at the beginning of
load_late_stop_cpus() and be done with it.
Also, if you want to send a patch, don't send it from a mail client which will
mangle it so that it is inapplicable and no one can play with it.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
© 2016 - 2025 Red Hat, Inc.