[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v6 11/12] x86/microcode: Synchronize late microcode loading
On Mon, Mar 11, 2019 at 03:57:35PM +0800, Chao Gao wrote: > This patch ports microcode improvement patches from linux kernel. > > 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. > > Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx> > Tested-by: Chao Gao <chao.gao@xxxxxxxxx> > [linux commit: a5321aec6412b20b5ad15db2d6b916c05349dbff] > [linux commit: bb8c13d61a629276a162c1d2b1a20a815cbcfbb7] > Cc: Kevin Tian <kevin.tian@xxxxxxxxx> > Cc: Jun Nakajima <jun.nakajima@xxxxxxxxx> > Cc: Ashok Raj <ashok.raj@xxxxxxxxx> > Cc: Borislav Petkov <bp@xxxxxxx> > Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> > Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> > Cc: Jan Beulich <jbeulich@xxxxxxxx> > --- > Changes in v6: > - Use one timeout period for rendezvous stage and another for update stage. > - scale time to wait by the number of remaining cpus to respond. > It helps to find something wrong earlier and thus we can reboot the > system earlier. > --- > xen/arch/x86/microcode.c | 149 > ++++++++++++++++++++++++++++++++++++++++++----- > 1 file changed, 136 insertions(+), 13 deletions(-) > > diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c > index c510808..96bcef6 100644 > --- a/xen/arch/x86/microcode.c > +++ b/xen/arch/x86/microcode.c > @@ -22,6 +22,7 @@ > */ > > #include <xen/cpu.h> > +#include <xen/cpumask.h> > #include <xen/lib.h> > #include <xen/kernel.h> > #include <xen/init.h> > @@ -30,15 +31,34 @@ > #include <xen/smp.h> > #include <xen/softirq.h> > #include <xen/spinlock.h> > +#include <xen/stop_machine.h> > #include <xen/tasklet.h> > #include <xen/guest_access.h> > #include <xen/earlycpio.h> > +#include <xen/watchdog.h> > > +#include <asm/delay.h> > #include <asm/msr.h> > #include <asm/processor.h> > #include <asm/setup.h> > #include <asm/microcode.h> > > +/* > + * Before performing a late microcode update on any thread, we > + * rendezvous all cpus in stop_machine context. The timeout for > + * waiting for cpu rendezvous is 30ms. It is the timeout used by > + * live patching > + */ > +#define MICROCODE_CALLIN_TIMEOUT_US 30000 > + > +/* > + * Timeout for each thread to complete update is set to 1s. It is a > + * conservative choice considering all possible interference (for > + * instance, sometimes wbinvd takes relative long time). And a perfect > + * timeout doesn't help a lot except an early shutdown. > + */ > +#define MICROCODE_UPDATE_TIMEOUT_US 1000000 > + > static module_t __initdata ucode_mod; > static signed int __initdata ucode_mod_idx; > static bool_t __initdata ucode_mod_forced; > @@ -189,6 +209,12 @@ static DEFINE_SPINLOCK(microcode_mutex); > DEFINE_PER_CPU(struct cpu_signature, cpu_sig); > > /* > + * Count the CPUs that have entered and exited the rendezvous > + * during late microcode update. > + */ > +static atomic_t cpu_in, cpu_out; > + > +/* > * Save an ucode patch to the global cache list. > * > * Return true if a patch is added to the global cache or it replaces > @@ -284,25 +310,86 @@ static int microcode_update_cpu(void) > return ret; > } > > -static long do_microcode_update(void *unused) > +/* Wait for CPUs to rendezvous with a timeout (us) */ > +static int wait_for_cpus(atomic_t *cnt, unsigned int expect, > + unsigned int timeout) > { > - int error, cpu; > + while ( atomic_read(cnt) < expect ) > + { > + if ( timeout <= 0 ) timeout is unsigned int.. it will never be < 0.. flip it to read better maybe? > + { > + printk("CPU%d: Timeout when waiting for CPUs calling in\n", > + smp_processor_id()); > + return -EBUSY; > + } > + udelay(1); > + timeout--; > + } > + > + return 0; > +} > > - error = microcode_update_cpu(); > - if ( error ) > - return error; > +static int do_microcode_update(void *unused) > +{ > + unsigned int cpu = smp_processor_id(); > + unsigned int cpu_nr = num_online_cpus(); > + unsigned int finished; > + int ret; > + static bool error; > > - cpu = cpumask_next(smp_processor_id(), &cpu_online_map); > - if ( cpu < nr_cpu_ids ) > - return continue_hypercall_on_cpu(cpu, do_microcode_update, NULL); > > - return error; > + atomic_inc(&cpu_in); > + ret = wait_for_cpus(&cpu_in, cpu_nr, MICROCODE_CALLIN_TIMEOUT_US); > + if ( ret ) > + return ret; > + > + /* > + * Initiate an update on all processors which don't have an online > sibling > + * thread with a lower thread id. Other sibling threads just await the > + * completion of microcode update. > + */ The above comment isn't clear. Looks like you are doing the update just to the first cpu in the sibling map? > + if ( cpu == cpumask_first(per_cpu(cpu_sibling_mask, cpu)) ) > + ret = microcode_update_cpu(); Does ret have any useful things on if the update failed? Doesn't seem to be used before you overwrite later in collect_cpu_info()? > + /* > + * Increase the wait timeout to a safe value here since we're serializing > + * the microcode update and that could take a while on a large number of > + * CPUs. And that is fine as the *actual* timeout will be determined by > + * the last CPU finished updating and thus cut short > + */ > + atomic_inc(&cpu_out); > + finished = atomic_read(&cpu_out); > + while ( !error && finished != cpu_nr ) Maybe i'm reading this wrong.. is "error" used uninitialized? > + { > + /* > + * During each timeout interval, at least a CPU is expected to > + * finish its update. Otherwise, something goes wrong. > + */ > + if ( wait_for_cpus(&cpu_out, finished + 1, > + MICROCODE_UPDATE_TIMEOUT_US) && !error ) > + { > + error = true; > + panic("Timeout when finishing updating microcode (finished > %d/%d)", > + finished, cpu_nr); > + } > + > + finished = atomic_read(&cpu_out); > + } > + > + /* > + * Refresh CPU signature (revision) on threads which didn't call > + * apply_microcode(). > + */ > + if ( cpu != cpumask_first(per_cpu(cpu_sibling_mask, cpu)) ) > + ret = microcode_ops->collect_cpu_info(&this_cpu(cpu_sig)); > + > + return ret; > } > > int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long > len) > { > int ret; > void *buffer; > + unsigned int cpu, nr_cores; > > if ( len != (uint32_t)len ) > return -E2BIG; > @@ -323,11 +410,18 @@ int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) > buf, unsigned long len) > goto free; > } > > + /* cpu_online_map must not change during update */ > + if ( !get_cpu_maps() ) > + { > + ret = -EBUSY; > + goto free; > + } > + > if ( microcode_ops->start_update ) > { > ret = microcode_ops->start_update(); > if ( ret != 0 ) > - goto free; > + goto put; > } > > ret = microcode_parse_blob(buffer, len); > @@ -335,12 +429,41 @@ int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) > buf, unsigned long len) > { > printk(XENLOG_ERR "No valid or newer ucode found. Update abort!\n"); > ret = -EINVAL; > - goto free; > + goto put; > } > > - return continue_hypercall_on_cpu(cpumask_first(&cpu_online_map), > - do_microcode_update, NULL); > + atomic_set(&cpu_in, 0); > + atomic_set(&cpu_out, 0); > + > + /* Calculate the number of online CPU core */ > + nr_cores = 0; > + for_each_online_cpu(cpu) > + if ( cpu == cpumask_first(per_cpu(cpu_sibling_mask, cpu)) ) > + nr_cores++; > + > + printk(XENLOG_INFO "%d cores are to update their microcode\n", nr_cores); > + > + /* > + * We intend to disable interrupt for long time, which may lead to > + * watchdog timeout. > + */ > + watchdog_disable(); > + /* > + * Late loading dance. Why the heavy-handed stop_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 cause 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. > + */ > + ret = stop_machine_run(do_microcode_update, NULL, NR_CPUS); > + watchdog_enable(); > > + put: > + put_cpu_maps(); > free: > xfree(buffer); > return ret; > -- > 1.8.3.1 > _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |