From 328383adc34762ccf4bc7724ce5eaf88d1966a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= Date: Tue, 7 Apr 2026 15:30:15 +0200 Subject: [PATCH] runtime-rs: Fix initial vCPU / memory with static_sandbox_resource_mgmt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InitialSizeManager::setup_config() is responsible for applying the sandbox workload sizing (computed from containerd/CRI-O sandbox annotations) to the hypervisor configuration before VM creation. Previously, the workload vCPU count was only logged but never actually added to default_vcpus, so the VM was always created with only the base vCPUs from the configuration/annotations. This caused the k8s-sandbox-vcpus-allocation test to fail with qemu-snp-runtime-rs: a pod with default_vcpus=0.75 and a container CPU limit of 1.2 should see ceil(0.75 + 1.2) = 2 vCPUs, but only got 1. Additionally, the workload memory was being added to default_memory unconditionally, diverging from the Go runtime which only applies both CPU and memory additions when static_sandbox_resource_mgmt is enabled. In the non-static path, adding workload resources here would cause double-counting: once from setup_config() at sandbox creation, and again from update_cpu_resources()/update_mem_resources() when individual containers are added. Guard both additions behind static_sandbox_resource_mgmt, matching the Go runtime's behavior in src/runtime/pkg/oci/utils.go: if sandboxConfig.StaticResourceMgmt { sandboxConfig.HypervisorConfig.NumVCPUsF += sandboxConfig.SandboxResources.WorkloadCPUs sandboxConfig.HypervisorConfig.MemorySize += sandboxConfig.SandboxResources.WorkloadMemMB } Fixes: k8s-sandbox-vcpus-allocation test failure on qemu-snp-runtime-rs Signed-off-by: Fabiano FidĂȘncio Made-with: Cursor --- .../resource/src/cpu_mem/initial_size.rs | 130 +++++++++++++++++- 1 file changed, 124 insertions(+), 6 deletions(-) diff --git a/src/runtime-rs/crates/resource/src/cpu_mem/initial_size.rs b/src/runtime-rs/crates/resource/src/cpu_mem/initial_size.rs index 9b56af9734..9b1b863349 100644 --- a/src/runtime-rs/crates/resource/src/cpu_mem/initial_size.rs +++ b/src/runtime-rs/crates/resource/src/cpu_mem/initial_size.rs @@ -142,15 +142,23 @@ impl InitialSizeManager { if self.resource.vcpu > 0.0 { info!(sl!(), "resource with vcpu {}", self.resource.vcpu); + if config.runtime.static_sandbox_resource_mgmt { + hv.cpu_info.default_vcpus += self.resource.vcpu; + let new_vcpus_ceil = hv.cpu_info.default_vcpus.ceil() as u32; + if hv.cpu_info.default_maxvcpus < new_vcpus_ceil { + hv.cpu_info.default_maxvcpus = new_vcpus_ceil; + } + } } self.resource.orig_toml_default_mem = hv.memory_info.default_memory; if self.resource.mem_mb > 0 { - // since the memory overhead introduced by kata-agent and system components - // will really affect the amount of memory the user can use, so we choose to - // plus the default_memory here, instead of overriding it. - // (if we override the default_memory here, and user apllications still - // use memory as they orignally expected, it would be easy to OOM.) - hv.memory_info.default_memory += self.resource.mem_mb; + info!(sl!(), "resource with memory {}", self.resource.mem_mb); + if config.runtime.static_sandbox_resource_mgmt { + hv.memory_info.default_memory += self.resource.mem_mb; + if hv.memory_info.default_maxmemory < hv.memory_info.default_memory { + hv.memory_info.default_maxmemory = hv.memory_info.default_memory; + } + } } Ok(()) } @@ -366,4 +374,114 @@ mod tests { ); } } + + fn make_config( + default_vcpus: f32, + default_maxvcpus: u32, + default_memory: u32, + default_maxmemory: u32, + static_sandbox_resource_mgmt: bool, + ) -> TomlConfig { + use kata_types::config::Hypervisor; + + let mut config = TomlConfig::default(); + config + .hypervisor + .insert("qemu".to_owned(), Hypervisor::default()); + config + .hypervisor + .entry("qemu".to_owned()) + .and_modify(|hv| { + hv.cpu_info.default_vcpus = default_vcpus; + hv.cpu_info.default_maxvcpus = default_maxvcpus; + hv.memory_info.default_memory = default_memory; + hv.memory_info.default_maxmemory = default_maxmemory; + }); + config.runtime.hypervisor_name = "qemu".to_owned(); + config.runtime.static_sandbox_resource_mgmt = static_sandbox_resource_mgmt; + config + } + + #[test] + fn test_setup_config_static_applies_vcpu_and_memory() { + let mut config = make_config(1.0, 4, 256, 4096, true); + let mut mgr = InitialSizeManager { + resource: InitialSize { + vcpu: 1.2, + mem_mb: 512, + orig_toml_default_mem: 0, + }, + }; + + mgr.setup_config(&mut config).unwrap(); + let hv = config.hypervisor.get("qemu").unwrap(); + assert_eq!(hv.cpu_info.default_vcpus, 2.2); + assert_eq!(hv.memory_info.default_memory, 768); + } + + #[test] + fn test_setup_config_non_static_does_not_apply() { + let mut config = make_config(1.0, 4, 256, 4096, false); + let mut mgr = InitialSizeManager { + resource: InitialSize { + vcpu: 1.2, + mem_mb: 512, + orig_toml_default_mem: 0, + }, + }; + + mgr.setup_config(&mut config).unwrap(); + let hv = config.hypervisor.get("qemu").unwrap(); + assert_eq!(hv.cpu_info.default_vcpus, 1.0); + assert_eq!(hv.memory_info.default_memory, 256); + } + + #[test] + fn test_setup_config_clamps_maxvcpus() { + let mut config = make_config(1.0, 2, 256, 4096, true); + let mut mgr = InitialSizeManager { + resource: InitialSize { + vcpu: 2.5, + mem_mb: 0, + orig_toml_default_mem: 0, + }, + }; + + mgr.setup_config(&mut config).unwrap(); + let hv = config.hypervisor.get("qemu").unwrap(); + assert_eq!(hv.cpu_info.default_vcpus, 3.5); + assert_eq!(hv.cpu_info.default_maxvcpus, 4); + } + + #[test] + fn test_setup_config_clamps_maxmemory() { + let mut config = make_config(1.0, 4, 256, 300, true); + let mut mgr = InitialSizeManager { + resource: InitialSize { + vcpu: 0.0, + mem_mb: 512, + orig_toml_default_mem: 0, + }, + }; + + mgr.setup_config(&mut config).unwrap(); + let hv = config.hypervisor.get("qemu").unwrap(); + assert_eq!(hv.memory_info.default_memory, 768); + assert_eq!(hv.memory_info.default_maxmemory, 768); + } + + #[test] + fn test_setup_config_preserves_orig_toml_default_mem() { + let mut config = make_config(1.0, 4, 256, 4096, true); + let mut mgr = InitialSizeManager { + resource: InitialSize { + vcpu: 0.0, + mem_mb: 128, + orig_toml_default_mem: 0, + }, + }; + + mgr.setup_config(&mut config).unwrap(); + assert_eq!(mgr.get_orig_toml_default_mem(), 256); + } }