From 6f62cd060a773760da856c0ae1e4bd582a4f5ad7 Mon Sep 17 00:00:00 2001 From: Walter Wang Date: Thu, 6 Aug 2026 20:42:01 +0000 Subject: [PATCH 1/4] aarch64: describe CPU topology to guests via cpu-map FDT node Emit a minimal cpu-map -- one cluster containing N cores -- and give each cpu node an explicit phandle so the cluster/core entries can reference it. Signed-off-by: Walter Wang --- src/vmm/src/arch/aarch64/fdt.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/vmm/src/arch/aarch64/fdt.rs b/src/vmm/src/arch/aarch64/fdt.rs index 74791d2b689..5532ee45e60 100644 --- a/src/vmm/src/arch/aarch64/fdt.rs +++ b/src/vmm/src/arch/aarch64/fdt.rs @@ -38,6 +38,12 @@ const MSI_PHANDLE: u32 = 3; // So, we start the indexing of the phandles used from a really big number and then subtract from // it as we need more and more phandle for each cache representation. const LAST_CACHE_PHANDLE: u32 = 4000; + +// Phandle base for cpu@N nodes. Placed above LAST_CACHE_PHANDLE so the cpu +// phandle range never collides with the cache phandles (which count DOWN +// from LAST_CACHE_PHANDLE). Used by /cpus/cpu-map/cluster*/core*/cpu +// references for per-core distinct CPUs visible in the guest. +const CPU_PHANDLE_BASE: u32 = 4001; // Read the documentation specified when appending the root node to the FDT. const ADDRESS_CELLS: u32 = 0x2; const SIZE_CELLS: u32 = 0x2; @@ -139,6 +145,12 @@ fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64]) -> Result<(), FdtEr // Set the field to first 24 bits of the MPIDR - Multiprocessor Affinity Register. // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABHBJCI.html. fdt.property_u64("reg", mpidr & 0x7FFFFF)?; + // Phandle so the /cpus/cpu-map nodes (emitted after this loop) can + // reference this cpu via `cpu = <&cpuN>`. Without an explicit + // phandle, the cpu-map references would not resolve and the Linux + // FDT topology parser would skip the cpu-map entirely. + let cpu_phandle = CPU_PHANDLE_BASE + u32::try_from(cpu_index).unwrap(); + fdt.property_u32("phandle", cpu_phandle)?; for cache in l1_caches.iter() { // Please check out @@ -215,6 +227,22 @@ fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64]) -> Result<(), FdtEr fdt.end_node(cpu)?; } + // Emit /cpus/cpu-map so the guest has an explicit cluster/core topology. + // Minimal viable shape: single cluster containing N cores. Mirrors + // crosvm/aarch64/src/fdt.rs::create_cpu_nodes. A real multi-cluster layout + // (matching host SoC topology) can be a follow-up once a config knob exists. + // + // Binding: Linux Documentation/devicetree/bindings/cpu/cpu-topology.txt + let cpu_map = fdt.begin_node("cpu-map")?; + let cluster0 = fdt.begin_node("cluster0")?; + for cpu_index in 0..num_cpus { + let core = fdt.begin_node(&format!("core{cpu_index}"))?; + let cpu_phandle = CPU_PHANDLE_BASE + u32::try_from(cpu_index).unwrap(); + fdt.property_u32("cpu", cpu_phandle)?; + fdt.end_node(core)?; + } + fdt.end_node(cluster0)?; + fdt.end_node(cpu_map)?; fdt.end_node(cpus)?; Ok(()) From 399d67ac4f64da158069c6a103c494b45310e0f1 Mon Sep 17 00:00:00 2001 From: Nathan Chen Date: Tue, 25 Aug 2026 21:53:33 +0000 Subject: [PATCH 2/4] aarch64: describe SMT thread siblings in the cpu-map node Extend the cpu-map node so that, when SMT is enabled, consecutive vCPUs (2*i, 2*i+1) are emitted as the two `thread` nodes of core `i` instead of as two independent cores. arm64 Linux derives thread siblings and "Thread(s) per core" purely from the device tree: parse_core() in drivers/base/arch_topology.c reads the `thread%d` child nodes, and store_cpu_topology() -- the fallback used when no cpu-map is present -- hardcodes thread_id to -1. MPIDR_EL1 is not consulted, so the cpu-map node alone is sufficient to expose SMT and no vCPU register state needs to change. With SMT disabled, or with a single vCPU, the emitted tree is unchanged. Signed-off-by: Nathan Chen --- src/vmm/src/arch/aarch64/fdt.rs | 81 ++++++++++++++++++++++++++++++--- src/vmm/src/arch/aarch64/mod.rs | 1 + 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/vmm/src/arch/aarch64/fdt.rs b/src/vmm/src/arch/aarch64/fdt.rs index 5532ee45e60..2fafd9b39e8 100644 --- a/src/vmm/src/arch/aarch64/fdt.rs +++ b/src/vmm/src/arch/aarch64/fdt.rs @@ -74,6 +74,7 @@ pub enum FdtError { pub fn create_fdt( guest_mem: &GuestMemoryMmap, vcpu_mpidr: Vec, + smt: bool, cmdline: CString, device_manager: &DeviceManager, gic_device: &GICDevice, @@ -96,7 +97,7 @@ pub fn create_fdt( // This is not mandatory but we use it to point the root node to the node // containing description of the interrupt controller for this VM. fdt_writer.property_u32("interrupt-parent", GIC_PHANDLE)?; - create_cpu_nodes(&mut fdt_writer, &vcpu_mpidr)?; + create_cpu_nodes(&mut fdt_writer, &vcpu_mpidr, smt)?; create_memory_node(&mut fdt_writer, guest_mem)?; create_chosen_node(&mut fdt_writer, cmdline, initrd)?; create_gic_node(&mut fdt_writer, gic_device)?; @@ -119,7 +120,7 @@ pub fn create_fdt( } // Following are the auxiliary function for creating the different nodes that we append to our FDT. -fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64]) -> Result<(), FdtError> { +fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64], smt: bool) -> Result<(), FdtError> { // Since the L1 caches are not shareable among CPUs and they are direct attributes of the // cpu in the device tree, we process the L1 and non-L1 caches separately. // We use sysfs for extracting the cache information. @@ -233,12 +234,24 @@ fn create_cpu_nodes(fdt: &mut FdtWriter, vcpu_mpidr: &[u64]) -> Result<(), FdtEr // (matching host SoC topology) can be a follow-up once a config knob exists. // // Binding: Linux Documentation/devicetree/bindings/cpu/cpu-topology.txt + let threads_per_core = if smt && num_cpus > 1 { 2 } else { 1 }; + let num_cores = num_cpus / threads_per_core; + let cpu_map = fdt.begin_node("cpu-map")?; let cluster0 = fdt.begin_node("cluster0")?; - for cpu_index in 0..num_cpus { - let core = fdt.begin_node(&format!("core{cpu_index}"))?; - let cpu_phandle = CPU_PHANDLE_BASE + u32::try_from(cpu_index).unwrap(); - fdt.property_u32("cpu", cpu_phandle)?; + for core_index in 0..num_cores { + let core = fdt.begin_node(&format!("core{core_index}"))?; + if threads_per_core > 1 { + for thread_index in 0..threads_per_core { + let cpu_index = core_index * threads_per_core + thread_index; + let thread = fdt.begin_node(&format!("thread{thread_index}"))?; + fdt.property_u32("cpu", CPU_PHANDLE_BASE + u32::try_from(cpu_index).unwrap())?; + fdt.end_node(thread)?; + } + } else { + let cpu_phandle = CPU_PHANDLE_BASE + u32::try_from(core_index).unwrap(); + fdt.property_u32("cpu", cpu_phandle)?; + } fdt.end_node(core)?; } fdt.end_node(cluster0)?; @@ -595,6 +608,61 @@ mod tests { use crate::vstate::memory::GuestAddress; use crate::{EventManager, Kvm}; + #[test] + fn test_create_cpu_nodes_without_smt() { + // MPIDR values as KVM defaults them: Aff0 holds the vcpu index. + let mpidrs = [0x8000_0000, 0x8000_0001]; + let mut fdt = FdtWriter::new().unwrap(); + let root = fdt.begin_node("").unwrap(); + create_cpu_nodes(&mut fdt, &mpidrs, false).unwrap(); + fdt.end_node(root).unwrap(); + + let generated_fdt = device_tree::DeviceTree::load(&fdt.finish().unwrap()).unwrap(); + + for cpu_index in 0..mpidrs.len() { + let phandle = CPU_PHANDLE_BASE + u32::try_from(cpu_index).unwrap(); + let cpu = generated_fdt + .find(&format!("/cpus/cpu@{cpu_index:x}")) + .unwrap(); + assert_eq!(cpu.prop_u32("phandle").unwrap(), phandle); + + // Each core references its cpu directly, with no thread nodes in between. + let core = generated_fdt + .find(&format!("/cpus/cpu-map/cluster0/core{cpu_index}")) + .unwrap(); + assert_eq!(core.prop_u32("cpu").unwrap(), phandle); + } + } + + #[test] + fn test_create_cpu_nodes_with_smt() { + let mpidrs = [0x8000_0000, 0x8000_0001, 0x8000_0002, 0x8000_0003]; + let mut fdt = FdtWriter::new().unwrap(); + let root = fdt.begin_node("").unwrap(); + create_cpu_nodes(&mut fdt, &mpidrs, true).unwrap(); + fdt.end_node(root).unwrap(); + + let generated_fdt = device_tree::DeviceTree::load(&fdt.finish().unwrap()).unwrap(); + + // Consecutive vCPUs are the two thread siblings of a core. + for cpu_index in 0..mpidrs.len() { + let phandle = CPU_PHANDLE_BASE + u32::try_from(cpu_index).unwrap(); + let cpu = generated_fdt + .find(&format!("/cpus/cpu@{cpu_index:x}")) + .unwrap(); + assert_eq!(cpu.prop_u32("phandle").unwrap(), phandle); + + let thread = generated_fdt + .find(&format!( + "/cpus/cpu-map/cluster0/core{}/thread{}", + cpu_index / 2, + cpu_index % 2 + )) + .unwrap(); + assert_eq!(thread.prop_u32("cpu").unwrap(), phandle); + } + } + #[test] fn test_create_fdt() { let mem = arch_mem(FDT_MAX_SIZE + 0x1000); @@ -629,6 +697,7 @@ mod tests { let dtb_bytes = create_fdt( &mem, vec![0], + false, CString::new("console=tty0").unwrap(), &device_manager, &gic, diff --git a/src/vmm/src/arch/aarch64/mod.rs b/src/vmm/src/arch/aarch64/mod.rs index b989d41980a..fbcbfe4d6c7 100644 --- a/src/vmm/src/arch/aarch64/mod.rs +++ b/src/vmm/src/arch/aarch64/mod.rs @@ -139,6 +139,7 @@ pub fn configure_system_for_boot( let fdt = fdt::create_fdt( vm.guest_memory(), vcpu_mpidr, + vcpu_config.smt, cmdline, device_manager, vm.get_irqchip(), From 924e14de4e72d4736d4c531d478f6a62f48dc991 Mon Sep 17 00:00:00 2001 From: Nathan Chen Date: Thu, 9 Jul 2026 04:20:38 +0000 Subject: [PATCH 3/4] vmm_config: allow enabling SMT on aarch64 Remove the aarch64-specific rejection of smt: true in machine-config updates. The same validation rules as x86_64 now apply: vcpu_count must be 1 or even when SMT is enabled. Update the affected tests, API documentation, and changelog accordingly. Signed-off-by: Nathan Chen --- CHANGELOG.md | 1 + .../src/api_server/request/machine_configuration.rs | 10 +++++++++- src/firecracker/swagger/firecracker.yaml | 2 +- src/vmm/src/resources.rs | 10 +--------- src/vmm/src/vmm_config/machine_config.rs | 8 -------- tests/integration_tests/functional/test_api.py | 8 ++------ 6 files changed, 14 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba249ff1db4..864dccb59c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to ### Added +- Added support for configuring SMT topology on aarch64 microVMs. - [#5891](https://github.com/firecracker-microvm/firecracker/pull/5891): Added support for virtio device reset. - [#5983](https://github.com/firecracker-microvm/firecracker/pull/5983): Add two diff --git a/src/firecracker/src/api_server/request/machine_configuration.rs b/src/firecracker/src/api_server/request/machine_configuration.rs index a12326d3a8d..90988d5eab0 100644 --- a/src/firecracker/src/api_server/request/machine_configuration.rs +++ b/src/firecracker/src/api_server/request/machine_configuration.rs @@ -261,7 +261,7 @@ mod tests { }"#; parse_patch_machine_config(&Body::new(body)).unwrap(); - // On aarch64, we allow `smt` to be configured to `false` but not `true`. + // Test that `smt` can be configured to `false`. let body = r#"{ "vcpu_count": 8, "mem_size_mib": 1024, @@ -269,6 +269,14 @@ mod tests { }"#; parse_patch_machine_config(&Body::new(body)).unwrap(); + // Test that `smt` can be configured to `true`. + let body = r#"{ + "vcpu_count": 8, + "mem_size_mib": 1024, + "smt": true + }"#; + parse_patch_machine_config(&Body::new(body)).unwrap(); + // 3. Check to see if an empty body returns an error. let body = r#"{}"#; parse_patch_machine_config(&Body::new(body)).unwrap_err(); diff --git a/src/firecracker/swagger/firecracker.yaml b/src/firecracker/swagger/firecracker.yaml index 5c6a8e28309..b161ac64777 100644 --- a/src/firecracker/swagger/firecracker.yaml +++ b/src/firecracker/swagger/firecracker.yaml @@ -1420,7 +1420,7 @@ definitions: # description: Path to the GDB socket. Requires the gdb feature to be enabled. smt: type: boolean - description: Flag for enabling/disabling simultaneous multithreading. Can be enabled only on x86. + description: Flag for enabling/disabling simultaneous multithreading. default: false mem_size_mib: type: integer diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index dc6c43f57a4..656afd774a6 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -1451,22 +1451,14 @@ mod tests { Err(MachineConfigError::InvalidVcpuCount) ); - // Check that SMT is not supported on aarch64, and that on x86_64 enabling it requires vcpu - // count to be even. + // Check that enabling SMT requires vcpu count to be even. aux_vm_config.smt = Some(true); - #[cfg(target_arch = "aarch64")] - assert_eq!( - vm_resources.update_machine_config(&aux_vm_config), - Err(MachineConfigError::SmtNotSupported) - ); aux_vm_config.vcpu_count = Some(3); - #[cfg(target_arch = "x86_64")] assert_eq!( vm_resources.update_machine_config(&aux_vm_config), Err(MachineConfigError::InvalidVcpuCount) ); aux_vm_config.vcpu_count = Some(32); - #[cfg(target_arch = "x86_64")] vm_resources.update_machine_config(&aux_vm_config).unwrap(); aux_vm_config.smt = Some(false); diff --git a/src/vmm/src/vmm_config/machine_config.rs b/src/vmm/src/vmm_config/machine_config.rs index 4ea804f6fc9..cbf55ab21ab 100644 --- a/src/vmm/src/vmm_config/machine_config.rs +++ b/src/vmm/src/vmm_config/machine_config.rs @@ -24,9 +24,6 @@ pub enum MachineConfigError { InvalidVcpuCount, /// Could not get the configuration of the previously installed balloon device to validate the memory size. InvalidVmState, - /// Enabling simultaneous multithreading is not supported on aarch64. - #[cfg(target_arch = "aarch64")] - SmtNotSupported, /// Could not determine host kernel version when checking hugetlbfs compatibility KernelVersion, } @@ -259,11 +256,6 @@ impl MachineConfig { let smt = update.smt.unwrap_or(self.smt); - #[cfg(target_arch = "aarch64")] - if smt { - return Err(MachineConfigError::SmtNotSupported); - } - if vcpu_count == 0 || vcpu_count > MAX_SUPPORTED_VCPUS { return Err(MachineConfigError::InvalidVcpuCount); } diff --git a/tests/integration_tests/functional/test_api.py b/tests/integration_tests/functional/test_api.py index 4efca607133..21707a46c27 100644 --- a/tests/integration_tests/functional/test_api.py +++ b/tests/integration_tests/functional/test_api.py @@ -15,7 +15,7 @@ import host_tools.drive as drive_tools import host_tools.network as net_tools -from framework import utils, utils_cpuid +from framework import utils from framework.artifacts import GUEST_KERNEL_DEFAULT, pin_guest_kernel from framework.utils import get_firecracker_version_from_toml from framework.utils_cpu_templates import ( @@ -152,7 +152,7 @@ def test_api_put_update_pre_boot(uvm, io_engine): # The machine configuration has a default value, so all PUTs are updates. microvm_config_json = { "vcpu_count": 4, - "smt": platform.machine() == "x86_64", + "smt": True, "mem_size_mib": 256, "track_dirty_pages": True, } @@ -1291,7 +1291,6 @@ def test_get_full_config_after_restoring_snapshot(microvm_factory, uvm_configure Test the configuration of a microVM after restoring from a snapshot. """ net_iface = uvm_configured.add_net_iface() - cpu_vendor = utils_cpuid.get_cpu_vendor() setup_cfg = {} # Basic config also implies a root block device. @@ -1303,9 +1302,6 @@ def test_get_full_config_after_restoring_snapshot(microvm_factory, uvm_configure "huge_pages": "None", } - if cpu_vendor == utils_cpuid.CpuVendor.ARM: - setup_cfg["machine-config"]["smt"] = False - if len(SUPPORTED_CPU_TEMPLATES) != 0: setup_cfg["machine-config"]["cpu_template"] = SUPPORTED_CPU_TEMPLATES[0] From 15d01588aaed60ebe0397de1582750943eaf8b84 Mon Sep 17 00:00:00 2001 From: Nathan Chen Date: Fri, 7 Aug 2026 01:12:47 +0000 Subject: [PATCH 4/4] tests: enable aarch64 SMT topology coverage Update integration tests to verify SMT can be configured on aarch64 and that guest CPU topology reflects the configured thread count. Add focused 2-vCPU and 4-vCPU cases that assert two threads per core. Signed-off-by: Nathan Chen --- .../integration_tests/functional/test_api.py | 11 +---- .../functional/test_cmd_line_start.py | 9 ++-- .../functional/test_topology.py | 49 +++++++++++++++---- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/tests/integration_tests/functional/test_api.py b/tests/integration_tests/functional/test_api.py index 21707a46c27..f893c705c9d 100644 --- a/tests/integration_tests/functional/test_api.py +++ b/tests/integration_tests/functional/test_api.py @@ -348,15 +348,8 @@ def test_api_machine_config(uvm): response = test_microvm.api.machine_config.get() assert response.json()["smt"] is False - # Test that smt=True errors on ARM. - if platform.machine() == "x86_64": - test_microvm.api.machine_config.patch(smt=True) - elif platform.machine() == "aarch64": - expected_msg = ( - "Enabling simultaneous multithreading is not supported on aarch64" - ) - with pytest.raises(RuntimeError, match=expected_msg): - test_microvm.api.machine_config.patch(smt=True) + # Test that smt=True is accepted. + test_microvm.api.machine_config.patch(smt=True) # Test invalid mem_size_mib < 0. with pytest.raises(RuntimeError): diff --git a/tests/integration_tests/functional/test_cmd_line_start.py b/tests/integration_tests/functional/test_cmd_line_start.py index c39081e1004..50b2ba64abb 100644 --- a/tests/integration_tests/functional/test_cmd_line_start.py +++ b/tests/integration_tests/functional/test_cmd_line_start.py @@ -4,7 +4,6 @@ import json import os -import platform import re import shutil from pathlib import Path @@ -204,8 +203,8 @@ def test_config_bad_machine_config(uvm, vm_config_file): @pytest.mark.parametrize( "test_config", [ - ("framework/vm_config_cpu_template_C3.json", True, False), - ("framework/vm_config_smt_true.json", False, True), + ("framework/vm_config_cpu_template_C3.json", True), + ("framework/vm_config_smt_true.json", False), ], ) def test_config_machine_config_params(uvm, test_config): @@ -216,7 +215,7 @@ def test_config_machine_config_params(uvm, test_config): # Test configuration determines if the file is a valid config or not # based on the CPU - vm_config_file, cpu_template_used, smt_used = test_config + vm_config_file, cpu_template_used = test_config _configure_vm_from_json(test_microvm, vm_config_file) test_microvm.jailer.extra_args.update({"no-api": None}) @@ -226,8 +225,6 @@ def test_config_machine_config_params(uvm, test_config): should_fail = False if cpu_template_used and "C3" not in SUPPORTED_CPU_TEMPLATES: should_fail = True - if smt_used and (platform.machine() == "aarch64"): - should_fail = True if should_fail: test_microvm.check_any_log_message( diff --git a/tests/integration_tests/functional/test_topology.py b/tests/integration_tests/functional/test_topology.py index af441da167d..64c9e51751c 100644 --- a/tests/integration_tests/functional/test_topology.py +++ b/tests/integration_tests/functional/test_topology.py @@ -9,6 +9,7 @@ from packaging import version import framework.utils_cpuid as utils +from framework.artifacts import GUEST_KERNEL_DEFAULT, pin_guest_kernel from framework.properties import global_props from framework.utils import get_kernel_version @@ -35,7 +36,7 @@ def _check_cpu_topology( expected_lscpu_output = { "CPU(s)": str(expected_cpu_count), "On-line CPU(s) list": expected_cpus_list, - "Thread(s) per core": "1", + "Thread(s) per core": str(expected_threads_per_core), "Core(s) per cluster": str( int(expected_cpu_count / expected_threads_per_core) ), @@ -59,14 +60,16 @@ def _check_cpu_topology( "depth 7": f"{expected_cpu_count} PU (type #3)", } else: + threads_per_core = expected_threads_per_core + cores = int(expected_cpu_count / threads_per_core) expected_hwloc_output = { "depth 0": "1 Machine (type #0)", "depth 1": "1 Package (type #1)", "depth 2": "1 L3Cache (type #6)", - "depth 3": f"{expected_cpu_count} L2Cache (type #5)", - "depth 4": f"{expected_cpu_count} L1dCache (type #4)", - "depth 5": f"{expected_cpu_count} L1iCache (type #9)", - "depth 6": f"{expected_cpu_count} Core (type #2)", + "depth 3": f"{cores if threads_per_core > 1 else expected_cpu_count} L2Cache (type #5)", + "depth 4": f"{cores if threads_per_core > 1 else expected_cpu_count} L1dCache (type #4)", + "depth 5": f"{cores if threads_per_core > 1 else expected_cpu_count} L1iCache (type #9)", + "depth 6": f"{cores} Core (type #2)", "depth 7": f"{expected_cpu_count} PU (type #3)", } @@ -192,15 +195,43 @@ def _check_cache_topology_arm(test_microvm, no_cpus, kernel_version_tpl): assert guest_slice == host_slice +@pin_guest_kernel(GUEST_KERNEL_DEFAULT) +@pytest.mark.parametrize("num_vcpus", [2, 4]) +def test_aarch64_smt_threads_per_core(uvm, num_vcpus): + """ + Check the guest-visible SMT topology without asserting cache hierarchy details. + """ + if PLATFORM != "aarch64": + pytest.skip("This test verifies aarch64 SMT topology.") + + vm = uvm + vm.spawn() + vm.basic_config(vcpu_count=num_vcpus, smt=True) + vm.add_net_iface() + vm.start() + + utils.check_guest_cpuid_output( + vm, + "lscpu", + None, + ":", + { + "CPU(s)": str(num_vcpus), + "On-line CPU(s) list": "0,1" if num_vcpus == 2 else "0-3", + "Thread(s) per core": "2", + "Core(s) per cluster": str(num_vcpus // 2), + "Cluster(s)": "1", + "NUMA node(s)": "1", + }, + ) + + @pytest.mark.parametrize("num_vcpus", [1, 2, 16]) @pytest.mark.parametrize("htt", [True, False], ids=["HTT_ON", "HTT_OFF"]) def test_cpu_topology(uvm, num_vcpus, htt): """ Check the CPU topology for a microvm with the specified config. """ - if htt and PLATFORM == "aarch64": - pytest.skip("SMT is configurable only on x86.") - # TODO:Remove (or adapt) this once we unify the way we expose the CPU cache hierarchy on # Aarch64 systems. if version.parse(get_kernel_version()) >= version.parse("6.14"): @@ -223,8 +254,6 @@ def test_cache_topology(uvm, num_vcpus, htt): """ Check the cache topology for a microvm with the specified config. """ - if htt and PLATFORM == "aarch64": - pytest.skip("SMT is configurable only on x86.") vm = uvm vm.spawn() vm.basic_config(vcpu_count=num_vcpus, smt=htt)