[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v9 04/15] microcode: introduce a global cache of ucode patch
On Mon, Aug 19, 2019 at 09:25:17AM +0800, Chao Gao wrote: > to replace the current per-cpu cache 'uci->mc'. > > With the assumption that all CPUs in the system have the same signature > (family, model, stepping and 'pf'), one microcode update matches with > one cpu should match with others. Having multiple microcode revisions > on different cpus would cause system unstable and should be avoided. > Hence, caching only one microcode update is good enough for all cases. > > Introduce a global variable, microcode_cache, to store the newest > matching microcode update. Whenever we get a new valid microcode update, > its revision id is compared against that of the microcode update to > determine whether the "microcode_cache" needs to be replaced. And > this global cache is loaded to cpu in apply_microcode(). > > All operations on the cache is protected by 'microcode_mutex'. > > Note that I deliberately avoid touching the old per-cpu cache ('uci->mc') > as I am going to remove it completely in the following patches. We copy > everything to create the new cache blob to avoid reusing some buffers > previously allocated for the old per-cpu cache. It is not so efficient, > but it is already corrected by a patch later in this series. > > Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx> Reviewed-by: Roger Pau Monné <roger.pau@xxxxxxxxxx> I have some nits below, but I don't think they affect functionality in anyway, hence my RB. It would be nice to get those fixed as follow-ups if others think the current version is suitable for committing. > --- > Changes in v9: > - on Intel side, ->compare_patch just checks the patch revision number. > - explain why all buffers are copied in alloc_microcode_patch() in > patch description. > > Changes in v8: > - Free generic wrapper struct in general code > - Try to update cache as long as a patch covers current cpu. Previsouly, > cache is updated only if the patch is newer than current update revision in > the CPU. The small difference can work around a broken bios which only > applies microcode update to BSP and software has to apply the same > update to other CPUs. > > Changes in v7: > - reworked to cache only one microcode patch rather than a list of > microcode patches. > --- > xen/arch/x86/microcode.c | 39 ++++++++++++++++++ > xen/arch/x86/microcode_amd.c | 90 > +++++++++++++++++++++++++++++++++++++---- > xen/arch/x86/microcode_intel.c | 73 ++++++++++++++++++++++++++------- > xen/include/asm-x86/microcode.h | 17 ++++++++ > 4 files changed, 197 insertions(+), 22 deletions(-) > > diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c > index 421d57e..0ecd2fd 100644 > --- a/xen/arch/x86/microcode.c > +++ b/xen/arch/x86/microcode.c > @@ -61,6 +61,9 @@ static struct ucode_mod_blob __initdata ucode_blob; > */ > static bool_t __initdata ucode_scan; > > +/* Protected by microcode_mutex */ > +static struct microcode_patch *microcode_cache; > + > void __init microcode_set_module(unsigned int idx) > { > ucode_mod_idx = idx; > @@ -262,6 +265,42 @@ int microcode_resume_cpu(unsigned int cpu) > return err; > } > > +void microcode_free_patch(struct microcode_patch *microcode_patch) > +{ > + microcode_ops->free_patch(microcode_patch->mc); > + xfree(microcode_patch); > +} > + > +const struct microcode_patch *microcode_get_cache(void) > +{ > + ASSERT(spin_is_locked(µcode_mutex)); > + > + return microcode_cache; > +} > + > +/* Return true if cache gets updated. Otherwise, return false */ > +bool microcode_update_cache(struct microcode_patch *patch) > +{ > + Nit: extra newline above. > + ASSERT(spin_is_locked(µcode_mutex)); > + > + if ( !microcode_cache ) > + microcode_cache = patch; > + else if ( microcode_ops->compare_patch(patch, > + microcode_cache) == NEW_UCODE ) > + { > + microcode_free_patch(microcode_cache); > + microcode_cache = patch; > + } > + else > + { > + microcode_free_patch(patch); > + return false; > + } > + > + return true; > +} > + > static int microcode_update_cpu(const void *buf, size_t size) > { > int err; > diff --git a/xen/arch/x86/microcode_amd.c b/xen/arch/x86/microcode_amd.c > index 3db3555..30129ca 100644 > --- a/xen/arch/x86/microcode_amd.c > +++ b/xen/arch/x86/microcode_amd.c > @@ -190,24 +190,83 @@ static enum microcode_match_result microcode_fits( > return NEW_UCODE; > } > > +static bool match_cpu(const struct microcode_patch *patch) > +{ > + if ( !patch ) > + return false; > + return microcode_fits(patch->mc_amd, smp_processor_id()) == NEW_UCODE; > +} > + > +static struct microcode_patch *alloc_microcode_patch( > + const struct microcode_amd *mc_amd) > +{ > + struct microcode_patch *microcode_patch = xmalloc(struct > microcode_patch); > + struct microcode_amd *cache = xmalloc(struct microcode_amd); > + void *mpb = xmalloc_bytes(mc_amd->mpb_size); > + struct equiv_cpu_entry *equiv_cpu_table = > + xmalloc_bytes(mc_amd->equiv_cpu_table_size); > + > + if ( !microcode_patch || !cache || !mpb || !equiv_cpu_table ) > + { > + xfree(microcode_patch); > + xfree(cache); > + xfree(mpb); > + xfree(equiv_cpu_table); > + return ERR_PTR(-ENOMEM); > + } > + > + memcpy(mpb, mc_amd->mpb, mc_amd->mpb_size); > + cache->mpb = mpb; > + cache->mpb_size = mc_amd->mpb_size; > + memcpy(equiv_cpu_table, mc_amd->equiv_cpu_table, > + mc_amd->equiv_cpu_table_size); > + cache->equiv_cpu_table = equiv_cpu_table; > + cache->equiv_cpu_table_size = mc_amd->equiv_cpu_table_size; > + microcode_patch->mc_amd = cache; > + > + return microcode_patch; > +} > + > +static void free_patch(void *mc) > +{ > + struct microcode_amd *mc_amd = mc; > + > + xfree(mc_amd->equiv_cpu_table); > + xfree(mc_amd->mpb); > + xfree(mc_amd); > +} It's asymmetric that alloc_microcode_patch allocates microcode_patch, but free_patch doesn't free it. Not a big deal, but it would be good to make this symmetric IMO. > + > +static enum microcode_match_result compare_patch( > + const struct microcode_patch *new, const struct microcode_patch *old) > +{ > + const struct microcode_amd *new_mc = new->mc_amd; > + const struct microcode_header_amd *new_header = new_mc->mpb; > + const struct microcode_amd *old_mc = old->mc_amd; > + const struct microcode_header_amd *old_header = old_mc->mpb; The local variables new_mc/old_mc are used just one, and hence are not really helpful IMO, you could just do: const struct microcode_header_amd *new_header = new->mc_amd->mpb; const struct microcode_header_amd *old_header = old->mc_amd->mpb; Again, just a nit. > + > + if ( new_header->processor_rev_id == old_header->processor_rev_id ) > + return (new_header->patch_id > old_header->patch_id) ? > + NEW_UCODE : OLD_UCODE; > + > + return MIS_UCODE; > +} > + > static int apply_microcode(unsigned int cpu) > { > unsigned long flags; > struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu); > uint32_t rev; > - struct microcode_amd *mc_amd = uci->mc.mc_amd; > - struct microcode_header_amd *hdr; > int hw_err; > + const struct microcode_header_amd *hdr; > + const struct microcode_patch *patch = microcode_get_cache(); > > /* We should bind the task to the CPU */ > BUG_ON(raw_smp_processor_id() != cpu); > > - if ( mc_amd == NULL ) > + if ( !match_cpu(patch) ) > return -EINVAL; Another nit, but !patch should get ENOENT rather than EINVAL IMO. > > - hdr = mc_amd->mpb; > - if ( hdr == NULL ) > - return -EINVAL; > + hdr = patch->mc_amd->mpb; > > spin_lock_irqsave(µcode_update_lock, flags); > > @@ -496,7 +555,21 @@ static int cpu_request_microcode(unsigned int cpu, const > void *buf, > while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize, > &offset)) == 0 ) > { > - if ( microcode_fits(mc_amd, cpu) == NEW_UCODE ) > + struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd); > + > + if ( IS_ERR(new_patch) ) > + { > + error = PTR_ERR(new_patch); > + break; > + } > + > + /* Update cache if this patch covers current CPU */ > + if ( microcode_fits(new_patch->mc_amd, cpu) != MIS_UCODE ) > + microcode_update_cache(new_patch); > + else > + microcode_free_patch(new_patch); > + > + if ( match_cpu(microcode_get_cache()) ) > { > error = apply_microcode(cpu); > if ( error ) > @@ -640,6 +713,9 @@ static const struct microcode_ops microcode_amd_ops = { > .collect_cpu_info = collect_cpu_info, > .apply_microcode = apply_microcode, > .start_update = start_update, > + .free_patch = free_patch, > + .compare_patch = compare_patch, > + .match_cpu = match_cpu, > }; > > int __init microcode_init_amd(void) > diff --git a/xen/arch/x86/microcode_intel.c b/xen/arch/x86/microcode_intel.c > index c185b5c..14485dc 100644 > --- a/xen/arch/x86/microcode_intel.c > +++ b/xen/arch/x86/microcode_intel.c > @@ -259,6 +259,31 @@ static int microcode_sanity_check(void *mc) > return 0; > } > > +static bool match_cpu(const struct microcode_patch *patch) > +{ > + if ( !patch ) > + return false; > + > + return microcode_update_match(&patch->mc_intel->hdr, > + smp_processor_id()) == NEW_UCODE; > +} > + > +static void free_patch(void *mc) > +{ > + xfree(mc); > +} > + > +/* > + * Both patches to compare are supposed to be applicable to local CPU. > + * Just compare the revision number. > + */ > +static enum microcode_match_result compare_patch( > + const struct microcode_patch *new, const struct microcode_patch *old) > +{ > + return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ? NEW_UCODE : > + OLD_UCODE; Nit, the usual format in Xen would be: return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ? NEW_UCODE : OLD_UCODE; > +} > + > /* > * return 0 - no update found > * return 1 - found update > @@ -269,10 +294,26 @@ static int get_matching_microcode(const void *mc, > unsigned int cpu) > struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu); > const struct microcode_header_intel *mc_header = mc; > unsigned long total_size = get_totalsize(mc_header); > - void *new_mc; > + void *new_mc = xmalloc_bytes(total_size); > + struct microcode_patch *new_patch = xmalloc(struct microcode_patch); > > - if ( microcode_update_match(mc, cpu) != NEW_UCODE ) > + if ( !new_patch || !new_mc ) > + { > + xfree(new_patch); > + xfree(new_mc); > + return -ENOMEM; > + } > + memcpy(new_mc, mc, total_size); > + new_patch->mc_intel = new_mc; > + > + /* Make sure that this patch covers current CPU */ > + if ( microcode_update_match(mc, cpu) == MIS_UCODE ) > + { > + microcode_free_patch(new_patch); > return 0; > + } Nit: won't it be easier to do this check first and then allocate if needed? AFAICT new_mc or new_patch are not required by the microcode_update_match call, and hence could be allocated after such call. Maybe this ugliness will go away in following patches? Thanks, Roger. _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |