[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(&microcode_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(&microcode_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(&microcode_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

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.