mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-31 16:36:38 +00:00
Merge pull request #10580 from pmores/make-vcpu-allocation-more-accurate
runtime-rs: make vcpu allocation more accurate
This commit is contained in:
@@ -635,13 +635,13 @@ impl Annotation {
|
||||
KATA_ANNO_CFG_HYPERVISOR_CPU_FEATURES => {
|
||||
hv.cpu_info.cpu_features = value.to_string();
|
||||
}
|
||||
KATA_ANNO_CFG_HYPERVISOR_DEFAULT_VCPUS => match self.get_value::<i32>(key) {
|
||||
KATA_ANNO_CFG_HYPERVISOR_DEFAULT_VCPUS => match self.get_value::<f32>(key) {
|
||||
Ok(num_cpus) => {
|
||||
let num_cpus = num_cpus.unwrap_or_default();
|
||||
if num_cpus
|
||||
> get_hypervisor_plugin(hypervisor_name)
|
||||
.unwrap()
|
||||
.get_max_cpus() as i32
|
||||
.get_max_cpus() as f32
|
||||
{
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
@@ -1079,6 +1079,9 @@ impl Annotation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.adjust_config()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -369,7 +369,7 @@ mod drop_in_directory_handling {
|
||||
config.hypervisor["qemu"].path,
|
||||
"/usr/bin/qemu-kvm".to_string()
|
||||
);
|
||||
assert_eq!(config.hypervisor["qemu"].cpu_info.default_vcpus, 2);
|
||||
assert_eq!(config.hypervisor["qemu"].cpu_info.default_vcpus, 2.0);
|
||||
assert_eq!(config.hypervisor["qemu"].device_info.default_bridges, 4);
|
||||
assert_eq!(
|
||||
config.hypervisor["qemu"].shared_fs.shared_fs.as_deref(),
|
||||
|
@@ -109,7 +109,7 @@ impl ConfigPlugin for CloudHypervisorConfig {
|
||||
return Err(eother!("Both guest boot image and initrd for CH are empty"));
|
||||
}
|
||||
|
||||
if (ch.cpu_info.default_vcpus > 0
|
||||
if (ch.cpu_info.default_vcpus > 0.0
|
||||
&& ch.cpu_info.default_vcpus as u32 > default::MAX_CH_VCPUS)
|
||||
|| ch.cpu_info.default_maxvcpus > default::MAX_CH_VCPUS
|
||||
{
|
||||
|
@@ -66,7 +66,7 @@ impl ConfigPlugin for DragonballConfig {
|
||||
}
|
||||
|
||||
if db.cpu_info.default_vcpus as u32 > db.cpu_info.default_maxvcpus {
|
||||
db.cpu_info.default_vcpus = db.cpu_info.default_maxvcpus as i32;
|
||||
db.cpu_info.default_vcpus = db.cpu_info.default_maxvcpus as f32;
|
||||
}
|
||||
|
||||
if db.machine_info.entropy_source.is_empty() {
|
||||
@@ -135,7 +135,7 @@ impl ConfigPlugin for DragonballConfig {
|
||||
));
|
||||
}
|
||||
|
||||
if (db.cpu_info.default_vcpus > 0
|
||||
if (db.cpu_info.default_vcpus > 0.0
|
||||
&& db.cpu_info.default_vcpus as u32 > default::MAX_DRAGONBALL_VCPUS)
|
||||
|| db.cpu_info.default_maxvcpus > default::MAX_DRAGONBALL_VCPUS
|
||||
{
|
||||
|
@@ -93,7 +93,7 @@ impl ConfigPlugin for FirecrackerConfig {
|
||||
));
|
||||
}
|
||||
|
||||
if (firecracker.cpu_info.default_vcpus > 0
|
||||
if (firecracker.cpu_info.default_vcpus > 0.0
|
||||
&& firecracker.cpu_info.default_vcpus as u32 > default::MAX_FIRECRACKER_VCPUS)
|
||||
|| firecracker.cpu_info.default_maxvcpus > default::MAX_FIRECRACKER_VCPUS
|
||||
{
|
||||
|
@@ -348,7 +348,7 @@ pub struct CpuInfo {
|
||||
/// > 0 <= number of physical cores --> will be set to the specified number
|
||||
/// > number of physical cores --> will be set to the actual number of physical cores
|
||||
#[serde(default)]
|
||||
pub default_vcpus: i32,
|
||||
pub default_vcpus: f32,
|
||||
|
||||
/// Default maximum number of vCPUs per SB/VM:
|
||||
/// - unspecified or == 0 --> will be set to the actual number of physical cores or
|
||||
@@ -380,22 +380,22 @@ impl CpuInfo {
|
||||
let features: Vec<&str> = self.cpu_features.split(',').map(|v| v.trim()).collect();
|
||||
self.cpu_features = features.join(",");
|
||||
|
||||
let cpus = num_cpus::get() as u32;
|
||||
let cpus = num_cpus::get() as f32;
|
||||
|
||||
// adjust default_maxvcpus
|
||||
if self.default_maxvcpus == 0 || self.default_maxvcpus > cpus {
|
||||
self.default_maxvcpus = cpus;
|
||||
if self.default_maxvcpus == 0 || self.default_maxvcpus as f32 > cpus {
|
||||
self.default_maxvcpus = cpus as u32;
|
||||
}
|
||||
|
||||
// adjust default_vcpus
|
||||
if self.default_vcpus < 0 || self.default_vcpus as u32 > cpus {
|
||||
self.default_vcpus = cpus as i32;
|
||||
} else if self.default_vcpus == 0 {
|
||||
self.default_vcpus = default::DEFAULT_GUEST_VCPUS as i32;
|
||||
if self.default_vcpus < 0.0 || self.default_vcpus > cpus {
|
||||
self.default_vcpus = cpus;
|
||||
} else if self.default_vcpus == 0.0 {
|
||||
self.default_vcpus = default::DEFAULT_GUEST_VCPUS as f32;
|
||||
}
|
||||
|
||||
if self.default_vcpus > self.default_maxvcpus as i32 {
|
||||
self.default_vcpus = self.default_maxvcpus as i32;
|
||||
if self.default_vcpus > self.default_maxvcpus as f32 {
|
||||
self.default_vcpus = self.default_maxvcpus as f32;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -403,7 +403,7 @@ impl CpuInfo {
|
||||
|
||||
/// Validate the configuration information.
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
if self.default_vcpus > self.default_maxvcpus as i32 {
|
||||
if self.default_vcpus > self.default_maxvcpus as f32 {
|
||||
return Err(eother!(
|
||||
"The default_vcpus({}) is greater than default_maxvcpus({})",
|
||||
self.default_vcpus,
|
||||
@@ -1413,8 +1413,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_cpu_info_adjust_config() {
|
||||
// get CPU cores of the test node
|
||||
let node_cpus = num_cpus::get() as u32;
|
||||
let default_vcpus = default::DEFAULT_GUEST_VCPUS as i32;
|
||||
let node_cpus = num_cpus::get() as f32;
|
||||
let default_vcpus = default::DEFAULT_GUEST_VCPUS as f32;
|
||||
|
||||
struct TestData<'a> {
|
||||
desc: &'a str,
|
||||
@@ -1427,38 +1427,38 @@ mod tests {
|
||||
desc: "all with default values",
|
||||
input: &mut CpuInfo {
|
||||
cpu_features: "".to_string(),
|
||||
default_vcpus: 0,
|
||||
default_vcpus: 0.0,
|
||||
default_maxvcpus: 0,
|
||||
},
|
||||
output: CpuInfo {
|
||||
cpu_features: "".to_string(),
|
||||
default_vcpus,
|
||||
default_maxvcpus: node_cpus,
|
||||
default_maxvcpus: node_cpus as u32,
|
||||
},
|
||||
},
|
||||
TestData {
|
||||
desc: "all with big values",
|
||||
input: &mut CpuInfo {
|
||||
cpu_features: "a,b,c".to_string(),
|
||||
default_vcpus: 9999999,
|
||||
default_vcpus: 9999999.0,
|
||||
default_maxvcpus: 9999999,
|
||||
},
|
||||
output: CpuInfo {
|
||||
cpu_features: "a,b,c".to_string(),
|
||||
default_vcpus: node_cpus as i32,
|
||||
default_maxvcpus: node_cpus,
|
||||
default_vcpus: node_cpus,
|
||||
default_maxvcpus: node_cpus as u32,
|
||||
},
|
||||
},
|
||||
TestData {
|
||||
desc: "default_vcpus lager than default_maxvcpus",
|
||||
input: &mut CpuInfo {
|
||||
cpu_features: "a, b ,c".to_string(),
|
||||
default_vcpus: -1,
|
||||
default_vcpus: -1.0,
|
||||
default_maxvcpus: 1,
|
||||
},
|
||||
output: CpuInfo {
|
||||
cpu_features: "a,b,c".to_string(),
|
||||
default_vcpus: 1,
|
||||
default_vcpus: 1.0,
|
||||
default_maxvcpus: 1,
|
||||
},
|
||||
},
|
||||
|
@@ -128,7 +128,7 @@ impl ConfigPlugin for QemuConfig {
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu.cpu_info.default_vcpus > 0
|
||||
if (qemu.cpu_info.default_vcpus > 0.0
|
||||
&& qemu.cpu_info.default_vcpus as u32 > default::MAX_QEMU_VCPUS)
|
||||
|| qemu.cpu_info.default_maxvcpus > default::MAX_QEMU_VCPUS
|
||||
{
|
||||
|
@@ -131,9 +131,7 @@ impl TomlConfig {
|
||||
pub fn load_from_file<P: AsRef<Path>>(config_file: P) -> Result<(TomlConfig, PathBuf)> {
|
||||
let mut result = Self::load_raw_from_file(config_file);
|
||||
if let Ok((ref mut config, _)) = result {
|
||||
Hypervisor::adjust_config(config)?;
|
||||
Runtime::adjust_config(config)?;
|
||||
Agent::adjust_config(config)?;
|
||||
config.adjust_config()?;
|
||||
info!(sl!(), "get kata config: {:?}", config);
|
||||
}
|
||||
|
||||
@@ -175,13 +173,20 @@ impl TomlConfig {
|
||||
/// drop-in config file fragments in config.d/.
|
||||
pub fn load(content: &str) -> Result<TomlConfig> {
|
||||
let mut config: TomlConfig = toml::from_str(content)?;
|
||||
Hypervisor::adjust_config(&mut config)?;
|
||||
Runtime::adjust_config(&mut config)?;
|
||||
Agent::adjust_config(&mut config)?;
|
||||
config.adjust_config()?;
|
||||
info!(sl!(), "get kata config: {:?}", config);
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Adjust Kata configuration information.
|
||||
pub fn adjust_config(&mut self) -> Result<()> {
|
||||
Hypervisor::adjust_config(self)?;
|
||||
Runtime::adjust_config(self)?;
|
||||
Agent::adjust_config(self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate Kata configuration information.
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
Hypervisor::validate(self)?;
|
||||
|
@@ -186,7 +186,7 @@ mod tests {
|
||||
"./test_hypervisor_hook_path"
|
||||
);
|
||||
assert!(!hv.memory_info.enable_mem_prealloc);
|
||||
assert_eq!(hv.cpu_info.default_vcpus, 12);
|
||||
assert_eq!(hv.cpu_info.default_vcpus, 12.0);
|
||||
assert!(!hv.memory_info.enable_guest_swap);
|
||||
assert_eq!(hv.memory_info.default_memory, 100);
|
||||
assert!(!hv.enable_iothreads);
|
||||
|
@@ -319,12 +319,12 @@ impl TryFrom<(CpuInfo, GuestProtection)> for CpusConfig {
|
||||
let guest_protection_to_use = args.1;
|
||||
|
||||
// This can only happen if runtime-rs fails to set default values.
|
||||
if cpu.default_vcpus <= 0 {
|
||||
if cpu.default_vcpus <= 0.0 {
|
||||
return Err(CpusConfigError::BootVCPUsTooSmall);
|
||||
}
|
||||
|
||||
let default_vcpus =
|
||||
u8::try_from(cpu.default_vcpus).map_err(CpusConfigError::BootVCPUsTooBig)?;
|
||||
let default_vcpus = u8::try_from(cpu.default_vcpus.ceil() as u32)
|
||||
.map_err(CpusConfigError::BootVCPUsTooBig)?;
|
||||
|
||||
// This can only happen if runtime-rs fails to set default values.
|
||||
if cpu.default_maxvcpus == 0 {
|
||||
@@ -611,7 +611,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let cpu_info = CpuInfo {
|
||||
default_vcpus: cpu_default as i32,
|
||||
default_vcpus: cpu_default as f32,
|
||||
default_maxvcpus,
|
||||
|
||||
..Default::default()
|
||||
@@ -1159,7 +1159,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: -1,
|
||||
default_vcpus: -1.0,
|
||||
|
||||
..Default::default()
|
||||
},
|
||||
@@ -1168,7 +1168,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 1,
|
||||
default_vcpus: 1.0,
|
||||
default_maxvcpus: 0,
|
||||
|
||||
..Default::default()
|
||||
@@ -1178,7 +1178,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 9,
|
||||
default_vcpus: 9.0,
|
||||
default_maxvcpus: 7,
|
||||
|
||||
..Default::default()
|
||||
@@ -1188,7 +1188,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 1,
|
||||
default_vcpus: 1.0,
|
||||
default_maxvcpus: 1,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -1208,7 +1208,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 1,
|
||||
default_vcpus: 1.0,
|
||||
default_maxvcpus: 3,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -1228,7 +1228,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 1,
|
||||
default_vcpus: 1.0,
|
||||
default_maxvcpus: 13,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -1823,7 +1823,7 @@ mod tests {
|
||||
|
||||
cfg: HypervisorConfig {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 0,
|
||||
default_vcpus: 0.0,
|
||||
|
||||
..cpu_info.clone()
|
||||
},
|
||||
@@ -1939,7 +1939,7 @@ mod tests {
|
||||
vsock_socket_path: "vsock_socket_path".into(),
|
||||
cfg: HypervisorConfig {
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 1,
|
||||
default_vcpus: 1.0,
|
||||
default_maxvcpus: 1,
|
||||
|
||||
..Default::default()
|
||||
@@ -1963,7 +1963,7 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
cpu_info: CpuInfo {
|
||||
default_vcpus: 1,
|
||||
default_vcpus: 1.0,
|
||||
default_maxvcpus: 1,
|
||||
|
||||
..Default::default()
|
||||
|
@@ -25,7 +25,7 @@ pub struct CpuResource {
|
||||
pub(crate) current_vcpu: Arc<RwLock<u32>>,
|
||||
|
||||
/// Default number of vCPUs
|
||||
pub(crate) default_vcpu: u32,
|
||||
pub(crate) default_vcpu: f32,
|
||||
|
||||
/// CpuResource of each container
|
||||
pub(crate) container_cpu_resources: Arc<RwLock<HashMap<String, LinuxContainerCpuResources>>>,
|
||||
@@ -40,7 +40,7 @@ impl CpuResource {
|
||||
.context(format!("failed to get hypervisor {}", hypervisor_name))?;
|
||||
Ok(Self {
|
||||
current_vcpu: Arc::new(RwLock::new(hypervisor_config.cpu_info.default_vcpus as u32)),
|
||||
default_vcpu: hypervisor_config.cpu_info.default_vcpus as u32,
|
||||
default_vcpu: hypervisor_config.cpu_info.default_vcpus,
|
||||
container_cpu_resources: Arc::new(RwLock::new(HashMap::new())),
|
||||
})
|
||||
}
|
||||
@@ -117,27 +117,66 @@ impl CpuResource {
|
||||
|
||||
// calculates the total required vcpus by adding each container's requirements within the pod
|
||||
async fn calc_cpu_resources(&self) -> Result<u32> {
|
||||
let mut total_vcpu = 0;
|
||||
let mut cpuset_vcpu: HashSet<u32> = HashSet::new();
|
||||
|
||||
let resources = self.container_cpu_resources.read().await;
|
||||
if resources.is_empty() {
|
||||
return Ok(self.default_vcpu.ceil() as u32);
|
||||
}
|
||||
|
||||
// If requests of individual containers are expresses with different
|
||||
// periods we'll need to rewrite them with a common denominator
|
||||
// (period) before we can add the numerators (quotas). We choose
|
||||
// to use the largest period as the common denominator since it
|
||||
// shifts precision out of the fractional part and into the
|
||||
// integral part in case a rewritten quota ends up non-integral.
|
||||
let max_period = resources
|
||||
.iter()
|
||||
.map(|(_, cpu_resource)| cpu_resource.period())
|
||||
.max()
|
||||
// It's ok to unwrap() here as we have checked that 'resources' is
|
||||
// not empty.
|
||||
.unwrap() as f64;
|
||||
|
||||
let mut cpuset_vcpu: HashSet<u32> = HashSet::new();
|
||||
// Even though summing up quotas is fixed-point conceptually we
|
||||
// represent the sum as floating-point because
|
||||
// - we might be rewriting the quota/period fractions if periods
|
||||
// vary,and a rewritten quota can end up non-integral. We want
|
||||
// to preserve the fractional parts until the final rounding
|
||||
// not to lose precision inadvertenty.
|
||||
// - also to avoid some tedious casting doing maths with quotas.
|
||||
// Using a 64-bit float to represent what are conceptually integral
|
||||
// numbers should be safe here - f64 starts losing precision for
|
||||
// integers only past 2^53 and a sums of quotas are extremely unlikely
|
||||
// to reach that magnitude.
|
||||
let mut total_quota: f64 = 0.0;
|
||||
|
||||
for (_, cpu_resource) in resources.iter() {
|
||||
let vcpu = cpu_resource.get_vcpus().unwrap_or(0) as u32;
|
||||
cpuset_vcpu.extend(cpu_resource.cpuset().iter());
|
||||
total_vcpu += vcpu;
|
||||
|
||||
let quota = cpu_resource.quota() as f64;
|
||||
let period = cpu_resource.period() as f64;
|
||||
if quota >= 0.0 && period != 0.0 {
|
||||
total_quota += quota * (max_period / period);
|
||||
}
|
||||
}
|
||||
|
||||
// contrained only by cpuset
|
||||
if total_vcpu == 0 && !cpuset_vcpu.is_empty() {
|
||||
if total_quota == 0.0 && !cpuset_vcpu.is_empty() {
|
||||
info!(sl!(), "(from cpuset)get vcpus # {:?}", cpuset_vcpu);
|
||||
return Ok(cpuset_vcpu.len() as u32);
|
||||
}
|
||||
|
||||
let total_vcpu = if total_quota > 0.0 && max_period != 0.0 {
|
||||
self.default_vcpu as f64 + total_quota / max_period
|
||||
} else {
|
||||
self.default_vcpu as f64
|
||||
};
|
||||
|
||||
info!(
|
||||
sl!(),
|
||||
"(from cfs_quota&cfs_period)get vcpus count {}", total_vcpu
|
||||
);
|
||||
Ok(total_vcpu)
|
||||
Ok(total_vcpu.ceil() as u32)
|
||||
}
|
||||
|
||||
// do hotplug and hot-unplug the vcpu
|
||||
@@ -159,7 +198,7 @@ impl CpuResource {
|
||||
|
||||
// do not reduce computing power
|
||||
// the number of vcpus would not be lower than the default size
|
||||
let new_vcpus = cmp::max(new_vcpus, self.default_vcpu);
|
||||
let new_vcpus = cmp::max(new_vcpus, self.default_vcpu.ceil() as u32);
|
||||
|
||||
let (_, new) = hypervisor
|
||||
.resize_vcpu(old_vcpus, new_vcpus)
|
||||
@@ -169,3 +208,238 @@ impl CpuResource {
|
||||
Ok(new)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kata_types::config::{Hypervisor, TomlConfig};
|
||||
use oci::LinuxCpu;
|
||||
|
||||
fn get_cpu_resource_with_default_vcpus(default_vcpus: f32) -> CpuResource {
|
||||
let mut config = TomlConfig::default();
|
||||
config
|
||||
.hypervisor
|
||||
.insert("qemu".to_owned(), Hypervisor::default());
|
||||
config
|
||||
.hypervisor
|
||||
.entry("qemu".to_owned())
|
||||
.and_modify(|hv_config| hv_config.cpu_info.default_vcpus = default_vcpus);
|
||||
config.runtime.hypervisor_name = "qemu".to_owned();
|
||||
CpuResource::new(Arc::new(config)).unwrap()
|
||||
}
|
||||
|
||||
async fn add_linux_container_cpu_resources(cpu_res: &mut CpuResource, res: Vec<(i64, u64)>) {
|
||||
let mut resources = cpu_res.container_cpu_resources.write().await;
|
||||
for (i, (quota, period)) in res.iter().enumerate() {
|
||||
let mut linux_cpu = LinuxCpu::default();
|
||||
linux_cpu.set_quota(Some(*quota));
|
||||
linux_cpu.set_period(Some(*period));
|
||||
let res = LinuxContainerCpuResources::try_from(&linux_cpu).unwrap();
|
||||
resources.insert(i.to_string(), res);
|
||||
}
|
||||
}
|
||||
|
||||
// A lot of the following tests document why a fixed-point-style
|
||||
// calc_cpu_resources() implementation is better than a f32-based one.
|
||||
#[tokio::test]
|
||||
async fn test_rounding() {
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(0.0);
|
||||
|
||||
// A f32-based calc_cpu_resources() implementation would fail this
|
||||
// test (adding 0.1 ten times gives roughly 1.0000001).
|
||||
// An f64-based implementation would pass this one (with the summation
|
||||
// result of 0.99999999999999989) but it still doesn't guarantee the
|
||||
// correct result in general. For instance, adding 0.1 twenty times
|
||||
// in 64 bits results in 2.0000000000000004.
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
(100_000, 1_000_000),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(cpu_resource.calc_cpu_resources().await.unwrap(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_big_allocation_1() {
|
||||
let default_vcpus = 10.0;
|
||||
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![
|
||||
(32_000_000, 1_000_000),
|
||||
(32_000_000, 1_000_000),
|
||||
(64_000_000, 1_000_000),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
128 + default_vcpus as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_big_allocation_2() {
|
||||
let default_vcpus = 10.0;
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![
|
||||
(33_000_000, 1_000_000),
|
||||
(31_000_000, 1_000_000),
|
||||
(77_000_011, 1_000_000),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
(33 + 31 + 77 + 1) + default_vcpus as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_big_allocation_3() {
|
||||
let default_vcpus = 10.0;
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(&mut cpu_resource, vec![(141_000_008, 1_000_000)]).await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
142 + default_vcpus as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_big_allocation_4() {
|
||||
let default_vcpus = 10.0;
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![
|
||||
(17_000_001, 1_000_000),
|
||||
(17_000_001, 1_000_000),
|
||||
(17_000_001, 1_000_000),
|
||||
(17_000_001, 1_000_000),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
(4 * 17 + 1) + default_vcpus as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_divisible_periods() {
|
||||
let default_vcpus = 3.0;
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![(1_000_000, 1_000_000), (1_000_000, 500_000)],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
3 + default_vcpus as u32
|
||||
);
|
||||
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![(3_000_000, 1_500_000), (1_000_000, 500_000)],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
4 + default_vcpus as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_indivisible_periods() {
|
||||
let default_vcpus = 1.0;
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![(1_000_000, 1_000_000), (900_000, 300_000)],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
4 + default_vcpus as u32
|
||||
);
|
||||
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![(1_000_000, 1_000_000), (900_000, 299_999)],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
5 + default_vcpus as u32
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fractional_default_vcpus() {
|
||||
let default_vcpus = 0.5;
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(&mut cpu_resource, vec![(250_000, 1_000_000)]).await;
|
||||
|
||||
assert_eq!(cpu_resource.calc_cpu_resources().await.unwrap(), 1);
|
||||
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(&mut cpu_resource, vec![(500_000, 1_000_000)]).await;
|
||||
|
||||
assert_eq!(cpu_resource.calc_cpu_resources().await.unwrap(), 1);
|
||||
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(&mut cpu_resource, vec![(500_001, 1_000_000)]).await;
|
||||
|
||||
assert_eq!(cpu_resource.calc_cpu_resources().await.unwrap(), 2);
|
||||
|
||||
// This test doesn't pass because 0.1 is periodic in binary and thus
|
||||
// not exactly representable by a float of any width for fundamental
|
||||
// reasons. Its actual representation is slightly over 0.1
|
||||
// (e.g. 0.100000001 in f32), which after adding the 900_000/1_000_000
|
||||
// container request pushes the sum over 1.
|
||||
// I don't think this problem is solvable without expressing
|
||||
// 'default_vcpus' in configuration.toml in a fixed point manner (e.g.
|
||||
// as an integral percentage of a vCPU).
|
||||
/*
|
||||
let default_vcpus = 0.1;
|
||||
let mut cpu_resource = get_cpu_resource_with_default_vcpus(default_vcpus);
|
||||
add_linux_container_cpu_resources(
|
||||
&mut cpu_resource,
|
||||
vec![(900_000, 1_000_000)],
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
cpu_resource.calc_cpu_resources().await.unwrap(),
|
||||
1
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@@ -141,7 +141,7 @@ impl InitialSizeManager {
|
||||
.context("failed to get hypervisor config")?;
|
||||
|
||||
if self.resource.vcpu > 0 {
|
||||
hv.cpu_info.default_vcpus = self.resource.vcpu as i32
|
||||
hv.cpu_info.default_vcpus = self.resource.vcpu as f32
|
||||
}
|
||||
self.resource.orig_toml_default_mem = hv.memory_info.default_memory;
|
||||
if self.resource.mem_mb > 0 {
|
||||
|
@@ -198,7 +198,7 @@ pub struct HypervisorInfo {
|
||||
#[serde(default)]
|
||||
enable_iommu_platform: bool,
|
||||
#[serde(default)]
|
||||
default_vcpus: i32,
|
||||
default_vcpus: f32,
|
||||
#[serde(default)]
|
||||
cpu_features: String,
|
||||
#[serde(default)]
|
||||
|
@@ -12,7 +12,6 @@ load "${BATS_TEST_DIRNAME}/tests_common.sh"
|
||||
setup() {
|
||||
[ "${KATA_HYPERVISOR}" == "dragonball" ] || [ "${KATA_HYPERVISOR}" == "cloud-hypervisor" ] && \
|
||||
skip "runtime-rs is still using the old vcpus allocation algorithm, skipping the test see https://github.com/kata-containers/kata-containers/issues/8660"
|
||||
[ "${KATA_HYPERVISOR}" = "qemu-runtime-rs" ] && skip "Requires CPU hotplug which isn't supported on ${KATA_HYPERVISOR} yet"
|
||||
[ "$(uname -m)" == "aarch64" ] && skip "See: https://github.com/kata-containers/kata-containers/issues/10928"
|
||||
|
||||
setup_common
|
||||
@@ -50,7 +49,6 @@ setup() {
|
||||
teardown() {
|
||||
[ "${KATA_HYPERVISOR}" == "dragonball" ] || [ "${KATA_HYPERVISOR}" == "cloud-hypervisor" ] && \
|
||||
skip "runtime-rs is still using the old vcpus allocation algorithm, skipping the test see https://github.com/kata-containers/kata-containers/issues/8660"
|
||||
[ "${KATA_HYPERVISOR}" = "qemu-runtime-rs" ] && skip "Requires CPU hotplug which isn't supported on ${KATA_HYPERVISOR} yet"
|
||||
[ "$(uname -m)" == "aarch64" ] && skip "See: https://github.com/kata-containers/kata-containers/issues/10928"
|
||||
|
||||
for pod in "${pods[@]}"; do
|
||||
|
Reference in New Issue
Block a user