arch: x86_64: handle npot CPU topology

This PR addresses a bug in which the cpu topology of a guest
with non power-of-two number of cores is incorrect. For example,
in some contexts, a virtual machine with 2-sockets and 12-cores
will incorrectly believe that 16 cores are on socket 1 and 8
cores are on socket 2. In other cases, common topology enumeration
software such as hwloc will crash.

The root of the problem was the way that cloud-hypervisor generates
apic_id. On x86_64, the (x2) apic_id embeds information about cpu
topology. The cpuid instruction is primarily used to discover the
number of sockets, dies, cores, threads, etc. Using this information,
the (x2) apic_id is masked to determine which {core, die, socket} the
cpu is on. When the cpu topology is not a power of two
(e.g. a 12-core machine), this requires non-contiguous (x2) apic_id.

Signed-off-by: Thomas Barrett <tbarrett@crusoeenergy.com>
This commit is contained in:
Thomas Barrett
2023-12-22 03:38:13 +00:00
committed by Rob Bradford
parent a2ceb00fbc
commit 5c0b66529a
6 changed files with 164 additions and 34 deletions
+22 -3
View File
@@ -277,7 +277,10 @@ fn create_tpm2_table() -> Sdt {
tpm
}
fn create_srat_table(numa_nodes: &NumaNodes) -> Sdt {
fn create_srat_table(
numa_nodes: &NumaNodes,
#[cfg(target_arch = "x86_64")] topology: Option<(u8, u8, u8)>,
) -> Sdt {
let mut srat = Sdt::new(*b"SRAT", 36, 3, *b"CLOUDH", *b"CHSRAT ", 1);
// SRAT reserved 12 bytes
srat.append_slice(&[0u8; 12]);
@@ -316,6 +319,9 @@ fn create_srat_table(numa_nodes: &NumaNodes) -> Sdt {
}
for cpu in &node.cpus {
#[cfg(target_arch = "x86_64")]
let x2apic_id = arch::x86_64::get_x2apic_id(*cpu as u32, topology);
#[cfg(target_arch = "aarch64")]
let x2apic_id = *cpu as u32;
// Flags
@@ -752,8 +758,14 @@ pub fn create_acpi_tables(
// SRAT and SLIT
// Only created if the NUMA nodes list is not empty.
if !numa_nodes.is_empty() {
#[cfg(target_arch = "x86_64")]
let topology = cpu_manager.lock().unwrap().get_vcpu_topology();
// SRAT
let srat = create_srat_table(numa_nodes);
let srat = create_srat_table(
numa_nodes,
#[cfg(target_arch = "x86_64")]
topology,
);
let srat_offset = prev_tbl_off.checked_add(prev_tbl_len).unwrap();
guest_mem
.write_slice(srat.as_slice(), srat_offset)
@@ -851,8 +863,15 @@ pub fn create_acpi_tables_tdx(
// SRAT and SLIT
// Only created if the NUMA nodes list is not empty.
if !numa_nodes.is_empty() {
#[cfg(target_arch = "x86_64")]
let topology = cpu_manager.lock().unwrap().get_vcpu_topology();
// SRAT
tables.push(create_srat_table(numa_nodes));
tables.push(create_srat_table(
numa_nodes,
#[cfg(target_arch = "x86_64")]
topology,
));
// SLIT
tables.push(create_slit_table(numa_nodes));