Re: [PATCH 6/6] xen/x86: Add topology generator

2024-05-02 Thread Jan Beulich
On 01.05.2024 19:06, Alejandro Vallejo wrote:
> On 26/03/2024 17:02, Jan Beulich wrote:
>> On 09.01.2024 16:38, Alejandro Vallejo wrote:
>>> --- a/tools/include/xenguest.h
>>> +++ b/tools/include/xenguest.h
>>> @@ -843,5 +843,20 @@ enum xc_static_cpu_featuremask {
>>>  XC_FEATUREMASK_HVM_HAP_DEF,
>>>  };
>>>  const uint32_t *xc_get_static_cpu_featuremask(enum 
>>> xc_static_cpu_featuremask);
>>> +
>>> +/**
>>> + * Synthesise topology information in `p` given high-level constraints
>>> + *
>>> + * Topology is given in various fields accross several leaves, some of
>>> + * which are vendor-specific. This function uses the policy itself to
>>> + * derive such leaves from threads/core and cores/package.
>>> + *
>>> + * @param p   CPU policy of the domain.
>>> + * @param threads_per_corethreads/core. Doesn't need to be a power of 
>>> 2.
>>> + * @param cores_per_package   cores/package. Doesn't need to be a power of 
>>> 2.
>>> + */
>>> +void xc_topo_from_parts(struct cpu_policy *p,
>>> +uint32_t threads_per_core, uint32_t cores_per_pkg);
>>
>> Do we really want to constrain things to just two (or in fact any fixed 
>> number
>> of) levels? Iirc on AMD there already can be up to 4.
> 
> For the time being, I think we should keep it simple(ish).

Perhaps, with (briefly) stating the reason(s) for doing so.

>>> +void xc_topo_from_parts(struct cpu_policy *p,
>>> +uint32_t threads_per_core, uint32_t cores_per_pkg)
>>> +{
>>> +uint32_t threads_per_pkg = threads_per_core * cores_per_pkg;
>>> +uint32_t apic_id_size;
>>> +
>>> +if ( p->basic.max_leaf < 0xb )
>>> +p->basic.max_leaf = 0xb;
>>> +
>>> +memset(p->topo.raw, 0, sizeof(p->topo.raw));
>>> +
>>> +/* thread level */
>>> +p->topo.subleaf[0].nr_logical = threads_per_core;
>>> +p->topo.subleaf[0].id_shift = 0;
>>> +p->topo.subleaf[0].level = 0;
>>> +p->topo.subleaf[0].type = 1;
>>> +if ( threads_per_core > 1 )
>>> +p->topo.subleaf[0].id_shift = order(threads_per_core - 1);
>>> +
>>> +/* core level */
>>> +p->topo.subleaf[1].nr_logical = cores_per_pkg;
>>> +if ( p->x86_vendor == X86_VENDOR_INTEL )
>>> +p->topo.subleaf[1].nr_logical = threads_per_pkg;
>>
>> Same concern as in the other patch regarding "== Intel".
> 
> Can you please articulate the concern?

See my replies to patch 5. Exactly the same applies here.

>>> +p->topo.subleaf[1].id_shift = p->topo.subleaf[0].id_shift;
>>> +p->topo.subleaf[1].level = 1;
>>> +p->topo.subleaf[1].type = 2;
>>> +if ( cores_per_pkg > 1 )
>>> +p->topo.subleaf[1].id_shift += order(cores_per_pkg - 1);
>>
>> Don't you want to return an error when any of the X_per_Y values is 0?
> 
> I'd rather not.
> 
> The checks on input parameters should be done wherever the inputs are
> taken from. Currently the call site passes threads_per_core=1 and
> cores_per_pkg=1+max_vcpus, so it's all guaranteed to work out.

Hmm, and then have this function produce potentially bogus results?

> Once it comes from xl, libxl should be in charge of the validations.
> Furthermore there's validations the function simply cannot do (nor
> should it) in its current form, like checking that...
> 
> max_vcpus == threads_per_core * cores_per_pkg * n_pkgs.

