Skip to content

Commit 4df4135

Browse files
committed
Edit CPUID Normalization to account for possibility of hotplugging
Changes some of the CPUID leaves to account for the possibility of hotplugged vCPUs. Previously, the maximum APIC address space and L3 cache were only shared between boot_cpus. Whereas, now we want this to be extended for the maximum number of CPUs that can be added, MAX_SUPPORTED_VCPUS. Signed-off-by: James Curtis <[email protected]>
1 parent fc8c91d commit 4df4135

File tree

6 files changed

+33
-42
lines changed

6 files changed

+33
-42
lines changed

src/vmm/src/cpu_config/x86_64/cpuid/amd/normalize.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::cpu_config::x86_64::cpuid::{
99
cpuid, cpuid_count, CpuidEntry, CpuidKey, CpuidRegisters, CpuidTrait, KvmCpuidFlags,
1010
MissingBrandStringLeaves, BRAND_STRING_LENGTH, VENDOR_ID_AMD,
1111
};
12+
use crate::vmm_config::machine_config::MAX_SUPPORTED_VCPUS;
1213

1314
/// Error type for [`super::AmdCpuid::normalize`].
1415
#[allow(clippy::module_name_repetitions)]
@@ -107,7 +108,7 @@ impl super::AmdCpuid {
107108
self.update_largest_extended_fn_entry()?;
108109
self.update_extended_feature_fn_entry()?;
109110
self.update_amd_feature_entry(cpu_count)?;
110-
self.update_extended_cache_topology_entry(cpu_count, cpus_per_core)?;
111+
self.update_extended_cache_topology_entry(cpus_per_core)?;
111112
self.update_extended_apic_id_entry(cpu_index, cpus_per_core)?;
112113
self.update_brand_string_entry()?;
113114

@@ -268,7 +269,6 @@ impl super::AmdCpuid {
268269
#[allow(clippy::unwrap_in_result, clippy::unwrap_used)]
269270
fn update_extended_cache_topology_entry(
270271
&mut self,
271-
cpu_count: u8,
272272
cpus_per_core: u8,
273273
) -> Result<(), ExtendedCacheTopologyError> {
274274
for i in 0.. {
@@ -312,7 +312,7 @@ impl super::AmdCpuid {
312312
// L3 Cache
313313
// The L3 cache is shared among all the logical threads
314314
3 => {
315-
let sub = cpu_count
315+
let sub = MAX_SUPPORTED_VCPUS
316316
.checked_sub(1)
317317
.ok_or(ExtendedCacheTopologyError::NumSharingCacheOverflow)?;
318318
set_range(&mut subleaf.result.eax, 14..26, u32::from(sub))

src/vmm/src/cpu_config/x86_64/cpuid/intel/normalize.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::cpu_config::x86_64::cpuid::{
88
host_brand_string, CpuidKey, CpuidRegisters, CpuidTrait, MissingBrandStringLeaves,
99
BRAND_STRING_LENGTH,
1010
};
11+
use crate::vmm_config::machine_config::MAX_SUPPORTED_VCPUS;
1112

1213
/// Error type for [`super::IntelCpuid::normalize`].
1314
#[derive(Debug, thiserror::Error, displaydoc::Display, Eq, PartialEq)]
@@ -62,14 +63,10 @@ impl super::IntelCpuid {
6263
#[inline]
6364
pub fn normalize(
6465
&mut self,
65-
// The index of the current logical CPU in the range [0..cpu_count].
66-
_cpu_index: u8,
67-
// The total number of logical CPUs.
68-
cpu_count: u8,
6966
// The number of logical CPUs per core.
7067
cpus_per_core: u8,
7168
) -> Result<(), NormalizeCpuidError> {
72-
self.update_deterministic_cache_entry(cpu_count, cpus_per_core)?;
69+
self.update_deterministic_cache_entry(cpus_per_core)?;
7370
self.update_power_management_entry()?;
7471
self.update_extended_feature_flags_entry()?;
7572
self.update_performance_monitoring_entry()?;
@@ -82,7 +79,6 @@ impl super::IntelCpuid {
8279
#[allow(clippy::unwrap_in_result)]
8380
fn update_deterministic_cache_entry(
8481
&mut self,
85-
cpu_count: u8,
8682
cpus_per_core: u8,
8783
) -> Result<(), DeterministicCacheError> {
8884
for i in 0.. {
@@ -125,7 +121,7 @@ impl super::IntelCpuid {
125121
// The L3 cache is shared among all the logical threads
126122
3 => {
127123
let sub = u32::from(
128-
cpu_count
124+
MAX_SUPPORTED_VCPUS
129125
.checked_sub(1)
130126
.ok_or(DeterministicCacheError::MaxCpusPerCoreUnderflow)?,
131127
);
@@ -137,7 +133,7 @@ impl super::IntelCpuid {
137133

138134
// We know `cpus_per_core !=0` therefore this is always safe.
139135
#[allow(clippy::unwrap_used)]
140-
let cores = cpu_count.checked_div(cpus_per_core).unwrap();
136+
let cores = MAX_SUPPORTED_VCPUS.checked_div(cpus_per_core).unwrap();
141137

142138
// Maximum number of addressable IDs for processor cores in the physical package.
143139
// - Add one to the return value to get the result.

src/vmm/src/cpu_config/x86_64/cpuid/normalize.rs

+15-21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use crate::cpu_config::x86_64::cpuid::{
55
cpuid, CpuidEntry, CpuidKey, CpuidRegisters, CpuidTrait, KvmCpuidFlags,
66
};
7+
use crate::vmm_config::machine_config::MAX_SUPPORTED_VCPUS;
78

89
/// Error type for [`super::Cpuid::normalize`].
910
#[allow(clippy::module_name_repetitions)]
@@ -182,15 +183,15 @@ impl super::Cpuid {
182183
.checked_shl(u32::from(cpu_bits))
183184
.ok_or(NormalizeCpuidError::CpuBits(cpu_bits))?;
184185
self.update_vendor_id()?;
185-
self.update_feature_info_entry(cpu_index, cpu_count)?;
186-
self.update_extended_topology_entry(cpu_index, cpu_count, cpu_bits, cpus_per_core)?;
186+
self.update_feature_info_entry(cpu_index)?;
187+
self.update_extended_topology_entry(cpu_index, cpu_bits, cpus_per_core)?;
187188
self.update_extended_cache_features()?;
188189

189190
// Apply manufacturer specific modifications.
190191
match self {
191192
// Apply Intel specific modifications.
192193
Self::Intel(intel_cpuid) => {
193-
intel_cpuid.normalize(cpu_index, cpu_count, cpus_per_core)?;
194+
intel_cpuid.normalize(cpus_per_core)?;
194195
}
195196
// Apply AMD specific modifications.
196197
Self::Amd(amd_cpuid) => amd_cpuid.normalize(cpu_index, cpu_count, cpus_per_core)?,
@@ -216,11 +217,7 @@ impl super::Cpuid {
216217
}
217218

218219
// Update feature information entry
219-
fn update_feature_info_entry(
220-
&mut self,
221-
cpu_index: u8,
222-
cpu_count: u8,
223-
) -> Result<(), FeatureInformationError> {
220+
fn update_feature_info_entry(&mut self, cpu_index: u8) -> Result<(), FeatureInformationError> {
224221
// Flush a cache line size.
225222
const EBX_CLFLUSH_CACHELINE: u32 = 8;
226223

@@ -268,7 +265,7 @@ impl super::Cpuid {
268265
.map_err(FeatureInformationError::Clflush)?;
269266

270267
let max_cpus_per_package = u32::from(
271-
get_max_cpus_per_package(cpu_count)
268+
get_max_cpus_per_package(MAX_SUPPORTED_VCPUS)
272269
.map_err(FeatureInformationError::GetMaxCpusPerPackage)?,
273270
);
274271

@@ -294,7 +291,7 @@ impl super::Cpuid {
294291
// A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16]
295292
// (the Maximum number of addressable IDs for logical processors in this package)
296293
// is valid for the package
297-
set_bit(&mut leaf_1.result.edx, 28, cpu_count > 1);
294+
set_bit(&mut leaf_1.result.edx, 28, true);
298295

299296
Ok(())
300297
}
@@ -303,7 +300,6 @@ impl super::Cpuid {
303300
fn update_extended_topology_entry(
304301
&mut self,
305302
cpu_index: u8,
306-
cpu_count: u8,
307303
cpu_bits: u8,
308304
cpus_per_core: u8,
309305
) -> Result<(), ExtendedTopologyError> {
@@ -408,8 +404,12 @@ impl super::Cpuid {
408404
set_range(&mut subleaf.result.eax, 0..5, LEAFBH_INDEX1_APICID)
409405
.map_err(ExtendedTopologyError::ApicId)?;
410406

411-
set_range(&mut subleaf.result.ebx, 0..16, u32::from(cpu_count))
412-
.map_err(ExtendedTopologyError::LogicalProcessors)?;
407+
set_range(
408+
&mut subleaf.result.ebx,
409+
0..16,
410+
u32::from(MAX_SUPPORTED_VCPUS),
411+
)
412+
.map_err(ExtendedTopologyError::LogicalProcessors)?;
413413

414414
// We expect here as this is an extremely rare case that is unlikely to ever
415415
// occur. It would require manual editing of the CPUID structure to push
@@ -577,12 +577,7 @@ mod tests {
577577
},
578578
},
579579
)])));
580-
let result = intel_cpuid.update_extended_topology_entry(
581-
cpu_index,
582-
cpu_count,
583-
cpu_bits,
584-
cpus_per_core,
585-
);
580+
let result = intel_cpuid.update_extended_topology_entry(cpu_index, cpu_bits, cpus_per_core);
586581
result.unwrap();
587582
assert!(intel_cpuid.inner().contains_key(&CpuidKey {
588583
leaf: 0xb,
@@ -605,8 +600,7 @@ mod tests {
605600
},
606601
},
607602
)])));
608-
let result =
609-
amd_cpuid.update_extended_topology_entry(cpu_index, cpu_count, cpu_bits, cpus_per_core);
603+
let result = amd_cpuid.update_extended_topology_entry(cpu_index, cpu_bits, cpus_per_core);
610604
result.unwrap();
611605
assert!(amd_cpuid.inner().contains_key(&CpuidKey {
612606
leaf: 0xb,

tests/framework/defs.py

+2
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@
3939
IMG_DIR = LOCAL_BUILD_PATH / "img"
4040

4141
ARTIFACT_DIR = IMG_DIR / platform.machine()
42+
43+
MAX_SUPPORTED_VCPUS = 32

tests/integration_tests/functional/test_cpu_features.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import framework.utils_cpuid as cpuid_utils
2121
from framework import utils
22-
from framework.defs import SUPPORTED_HOST_KERNELS
22+
from framework.defs import MAX_SUPPORTED_VCPUS, SUPPORTED_HOST_KERNELS
2323
from framework.properties import global_props
2424
from framework.utils_cpu_templates import SUPPORTED_CPU_TEMPLATES
2525

@@ -38,12 +38,12 @@ def clean_and_mkdir(dir_path):
3838
os.makedirs(dir_path)
3939

4040

41-
def _check_cpuid_x86(test_microvm, expected_cpu_count, expected_htt):
41+
def _check_cpuid_x86(test_microvm):
4242
expected_cpu_features = {
43-
"maximum IDs for CPUs in pkg": f"{expected_cpu_count:#x} ({expected_cpu_count})",
43+
"maximum IDs for CPUs in pkg": f"{MAX_SUPPORTED_VCPUS:#x} ({MAX_SUPPORTED_VCPUS})",
4444
"CLFLUSH line size": "0x8 (8)",
4545
"hypervisor guest status": "true",
46-
"hyper-threading / multi-core supported": expected_htt,
46+
"hyper-threading / multi-core supported": "true",
4747
}
4848

4949
cpuid_utils.check_guest_cpuid_output(
@@ -116,7 +116,7 @@ def test_cpuid(uvm_plain_any, num_vcpus, htt):
116116
vm.basic_config(vcpu_count=num_vcpus, smt=htt)
117117
vm.add_net_iface()
118118
vm.start()
119-
_check_cpuid_x86(vm, num_vcpus, "true" if num_vcpus > 1 else "false")
119+
_check_cpuid_x86(vm)
120120

121121

122122
@pytest.mark.skipif(PLATFORM != "x86_64", reason="CPUID is only supported on x86_64.")

tests/integration_tests/functional/test_topology.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pytest
99

1010
import framework.utils_cpuid as utils
11+
from framework.defs import MAX_SUPPORTED_VCPUS
1112
from framework.properties import global_props
1213

1314
TOPOLOGY_STR = {1: "0", 2: "0,1", 16: "0-15"}
@@ -31,15 +32,13 @@ def _check_cpu_topology(
3132
)
3233

3334

34-
def _check_cache_topology_x86(
35-
test_microvm, num_vcpus_on_lvl_1_cache, num_vcpus_on_lvl_3_cache
36-
):
35+
def _check_cache_topology_x86(test_microvm, num_vcpus_on_lvl_1_cache):
3736
vm = test_microvm
3837
expected_lvl_1_str = "{} ({})".format(
3938
hex(num_vcpus_on_lvl_1_cache), num_vcpus_on_lvl_1_cache
4039
)
4140
expected_lvl_3_str = "{} ({})".format(
42-
hex(num_vcpus_on_lvl_3_cache), num_vcpus_on_lvl_3_cache
41+
hex(MAX_SUPPORTED_VCPUS - 1), MAX_SUPPORTED_VCPUS - 1
4342
)
4443

4544
cpu_vendor = utils.get_cpu_vendor()
@@ -181,7 +180,7 @@ def test_cache_topology(uvm_plain_any, num_vcpus, htt):
181180
vm.add_net_iface()
182181
vm.start()
183182
if PLATFORM == "x86_64":
184-
_check_cache_topology_x86(vm, 1 if htt and num_vcpus > 1 else 0, num_vcpus - 1)
183+
_check_cache_topology_x86(vm, 1 if htt and num_vcpus > 1 else 0)
185184
elif PLATFORM == "aarch64":
186185
_check_cache_topology_arm(vm, num_vcpus, global_props.host_linux_version_tpl)
187186
else:

0 commit comments

Comments
 (0)