Message ID | 20240227103231.1556302-12-zhao1.liu@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Introduce smp.modules for x86 in QEMU | expand |
On 2/27/2024 6:32 PM, Zhao Liu wrote: > From: Zhao Liu <zhao1.liu@intel.com> > > At present, the subleaf 0x02 of CPUID[0x1F] is bound to the "die" level. > > In fact, the specific topology level exposed in 0x1F depends on the > platform's support for extension levels (module, tile and die). > > To help expose "module" level in 0x1F, decouple CPUID[0x1F] subleaf > with specific topology level. > > Tested-by: Yongwei Ma <yongwei.ma@intel.com> > Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com> Besides, some nits below. > --- > Changes since v7: > * Refactored the encode_topo_cpuid1f() to use traversal to search the > encoded level and avoid using static variables. (Xiaoyao) > - Since the total number of levels in the bitmap is not too large, > the overhead of traversing is supposed to be acceptable. > * Renamed the variable num_cpus_next_level to num_threads_next_level. > (Xiaoyao) > * Renamed the helper num_cpus_by_topo_level() to > num_threads_by_topo_level(). (Xiaoyao) > * Dropped Michael/Babu's Acked/Tested tags since the code change. > * Re-added Yongwei's Tested tag For his re-testing. > > Changes since v3: > * New patch to prepare to expose module level in 0x1F. > * Moved the CPUTopoLevel enumeration definition from "i386: Add cache > topology info in CPUCacheInfo" to this patch. Note, to align with > topology types in SDM, revert the name of CPU_TOPO_LEVEL_UNKNOW to > CPU_TOPO_LEVEL_INVALID. > --- > target/i386/cpu.c | 138 +++++++++++++++++++++++++++++++++++++--------- > 1 file changed, 113 insertions(+), 25 deletions(-) > > diff --git a/target/i386/cpu.c b/target/i386/cpu.c > index 88dffd2b52e3..b0f171c6a465 100644 > --- a/target/i386/cpu.c > +++ b/target/i386/cpu.c > @@ -269,6 +269,118 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache, > (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); > } > > +static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, > + enum CPUTopoLevel topo_level) > +{ > + switch (topo_level) { > + case CPU_TOPO_LEVEL_SMT: > + return 1; > + case CPU_TOPO_LEVEL_CORE: > + return topo_info->threads_per_core; > + case CPU_TOPO_LEVEL_DIE: > + return topo_info->threads_per_core * topo_info->cores_per_die; > + case CPU_TOPO_LEVEL_PACKAGE: > + return topo_info->threads_per_core * topo_info->cores_per_die * > + topo_info->dies_per_pkg; > + default: > + g_assert_not_reached(); > + } > + return 0; > +} > + > +static uint32_t apicid_offset_by_topo_level(X86CPUTopoInfo *topo_info, > + enum CPUTopoLevel topo_level) > +{ > + switch (topo_level) { > + case CPU_TOPO_LEVEL_SMT: > + return 0; > + case CPU_TOPO_LEVEL_CORE: > + return apicid_core_offset(topo_info); > + case CPU_TOPO_LEVEL_DIE: > + return apicid_die_offset(topo_info); > + case CPU_TOPO_LEVEL_PACKAGE: > + return apicid_pkg_offset(topo_info); > + default: > + g_assert_not_reached(); > + } > + return 0; > +} > + > +static uint32_t cpuid1f_topo_type(enum CPUTopoLevel topo_level) > +{ > + switch (topo_level) { > + case CPU_TOPO_LEVEL_INVALID: > + return CPUID_1F_ECX_TOPO_LEVEL_INVALID; > + case CPU_TOPO_LEVEL_SMT: > + return CPUID_1F_ECX_TOPO_LEVEL_SMT; > + case CPU_TOPO_LEVEL_CORE: > + return CPUID_1F_ECX_TOPO_LEVEL_CORE; > + case CPU_TOPO_LEVEL_DIE: > + return CPUID_1F_ECX_TOPO_LEVEL_DIE; > + default: > + /* Other types are not supported in QEMU. */ > + g_assert_not_reached(); > + } > + return 0; > +} > + > +static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count, > + X86CPUTopoInfo *topo_info, > + uint32_t *eax, uint32_t *ebx, > + uint32_t *ecx, uint32_t *edx) > +{ > + X86CPU *cpu = env_archcpu(env); > + unsigned long level; > + uint32_t num_threads_next_level, offset_next_level; > + > + assert(count + 1 < CPU_TOPO_LEVEL_MAX); > + > + /* > + * Find the No.count topology levels in avail_cpu_topo bitmap. > + * Start from bit 0 (CPU_TOPO_LEVEL_INVALID). AFAICS, it starts from bit 1 (CPU_TOPO_LEVEL_SMT). Because the initial value of level is CPU_TOPO_LEVEL_INVALID, but the first round of the loop is to find the valid bit starting from (level + 1). > + */ > + level = CPU_TOPO_LEVEL_INVALID; > + for (int i = 0; i <= count; i++) { > + level = find_next_bit(env->avail_cpu_topo, > + CPU_TOPO_LEVEL_PACKAGE, > + level + 1); > + > + /* > + * CPUID[0x1f] doesn't explicitly encode the package level, > + * and it just encode the invalid level (all fields are 0) > + * into the last subleaf of 0x1f. > + */ QEMU will never set bit CPU_TOPO_LEVEL_PACKAGE in env->avail_cpu_topo. So I think we should assert() it instead of fixing it silently. > + if (level == CPU_TOPO_LEVEL_PACKAGE) { > + level = CPU_TOPO_LEVEL_INVALID; > + break; > + } > + } > + > + if (level == CPU_TOPO_LEVEL_INVALID) { > + num_threads_next_level = 0; > + offset_next_level = 0; > + } else { > + unsigned long next_level; please define it at the beginning of the function. e.g., > + next_level = find_next_bit(env->avail_cpu_topo, > + CPU_TOPO_LEVEL_PACKAGE, > + level + 1); > + num_threads_next_level = num_threads_by_topo_level(topo_info, > + next_level); > + offset_next_level = apicid_offset_by_topo_level(topo_info, > + next_level); > + } > + > + *eax = offset_next_level; > + *ebx = num_threads_next_level; > + *ebx &= 0xffff; /* The count doesn't need to be reliable. */ we can combine them together. e.g., *ebx = num_threads_next_level & 0xffff; /* ... */ > + *ecx = count & 0xff; > + *ecx |= cpuid1f_topo_type(level) << 8; Ditto, *ecx = count & 0xff | cpuid1f_topo_type(level) << 8; > + *edx = cpu->apic_id; > + > + assert(!(*eax & ~0x1f)); > +} > + > /* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */ > static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) > { > @@ -6287,31 +6399,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, > break; > } > > - *ecx = count & 0xff; > - *edx = cpu->apic_id; > - switch (count) { > - case 0: > - *eax = apicid_core_offset(&topo_info); > - *ebx = topo_info.threads_per_core; > - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_SMT << 8; > - break; > - case 1: > - *eax = apicid_die_offset(&topo_info); > - *ebx = topo_info.cores_per_die * topo_info.threads_per_core; > - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_CORE << 8; > - break; > - case 2: > - *eax = apicid_pkg_offset(&topo_info); > - *ebx = threads_per_pkg; > - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_DIE << 8; > - break; > - default: > - *eax = 0; > - *ebx = 0; > - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_INVALID << 8; > - } > - assert(!(*eax & ~0x1f)); > - *ebx &= 0xffff; /* The count doesn't need to be reliable. */ > + encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx); > break; > case 0xD: { > /* Processor Extended State */
On Mon, Mar 11, 2024 at 04:45:41PM +0800, Xiaoyao Li wrote: > Date: Mon, 11 Mar 2024 16:45:41 +0800 > From: Xiaoyao Li <xiaoyao.li@intel.com> > Subject: Re: [PATCH v9 11/21] i386/cpu: Decouple CPUID[0x1F] subleaf with > specific topology level > > On 2/27/2024 6:32 PM, Zhao Liu wrote: > > From: Zhao Liu <zhao1.liu@intel.com> > > > > At present, the subleaf 0x02 of CPUID[0x1F] is bound to the "die" level. > > > > In fact, the specific topology level exposed in 0x1F depends on the > > platform's support for extension levels (module, tile and die). > > > > To help expose "module" level in 0x1F, decouple CPUID[0x1F] subleaf > > with specific topology level. > > > > Tested-by: Yongwei Ma <yongwei.ma@intel.com> > > Signed-off-by: Zhao Liu <zhao1.liu@intel.com> > > Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com> Thanks! > Besides, some nits below. > [snip] > > +static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count, > > + X86CPUTopoInfo *topo_info, > > + uint32_t *eax, uint32_t *ebx, > > + uint32_t *ecx, uint32_t *edx) > > +{ > > + X86CPU *cpu = env_archcpu(env); > > + unsigned long level; > > + uint32_t num_threads_next_level, offset_next_level; > > + > > + assert(count + 1 < CPU_TOPO_LEVEL_MAX); > > + > > + /* > > + * Find the No.count topology levels in avail_cpu_topo bitmap. > > + * Start from bit 0 (CPU_TOPO_LEVEL_INVALID). > > AFAICS, it starts from bit 1 (CPU_TOPO_LEVEL_SMT). Because the initial value > of level is CPU_TOPO_LEVEL_INVALID, but the first round of the loop is to > find the valid bit starting from (level + 1). Yes, this description is much clearer. > > + */ > > + level = CPU_TOPO_LEVEL_INVALID; > > + for (int i = 0; i <= count; i++) { > > + level = find_next_bit(env->avail_cpu_topo, > > + CPU_TOPO_LEVEL_PACKAGE, > > + level + 1); > > + > > + /* > > + * CPUID[0x1f] doesn't explicitly encode the package level, > > + * and it just encode the invalid level (all fields are 0) > > + * into the last subleaf of 0x1f. > > + */ > > QEMU will never set bit CPU_TOPO_LEVEL_PACKAGE in env->avail_cpu_topo. In the patch 9 [1], I set the CPU_TOPO_LEVEL_PACKAGE in bitmap. This level is a basic topology level in general, so it's worth being set. Only in Intel's 0x1F, it doesn't have a corresponding type, and where I use it as a termination condition for 0x1F encoding (not an error case). [1]: https://lore.kernel.org/qemu-devel/20240227103231.1556302-10-zhao1.liu@linux.intel.com/ > So I think we should assert() it instead of fixing it silently. > > > + if (level == CPU_TOPO_LEVEL_PACKAGE) { > > + level = CPU_TOPO_LEVEL_INVALID; > > + break; > > + } > > + } > > + > > + if (level == CPU_TOPO_LEVEL_INVALID) { > > + num_threads_next_level = 0; > > + offset_next_level = 0; > > + } else { > > + unsigned long next_level; > > please define it at the beginning of the function. e.g., Okay, I'll put the declaration of "next_level" at the beginning of this function with a current variable "level". > > > + next_level = find_next_bit(env->avail_cpu_topo, > > + CPU_TOPO_LEVEL_PACKAGE, > > + level + 1); > > + num_threads_next_level = num_threads_by_topo_level(topo_info, > > + next_level); > > + offset_next_level = apicid_offset_by_topo_level(topo_info, > > + next_level); > > + } > > + > > + *eax = offset_next_level; > > + *ebx = num_threads_next_level; > > + *ebx &= 0xffff; /* The count doesn't need to be reliable. */ > > we can combine them together. e.g., > > *ebx = num_threads_next_level & 0xffff; /* ... */ > > > + *ecx = count & 0xff; > > + *ecx |= cpuid1f_topo_type(level) << 8; > > Ditto, > > *ecx = count & 0xff | cpuid1f_topo_type(level) << 8; OK, will combine these. > > + *edx = cpu->apic_id; > > + > > + assert(!(*eax & ~0x1f)); > > +} > > +
diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 88dffd2b52e3..b0f171c6a465 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -269,6 +269,118 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache, (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); } +static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, + enum CPUTopoLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPO_LEVEL_SMT: + return 1; + case CPU_TOPO_LEVEL_CORE: + return topo_info->threads_per_core; + case CPU_TOPO_LEVEL_DIE: + return topo_info->threads_per_core * topo_info->cores_per_die; + case CPU_TOPO_LEVEL_PACKAGE: + return topo_info->threads_per_core * topo_info->cores_per_die * + topo_info->dies_per_pkg; + default: + g_assert_not_reached(); + } + return 0; +} + +static uint32_t apicid_offset_by_topo_level(X86CPUTopoInfo *topo_info, + enum CPUTopoLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPO_LEVEL_SMT: + return 0; + case CPU_TOPO_LEVEL_CORE: + return apicid_core_offset(topo_info); + case CPU_TOPO_LEVEL_DIE: + return apicid_die_offset(topo_info); + case CPU_TOPO_LEVEL_PACKAGE: + return apicid_pkg_offset(topo_info); + default: + g_assert_not_reached(); + } + return 0; +} + +static uint32_t cpuid1f_topo_type(enum CPUTopoLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPO_LEVEL_INVALID: + return CPUID_1F_ECX_TOPO_LEVEL_INVALID; + case CPU_TOPO_LEVEL_SMT: + return CPUID_1F_ECX_TOPO_LEVEL_SMT; + case CPU_TOPO_LEVEL_CORE: + return CPUID_1F_ECX_TOPO_LEVEL_CORE; + case CPU_TOPO_LEVEL_DIE: + return CPUID_1F_ECX_TOPO_LEVEL_DIE; + default: + /* Other types are not supported in QEMU. */ + g_assert_not_reached(); + } + return 0; +} + +static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count, + X86CPUTopoInfo *topo_info, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + X86CPU *cpu = env_archcpu(env); + unsigned long level; + uint32_t num_threads_next_level, offset_next_level; + + assert(count + 1 < CPU_TOPO_LEVEL_MAX); + + /* + * Find the No.count topology levels in avail_cpu_topo bitmap. + * Start from bit 0 (CPU_TOPO_LEVEL_INVALID). + */ + level = CPU_TOPO_LEVEL_INVALID; + for (int i = 0; i <= count; i++) { + level = find_next_bit(env->avail_cpu_topo, + CPU_TOPO_LEVEL_PACKAGE, + level + 1); + + /* + * CPUID[0x1f] doesn't explicitly encode the package level, + * and it just encode the invalid level (all fields are 0) + * into the last subleaf of 0x1f. + */ + if (level == CPU_TOPO_LEVEL_PACKAGE) { + level = CPU_TOPO_LEVEL_INVALID; + break; + } + } + + if (level == CPU_TOPO_LEVEL_INVALID) { + num_threads_next_level = 0; + offset_next_level = 0; + } else { + unsigned long next_level; + + next_level = find_next_bit(env->avail_cpu_topo, + CPU_TOPO_LEVEL_PACKAGE, + level + 1); + num_threads_next_level = num_threads_by_topo_level(topo_info, + next_level); + offset_next_level = apicid_offset_by_topo_level(topo_info, + next_level); + } + + *eax = offset_next_level; + *ebx = num_threads_next_level; + *ebx &= 0xffff; /* The count doesn't need to be reliable. */ + *ecx = count & 0xff; + *ecx |= cpuid1f_topo_type(level) << 8; + *edx = cpu->apic_id; + + assert(!(*eax & ~0x1f)); +} + /* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */ static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) { @@ -6287,31 +6399,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } - *ecx = count & 0xff; - *edx = cpu->apic_id; - switch (count) { - case 0: - *eax = apicid_core_offset(&topo_info); - *ebx = topo_info.threads_per_core; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_SMT << 8; - break; - case 1: - *eax = apicid_die_offset(&topo_info); - *ebx = topo_info.cores_per_die * topo_info.threads_per_core; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_CORE << 8; - break; - case 2: - *eax = apicid_pkg_offset(&topo_info); - *ebx = threads_per_pkg; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_DIE << 8; - break; - default: - *eax = 0; - *ebx = 0; - *ecx |= CPUID_1F_ECX_TOPO_LEVEL_INVALID << 8; - } - assert(!(*eax & ~0x1f)); - *ebx &= 0xffff; /* The count doesn't need to be reliable. */ + encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx); break; case 0xD: { /* Processor Extended State */