But that isn't an equality that needs to hold, is it? It would put
constraints on exposing e.g. HT to a guest with an odd number of
vCPU-s. Imo especially with core scheduling HT-ness wants properly
reflecting from underlying hardware, so core-sched-like behavior in
the guest itself can actually achieve its goals.

>>> +apic_id_size = p->topo.subleaf[1].id_shift;
>>> +
>>> +/*
>>> + * Contrary to what the name might seem to imply. HTT is an enabler for
>>> + * SMP and there's no harm in setting it even with a single vCPU.
>>> + */
>>> +p->basic.htt = true;
>>> +
>>> +p->basic.lppp = 0xff;
>>> +if ( threads_per_pkg < 0xff )
>>> +p->basic.lppp = threads_per_pkg;
>>> +
>>> +switch ( p->x86_vendor )
>>> +{
>>> +case X86_VENDOR_INTEL:
>>> +struct cpuid_cache_leaf *sl = p->cache.subleaf;
>>> +for ( size_t i = 0; sl->type &&
>>> +i < ARRAY_SIZE(p->cache.raw); i++, sl++ )
>>> +{
>>> +sl->cores_per_package = cores_per_pkg - 1;
>>> +sl->threads_per_cache = threads_per_core - 1;
>>
>> IOW the names in struct cpuid_cache_leaf aren't quite correct.
> 
> Because of the - 1, you mean?

Yes. The expressions above simply read as if there was an (obvious)
off-by-1.

> If anything our name is marginally clearer
> than the SDM description. It goes on to say "Add one to the return value
> to get the result" in a [**] note, so it's not something we made up.
> 
> Xen: threads_per_cache => SDM: Maximum number of addressable IDs for
> logical processors sharing this cache
> 
> Xen: cores_per_package => SDM: 

Re: [PATCH 6/6] xen/x86: Add topology generator

2024-05-01 Thread Alejandro Vallejo
On 26/03/2024 17:02, Jan Beulich wrote:
> On 09.01.2024 16:38, Alejandro Vallejo wrote:
>> --- a/tools/include/xenguest.h
>> +++ b/tools/include/xenguest.h
>> @@ -843,5 +843,20 @@ enum xc_static_cpu_featuremask {
>>  XC_FEATUREMASK_HVM_HAP_DEF,
>>  };
>>  const uint32_t *xc_get_static_cpu_featuremask(enum 
>> xc_static_cpu_featuremask);
>> +
>> +/**
>> + * Synthesise topology information in `p` given high-level constraints
>> + *
>> + * Topology is given in various fields accross several leaves, some of
>> + * which are vendor-specific. This function uses the policy itself to
>> + * derive such leaves from threads/core and cores/package.
>> + *
>> + * @param p   CPU policy of the domain.
>> + * @param threads_per_corethreads/core. Doesn't need to be a power of 2.
>> + * @param cores_per_package   cores/package. Doesn't need to be a power of 
>> 2.
>> + */
>> +void xc_topo_from_parts(struct cpu_policy *p,
>> +uint32_t threads_per_core, uint32_t cores_per_pkg);
> 
> Do we really want to constrain things to just two (or in fact any fixed number
> of) levels? Iirc on AMD there already can be up to 4.

For the time being, I think we should keep it simple(ish).

Leaf 0xb is always 2 levels, and it implicitly defines the offset into
the x2APIC ID for the 3rd level (the package, or the die). This approach
can be used for a long time before we need to expose anything else. It
can already be used for exposing multi-socket configurations, but it's
not properly wired to xl.

I suspect we won't have a need to expose more complicated topologies
until hetero systems are more common, and by that time the generator
will need tweaking anyway.

> 
>> @@ -1028,3 +976,89 @@ bool xc_cpu_policy_is_compatible(xc_interface *xch, 
>> xc_cpu_policy_t *host,
>>  
>>  return false;
>>  }
>> +
>> +static uint32_t order(uint32_t n)
>> +{
>> +return 32 - __builtin_clz(n);
>> +}
> 
> This isn't really portable. __builtin_clz() takes an unsigned int, which may
> in principle be wider than 32 bits. I also can't see a reason for the
> function to have a fixed-width return type. Perhaps

Sure to unsigned int. I'll s/CHAR_BIT/8/ as that's mandated by POSIX
already.

> 
> static unsigned int order(unsigned int n)
> {
> return sizeof(n) * CHAR_BIT - __builtin_clz(n);
> }
> 
> ?
> 
>> +void xc_topo_from_parts(struct cpu_policy *p,
>> +uint32_t threads_per_core, uint32_t cores_per_pkg)
>> +{
>> +uint32_t threads_per_pkg = threads_per_core * cores_per_pkg;
>> +uint32_t apic_id_size;
>> +
>> +if ( p->basic.max_leaf < 0xb )
>> +p->basic.max_leaf = 0xb;
>> +
>> +memset(p->topo.raw, 0, sizeof(p->topo.raw));
>> +
>> +/* thread level */
>> +p->topo.subleaf[0].nr_logical = threads_per_core;
>> +p->topo.subleaf[0].id_shift = 0;
>> +p->topo.subleaf[0].level = 0;
>> +p->topo.subleaf[0].type = 1;
>> +if ( threads_per_core > 1 )
>> +p->topo.subleaf[0].id_shift = order(threads_per_core - 1);
>> +
>> +/* core level */
>> +p->topo.subleaf[1].nr_logical = cores_per_pkg;
>> +if ( p->x86_vendor == X86_VENDOR_INTEL )
>> +p->topo.subleaf[1].nr_logical = threads_per_pkg;
> 
> Same concern as in the other patch regarding "== Intel".
> 

Can you please articulate the concern?

>> +p->topo.subleaf[1].id_shift = p->topo.subleaf[0].id_shift;
>> +p->topo.subleaf[1].level = 1;
>> +p->topo.subleaf[1].type = 2;
>> +if ( cores_per_pkg > 1 )
>> +p->topo.subleaf[1].id_shift += order(cores_per_pkg - 1);
> 
> Don't you want to return an error when any of the X_per_Y values is 0?

I'd rather not.

The checks on input parameters should be done wherever the inputs are
taken from. Currently the call site passes threads_per_core=1 and
cores_per_pkg=1+max_vcpus, so it's all guaranteed to work out.

Once it comes from xl, libxl should be in charge of the validations.
Furthermore there's validations the function simply cannot do (nor
should it) in its current form, like checking that...

max_vcpus == threads_per_core * cores_per_pkg * n_pkgs.

> 
>> +apic_id_size = p->topo.subleaf[1].id_shift;
>> +
>> +/*
>> + * Contrary to what the name might seem to imply. HTT is an enabler for
>> + * SMP and there's no harm in setting it even with a single vCPU.
>> + */
>> +p->basic.htt = true;
>> +
>> +p->basic.lppp = 0xff;
>> +if ( threads_per_pkg < 0xff )
>> +p->basic.lppp = threads_per_pkg;
>> +
>> +switch ( p->x86_vendor )
>> +{
>> +case X86_VENDOR_INTEL:
>> +struct cpuid_cache_leaf *sl = p->cache.subleaf;
>> +for ( size_t i = 0; sl->type &&
>> +i < ARRAY_SIZE(p->cache.raw); i++, sl++ )
>> +{
>> +sl->cores_per_package = cores_per_pkg - 1;
>> +sl->threads_per_cache = threads_per_core - 1;
> 
> IOW the names in struct cpuid_cache_leaf aren't quite correct.


Re: [PATCH 6/6] xen/x86: Add topology generator

2024-03-26 Thread Jan Beulich
On 09.01.2024 16:38, Alejandro Vallejo wrote:
> --- a/tools/include/xenguest.h
> +++ b/tools/include/xenguest.h
> @@ -843,5 +843,20 @@ enum xc_static_cpu_featuremask {
>  XC_FEATUREMASK_HVM_HAP_DEF,
>  };
>  const uint32_t *xc_get_static_cpu_featuremask(enum 
> xc_static_cpu_featuremask);
> +
> +/**
> + * Synthesise topology information in `p` given high-level constraints
> + *
> + * Topology is given in various fields accross several leaves, some of
> + * which are vendor-specific. This function uses the policy itself to
> + * derive such leaves from threads/core and cores/package.
> + *
> + * @param p   CPU policy of the domain.
> + * @param threads_per_corethreads/core. Doesn't need to be a power of 2.
> + * @param cores_per_package   cores/package. Doesn't need to be a power of 2.
> + */
> +void xc_topo_from_parts(struct cpu_policy *p,
> +uint32_t threads_per_core, uint32_t cores_per_pkg);

Do we really want to constrain things to just two (or in fact any fixed number
of) levels? Iirc on AMD there already can be up to 4.

> @@ -1028,3 +976,89 @@ bool xc_cpu_policy_is_compatible(xc_interface *xch, 
> xc_cpu_policy_t *host,
>  
>  return false;
>  }
> +
> +static uint32_t order(uint32_t n)
> +{
> +return 32 - __builtin_clz(n);
> +}

This isn't really portable. __builtin_clz() takes an unsigned int, which may
in principle be wider than 32 bits. I also can't see a reason for the
function to have a fixed-width return type. Perhaps

static unsigned int order(unsigned int n)
{
return sizeof(n) * CHAR_BIT - __builtin_clz(n);
}

?

> +void xc_topo_from_parts(struct cpu_policy *p,
> +uint32_t threads_per_core, uint32_t cores_per_pkg)
> +{
> +uint32_t threads_per_pkg = threads_per_core * cores_per_pkg;
> +uint32_t apic_id_size;
> +
> +if ( p->basic.max_leaf < 0xb )
> +p->basic.max_leaf = 0xb;
> +
> +memset(p->topo.raw, 0, sizeof(p->topo.raw));
> +
> +/* thread level */
> +p->topo.subleaf[0].nr_logical = threads_per_core;
> +p->topo.subleaf[0].id_shift = 0;
> +p->topo.subleaf[0].level = 0;
> +p->topo.subleaf[0].type = 1;
> +if ( threads_per_core > 1 )
> +p->topo.subleaf[0].id_shift = order(threads_per_core - 1);
> +
> +/* core level */
> +p->topo.subleaf[1].nr_logical = cores_per_pkg;
> +if ( p->x86_vendor == X86_VENDOR_INTEL )
> +p->topo.subleaf[1].nr_logical = threads_per_pkg;

Same concern as in the other patch regarding "== Intel".

> +p->topo.subleaf[1].id_shift = p->topo.subleaf[0].id_shift;
> +p->topo.subleaf[1].level = 1;
> +p->topo.subleaf[1].type = 2;
> +if ( cores_per_pkg > 1 )
> +p->topo.subleaf[1].id_shift += order(cores_per_pkg - 1);

Don't you want to return an error when any of the X_per_Y values is 0?

> +apic_id_size = p->topo.subleaf[1].id_shift;
> +
> +/*
> + * Contrary to what the name might seem to imply. HTT is an enabler for
> + * SMP and there's no harm in setting it even with a single vCPU.
> + */
> +p->basic.htt = true;
> +
> +p->basic.lppp = 0xff;
> +if ( threads_per_pkg < 0xff )
> +p->basic.lppp = threads_per_pkg;
> +
> +switch ( p->x86_vendor )
> +{
> +case X86_VENDOR_INTEL:
> +struct cpuid_cache_leaf *sl = p->cache.subleaf;
> +for ( size_t i = 0; sl->type &&
> +i < ARRAY_SIZE(p->cache.raw); i++, sl++ )
> +{
> +sl->cores_per_package = cores_per_pkg - 1;
> +sl->threads_per_cache = threads_per_core - 1;

IOW the names in struct cpuid_cache_leaf aren't quite correct.

> +if ( sl->type == 3 /* unified cache */ )
> +sl->threads_per_cache = threads_per_pkg - 1;
> +}
> +break;
> +
> +case X86_VENDOR_AMD:
> +case X86_VENDOR_HYGON:
> +/* Expose p->basic.lppp */
> +p->extd.cmp_legacy = true;
> +
> +/* Clip NC to the maximum value it can hold */
> +p->extd.nc = 0xff;
> +if ( threads_per_pkg <= 0xff )
> +p->extd.nc = threads_per_pkg - 1;
> +
> +/* TODO: Expose leaf e1E */
> +p->extd.topoext = false;
> +
> +/*
> + * Clip APIC ID to 8, as that's what high core-count machines do

Nit: "... to 8 bits, ..."

> + *
> + * That what AMD EPYC 9654 does with >256 CPUs
> + */
> +p->extd.apic_id_size = 8;
> +if ( apic_id_size < 8 )
> +p->extd.apic_id_size = apic_id_size;

Use min(), with apic_id_size's type suitably adjusted?

> --- a/xen/arch/x86/cpu-policy.c
> +++ b/xen/arch/x86/cpu-policy.c
> @@ -278,9 +278,6 @@ static void recalculate_misc(struct cpu_policy *p)
>  
>  p->basic.raw[0x8] = EMPTY_LEAF;
>  
> -/* TODO: Rework topology logic. */
> -memset(p->topo.raw, 0, sizeof(p->topo.raw));

Re: [PATCH 6/6] xen/x86: Add topology generator

2024-03-20 Thread Roger Pau Monné
On Tue, Jan 09, 2024 at 03:38:34PM +, Alejandro Vallejo wrote:
> This allows toolstack to synthesise sensible topologies for guests. In
> particular, this patch causes x2APIC IDs to be packed according to the
> topology now exposed to the guests on leaf 0xb.
> 
> Signed-off-by: Alejandro Vallejo 
> ---
>  tools/include/xenguest.h|  15 
>  tools/libs/guest/xg_cpuid_x86.c | 144 
>  xen/arch/x86/cpu-policy.c   |   6 +-
>  3 files changed, 107 insertions(+), 58 deletions(-)
> 
> diff --git a/tools/include/xenguest.h b/tools/include/xenguest.h
> index 4e9078fdee..f0043c559b 100644
> --- a/tools/include/xenguest.h
> +++ b/tools/include/xenguest.h
> @@ -843,5 +843,20 @@ enum xc_static_cpu_featuremask {
>  XC_FEATUREMASK_HVM_HAP_DEF,
>  };
>  const uint32_t *xc_get_static_cpu_featuremask(enum 
> xc_static_cpu_featuremask);
> +
> +/**
> + * Synthesise topology information in `p` given high-level constraints
> + *
> + * Topology is given in various fields accross several leaves, some of
> + * which are vendor-specific. This function uses the policy itself to
> + * derive such leaves from threads/core and cores/package.
> + *
> + * @param p   CPU policy of the domain.
> + * @param threads_per_corethreads/core. Doesn't need to be a power of 2.
> + * @param cores_per_package   cores/package. Doesn't need to be a power of 2.
> + */
> +void xc_topo_from_parts(struct cpu_policy *p,
> +uint32_t threads_per_core, uint32_t cores_per_pkg);

I think you can use plain unsigned int here.

> +
>  #endif /* __i386__ || __x86_64__ */
>  #endif /* XENGUEST_H */
> diff --git a/tools/libs/guest/xg_cpuid_x86.c b/tools/libs/guest/xg_cpuid_x86.c
> index 4453178100..7a622721be 100644
> --- a/tools/libs/guest/xg_cpuid_x86.c
> +++ b/tools/libs/guest/xg_cpuid_x86.c
> @@ -584,7 +584,7 @@ int xc_cpuid_apply_policy(xc_interface *xch, uint32_t 
> domid, bool restore,
>  bool hvm;
>  xc_domaininfo_t di;
>  struct xc_cpu_policy *p = xc_cpu_policy_init();
> -unsigned int i, nr_leaves = ARRAY_SIZE(p->leaves), nr_msrs = 0;
> +unsigned int nr_leaves = ARRAY_SIZE(p->leaves), nr_msrs = 0;
>  uint32_t err_leaf = -1, err_subleaf = -1, err_msr = -1;
>  uint32_t host_featureset[FEATURESET_NR_ENTRIES] = {};
>  uint32_t len = ARRAY_SIZE(host_featureset);
> @@ -727,60 +727,8 @@ int xc_cpuid_apply_policy(xc_interface *xch, uint32_t 
> domid, bool restore,
>  }
>  else
>  {
> -/*
> - * Topology for HVM guests is entirely controlled by Xen.  For now, 
> we
> - * hardcode APIC_ID = vcpu_id * 2 to give the illusion of no SMT.
> - */
> -p->policy.basic.htt = true;
> -p->policy.extd.cmp_legacy = false;
> -
> -/*
> - * Leaf 1 EBX[23:16] is Maximum Logical Processors Per Package.
> - * Update to reflect vLAPIC_ID = vCPU_ID * 2, but make sure to avoid
> - * overflow.
> - */
> -if ( !p->policy.basic.lppp )
> -p->policy.basic.lppp = 2;
> -else if ( !(p->policy.basic.lppp & 0x80) )
> -p->policy.basic.lppp *= 2;
> -
> -switch ( p->policy.x86_vendor )
> -{
> -case X86_VENDOR_INTEL:
> -for ( i = 0; (p->policy.cache.subleaf[i].type &&
> -  i < ARRAY_SIZE(p->policy.cache.raw)); ++i )
> -{
> -p->policy.cache.subleaf[i].cores_per_package =
> -(p->policy.cache.subleaf[i].cores_per_package << 1) | 1;
> -p->policy.cache.subleaf[i].threads_per_cache = 0;
> -}
> -break;
> -
> -case X86_VENDOR_AMD:
> -case X86_VENDOR_HYGON:
> -/*
> - * Leaf 0x8008 ECX[15:12] is ApicIdCoreSize.
> - * Leaf 0x8008 ECX[7:0] is NumberOfCores (minus one).
> - * Update to reflect vLAPIC_ID = vCPU_ID * 2.  But avoid
> - * - overflow,
> - * - going out of sync with leaf 1 EBX[23:16],
> - * - incrementing ApicIdCoreSize when it's zero (which changes 
> the
> - *   meaning of bits 7:0).
> - *
> - * UPDATE: I addition to avoiding overflow, some
> - * proprietary operating systems have trouble with
> - * apic_id_size values greater than 7.  Limit the value to
> - * 7 for now.
> - */
> -if ( p->policy.extd.nc < 0x7f )
> -{
> -if ( p->policy.extd.apic_id_size != 0 && 
> p->policy.extd.apic_id_size < 0x7 )
> -p->policy.extd.apic_id_size++;
> -
> -p->policy.extd.nc = (p->policy.extd.nc << 1) | 1;
> -}
> -break;
> -}
> +/* TODO: Expose the ability to choose a custom topology for HVM/PVH 
> */
> +xc_topo_from_parts(>policy, 1, di.max_vcpu_id + 1);

It would be better if this was split into two 

Re: [PATCH 6/6] xen/x86: Add topology generator

2024-01-10 Thread Jan Beulich
On 10.01.2024 15:16, Alejandro Vallejo wrote:
> Review-to-self after running in Gitlab:
> 
> On 09/01/2024 15:38, Alejandro Vallejo wrote:
>> +p->basic.lppp = 0xff;
>> +if ( threads_per_pkg < 0xff )
>> +p->basic.lppp = threads_per_pkg;
>> +
>> +switch ( p->x86_vendor )
>> +{
>> +case X86_VENDOR_INTEL:
>> +struct cpuid_cache_leaf *sl = p->cache.subleaf;
>> +for ( size_t i = 0; sl->type &&
>> +i < ARRAY_SIZE(p->cache.raw); i++, sl++ )
>> +{
>> +sl->cores_per_package = cores_per_pkg - 1;
>> +sl->threads_per_cache = threads_per_core - 1;
>> +if ( sl->type == 3 /* unified cache */ )
>> +sl->threads_per_cache = threads_per_pkg - 1;
>> +}
>> +break;
>> +
>> +case X86_VENDOR_AMD:
>> +case X86_VENDOR_HYGON:
> 
> Missing braces around the INTEL block due to the variable declarared
> there. I'll include that in v2 after the rest of the review comments
> come through.

And (just looking at the fragment above) too deep indentation as well.

Jan



Re: [PATCH 6/6] xen/x86: Add topology generator

2024-01-10 Thread Alejandro Vallejo
Review-to-self after running in Gitlab:

On 09/01/2024 15:38, Alejandro Vallejo wrote:
> +p->basic.lppp = 0xff;
> +if ( threads_per_pkg < 0xff )
> +p->basic.lppp = threads_per_pkg;
> +
> +switch ( p->x86_vendor )
> +{
> +case X86_VENDOR_INTEL:
> +struct cpuid_cache_leaf *sl = p->cache.subleaf;
> +for ( size_t i = 0; sl->type &&
> +i < ARRAY_SIZE(p->cache.raw); i++, sl++ )
> +{
> +sl->cores_per_package = cores_per_pkg - 1;
> +sl->threads_per_cache = threads_per_core - 1;
> +if ( sl->type == 3 /* unified cache */ )
> +sl->threads_per_cache = threads_per_pkg - 1;
> +}
> +break;
> +
> +case X86_VENDOR_AMD:
> +case X86_VENDOR_HYGON:

Missing braces around the INTEL block due to the variable declarared
there. I'll include that in v2 after the rest of the review comments
come through.

Cheers,
Alejandro



[PATCH 6/6] xen/x86: Add topology generator

2024-01-09 Thread Alejandro Vallejo
This allows toolstack to synthesise sensible topologies for guests. In
particular, this patch causes x2APIC IDs to be packed according to the
topology now exposed to the guests on leaf 0xb.

Signed-off-by: Alejandro Vallejo 
---
 tools/include/xenguest.h|  15 
 tools/libs/guest/xg_cpuid_x86.c | 144 
 xen/arch/x86/cpu-policy.c   |   6 +-
 3 files changed, 107 insertions(+), 58 deletions(-)

diff --git a/tools/include/xenguest.h b/tools/include/xenguest.h
index 4e9078fdee..f0043c559b 100644
--- a/tools/include/xenguest.h
+++ b/tools/include/xenguest.h
@@ -843,5 +843,20 @@ enum xc_static_cpu_featuremask {
 XC_FEATUREMASK_HVM_HAP_DEF,
 };
 const uint32_t *xc_get_static_cpu_featuremask(enum xc_static_cpu_featuremask);
+
+/**
+ * Synthesise topology information in `p` given high-level constraints
+ *
+ * Topology is given in various fields accross several leaves, some of
+ * which are vendor-specific. This function uses the policy itself to
+ * derive such leaves from threads/core and cores/package.
+ *
+ * @param p   CPU policy of the domain.
+ * @param threads_per_corethreads/core. Doesn't need to be a power of 2.
+ * @param cores_per_package   cores/package. Doesn't need to be a power of 2.
+ */
+void xc_topo_from_parts(struct cpu_policy *p,
+uint32_t threads_per_core, uint32_t cores_per_pkg);
+
 #endif /* __i386__ || __x86_64__ */
 #endif /* XENGUEST_H */
diff --git a/tools/libs/guest/xg_cpuid_x86.c b/tools/libs/guest/xg_cpuid_x86.c
index 4453178100..7a622721be 100644
--- a/tools/libs/guest/xg_cpuid_x86.c
+++ b/tools/libs/guest/xg_cpuid_x86.c
@@ -584,7 +584,7 @@ int xc_cpuid_apply_policy(xc_interface *xch, uint32_t 
domid, bool restore,
 bool hvm;
 xc_domaininfo_t di;
 struct xc_cpu_policy *p = xc_cpu_policy_init();
-unsigned int i, nr_leaves = ARRAY_SIZE(p->leaves), nr_msrs = 0;
+unsigned int nr_leaves = ARRAY_SIZE(p->leaves), nr_msrs = 0;
 uint32_t err_leaf = -1, err_subleaf = -1, err_msr = -1;
 uint32_t host_featureset[FEATURESET_NR_ENTRIES] = {};
 uint32_t len = ARRAY_SIZE(host_featureset);
@@ -727,60 +727,8 @@ int xc_cpuid_apply_policy(xc_interface *xch, uint32_t 
domid, bool restore,
 }
 else
 {
-/*
- * Topology for HVM guests is entirely controlled by Xen.  For now, we
- * hardcode APIC_ID = vcpu_id * 2 to give the illusion of no SMT.
- */
-p->policy.basic.htt = true;
-p->policy.extd.cmp_legacy = false;
-
-/*
- * Leaf 1 EBX[23:16] is Maximum Logical Processors Per Package.
- * Update to reflect vLAPIC_ID = vCPU_ID * 2, but make sure to avoid
- * overflow.
- */
-if ( !p->policy.basic.lppp )
-p->policy.basic.lppp = 2;
-else if ( !(p->policy.basic.lppp & 0x80) )
-p->policy.basic.lppp *= 2;
-
-switch ( p->policy.x86_vendor )
-{
-case X86_VENDOR_INTEL:
-for ( i = 0; (p->policy.cache.subleaf[i].type &&
-  i < ARRAY_SIZE(p->policy.cache.raw)); ++i )
-{
-p->policy.cache.subleaf[i].cores_per_package =
-(p->policy.cache.subleaf[i].cores_per_package << 1) | 1;
-p->policy.cache.subleaf[i].threads_per_cache = 0;
-}
-break;
-
-case X86_VENDOR_AMD:
-case X86_VENDOR_HYGON:
-/*
- * Leaf 0x8008 ECX[15:12] is ApicIdCoreSize.
- * Leaf 0x8008 ECX[7:0] is NumberOfCores (minus one).
- * Update to reflect vLAPIC_ID = vCPU_ID * 2.  But avoid
- * - overflow,
- * - going out of sync with leaf 1 EBX[23:16],
- * - incrementing ApicIdCoreSize when it's zero (which changes the
- *   meaning of bits 7:0).
- *
- * UPDATE: I addition to avoiding overflow, some
- * proprietary operating systems have trouble with
- * apic_id_size values greater than 7.  Limit the value to
- * 7 for now.
- */
-if ( p->policy.extd.nc < 0x7f )
-{
-if ( p->policy.extd.apic_id_size != 0 && 
p->policy.extd.apic_id_size < 0x7 )
-p->policy.extd.apic_id_size++;
-
-p->policy.extd.nc = (p->policy.extd.nc << 1) | 1;
-}
-break;
-}
+/* TODO: Expose the ability to choose a custom topology for HVM/PVH */
+xc_topo_from_parts(>policy, 1, di.max_vcpu_id + 1);
 }
 
 nr_leaves = ARRAY_SIZE(p->leaves);
@@ -1028,3 +976,89 @@ bool xc_cpu_policy_is_compatible(xc_interface *xch, 
xc_cpu_policy_t *host,
 
 return false;
 }
+
+static uint32_t order(uint32_t n)
+{
+return 32 - __builtin_clz(n);
+}
+
+void xc_topo_from_parts(struct cpu_policy *p,
+uint32_t threads_per_core, uint32_t cores_per_pkg)
+{
+uint32_t