On Mon, Mar 09, 2020 at 05:00:37PM +1000, David Gwynne wrote:
> ive been running multi-cpu/core openbsd VMs on esxi on top of amd
> epyc cpus, and have noticed for a long time now that for some reason
> openbsd decides that all the virtual cpus are threads on the one core.
> this is annoying, cos setting hw.smt=1 feels dirty and wrong.
>
> i spent the weekend figuring this out, and came up with the following.
>
> our current code assumes that some cpuid fields on recent CPUs contain
> information about package and core topologies which we base the package
> and core ids on. turns out we're pretty much alone in this assumption. if
> you're running on real hardware, they do tend to contain useful info,
> but virtual machines don't fill them in properly.
>
> every other operating system seems to rely on the one mechanism across
> all families. specifically, the initial local apic id provides a
> globally unique identifier that has bits representing at least the
> package (socket) the logical thread is on, plus the core, and if
> supported, the smt id. multi socket cpus provide information about
> how many bits there are before you get to the ones identifying the
> package, so we use that everywhere.
>
> only the most recent generation of cpu (zen) supports SMT, but the cpuid
> that provides that information is available on at least the previous
> gen. fortunately they made it so reading the number of threads per
> core falls back gracefully. this means we can default to there being
> one thread per core, and increase that if the cpuid is available and
> appropriately set.
>
> this seems to fix my vmware problem, but also still seems to work on my
> physical epyc boxes. unfortunately i do not have any other amd systems
> up and running at the moment, so i would appreciate testing on any and
> every amd based amd64 system.
>
> tests please. ok?
of course hrvoje found a bug. i cleaned up my diff too hard before
sending it out, and ended up reading nthreads from the wrong bits.
this works better on his epyc 2 box, and works right on my epyc 1, esxi
on epyc 1, and on an apu1.
Index: identcpu.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/identcpu.c,v
retrieving revision 1.113
diff -u -p -r1.113 identcpu.c
--- identcpu.c 14 Jun 2019 18:13:55 -0000 1.113
+++ identcpu.c 9 Mar 2020 12:38:59 -0000
@@ -824,37 +824,31 @@ cpu_topology(struct cpu_info *ci)
apicid = (ebx >> 24) & 0xff;
if (strcmp(cpu_vendor, "AuthenticAMD") == 0) {
+ uint32_t nthreads = 1; /* per core */
+ uint32_t thread_id; /* within a package */
+
/* We need at least apicid at CPUID 0x80000008 */
if (ci->ci_pnfeatset < 0x80000008)
goto no_topology;
- if (ci->ci_pnfeatset >= 0x8000001e) {
- struct cpu_info *ci_other;
- CPU_INFO_ITERATOR cii;
+ CPUID(0x80000008, eax, ebx, ecx, edx);
+ core_bits = (ecx >> 12) & 0xf;
+ if (ci->ci_pnfeatset >= 0x8000001e) {
CPUID(0x8000001e, eax, ebx, ecx, edx);
- ci->ci_core_id = ebx & 0xff;
- ci->ci_pkg_id = ecx & 0xff;
- ci->ci_smt_id = 0;
- CPU_INFO_FOREACH(cii, ci_other) {
- if (ci != ci_other &&
- ci_other->ci_core_id == ci->ci_core_id &&
- ci_other->ci_pkg_id == ci->ci_pkg_id)
- ci->ci_smt_id++;
- }
- } else {
- CPUID(0x80000008, eax, ebx, ecx, edx);
- core_bits = (ecx >> 12) & 0xf;
- if (core_bits == 0)
- goto no_topology;
- /* So coreidsize 2 gives 3, 3 gives 7... */
- core_mask = (1 << core_bits) - 1;
- /* Core id is the least significant considering mask */
- ci->ci_core_id = apicid & core_mask;
- /* Pkg id is the upper remaining bits */
- ci->ci_pkg_id = apicid & ~core_mask;
- ci->ci_pkg_id >>= core_bits;
+ nthreads = ((ebx >> 8) & 0xf) + 1;
}
+
+ /* Shift the core_bits off to get at the pkg bits */
+ ci->ci_pkg_id = apicid >> core_bits;
+
+ /* Get rid of the package bits */
+ core_mask = (1 << core_bits) - 1;
+ thread_id = apicid & core_mask;
+
+ /* Cut logical thread_id into core id, and smt id in a core */
+ ci->ci_core_id = thread_id / nthreads;
+ ci->ci_smt_id = thread_id % nthreads;
} else if (strcmp(cpu_vendor, "GenuineIntel") == 0) {
/* We only support leaf 1/4 detection */
if (cpuid_level < 4)