runtime-rs: Add the memory hotplug for cloud-hypervisor

For cloud-hypervisor, currently only hot plugging of memory is
supported, but hot unplugging of memory is not supported. In addition,
by default, cloud-hypervisor uses ACPI-based memory hot-plugging instead
of virtio-mem based memory hot-plugging.

Signed-off-by: Fupan Li <fupan.lfp@antgroup.com>
This commit is contained in:
Fupan Li
2025-06-18 11:54:08 +08:00
committed by Fupan Li
parent 7df29605a4
commit a3671b7a5c
3 changed files with 98 additions and 13 deletions

View File

@@ -74,8 +74,8 @@ pub struct CloudHypervisorInner {
// None.
pub(crate) ch_features: Option<Vec<String>>,
/// Size of memory block of guest OS in MB (currently unused)
pub(crate) _guest_memory_block_size_mb: u32,
/// Size of memory block of guest OS in MB
pub(crate) guest_memory_block_size_mb: u32,
pub(crate) exit_notify: Option<mpsc::Sender<i32>>,
}
@@ -117,7 +117,7 @@ impl CloudHypervisorInner {
tasks: None,
guest_protection_to_use: GuestProtection::NoProtection,
ch_features: None,
_guest_memory_block_size_mb: 0,
guest_memory_block_size_mb: 0,
exit_notify,
}

View File

@@ -7,15 +7,18 @@ use super::inner::CloudHypervisorInner;
use crate::ch::utils::get_api_socket_path;
use crate::ch::utils::get_vsock_path;
use crate::kernel_param::KernelParams;
use crate::utils::{get_jailer_root, get_sandbox_path};
use crate::utils::{bytes_to_megs, get_jailer_root, get_sandbox_path, megs_to_bytes};
use crate::MemoryConfig;
use crate::VM_ROOTFS_DRIVER_BLK;
use crate::VM_ROOTFS_DRIVER_PMEM;
use crate::{VcpuThreadIds, VmmState};
use anyhow::{anyhow, Context, Result};
use ch_config::ch_api::{
cloud_hypervisor_vm_create, cloud_hypervisor_vm_start, cloud_hypervisor_vmm_ping,
cloud_hypervisor_vmm_shutdown,
use ch_config::{
ch_api::{
cloud_hypervisor_vm_create, cloud_hypervisor_vm_info, cloud_hypervisor_vm_resize,
cloud_hypervisor_vm_start, cloud_hypervisor_vmm_ping, cloud_hypervisor_vmm_shutdown,
},
VmResize,
};
use ch_config::{guest_protection_is_tdx, NamedHypervisorConfig, VmConfig};
use core::future::poll_fn;
@@ -748,17 +751,99 @@ impl CloudHypervisorInner {
}
pub(crate) fn set_guest_memory_block_size(&mut self, size: u32) {
self._guest_memory_block_size_mb = size;
self.guest_memory_block_size_mb = bytes_to_megs(size as u64);
}
pub(crate) fn guest_memory_block_size_mb(&self) -> u32 {
self._guest_memory_block_size_mb
self.guest_memory_block_size_mb
}
pub(crate) fn resize_memory(&self, _new_mem_mb: u32) -> Result<(u32, MemoryConfig)> {
warn!(sl!(), "CH memory resize not implemented - see https://github.com/kata-containers/kata-containers/issues/8801");
pub(crate) async fn resize_memory(&self, new_mem_mb: u32) -> Result<(u32, MemoryConfig)> {
let socket = self
.api_socket
.as_ref()
.ok_or("missing socket")
.map_err(|e| anyhow!(e))?;
Ok((0, MemoryConfig::default()))
let vminfo =
cloud_hypervisor_vm_info(socket.try_clone().context("failed to clone socket")?)
.await
.context("get vminfo")?;
let current_mem_size = vminfo.config.memory.size;
let new_total_mem = megs_to_bytes(new_mem_mb);
info!(
sl!(),
"cloud-hypervisor::resize_memory(): asked to resize memory to {} MB, current memory is {} MB", new_mem_mb, bytes_to_megs(current_mem_size)
);
// Early Check to verify if boot memory is the same as requested
if current_mem_size == new_total_mem {
info!(sl!(), "VM alreay has requested memory");
return Ok((new_mem_mb, MemoryConfig::default()));
}
if current_mem_size > new_total_mem {
info!(sl!(), "Remove memory is not supported, nothing to do");
return Ok((new_mem_mb, MemoryConfig::default()));
}
let guest_mem_block_size = megs_to_bytes(self.guest_memory_block_size_mb);
let mut new_hotplugged_mem = new_total_mem - current_mem_size;
info!(
sl!(),
"new hotplugged mem before alignment: {} B ({} MB), guest_mem_block_size: {} MB",
new_hotplugged_mem,
bytes_to_megs(new_hotplugged_mem),
bytes_to_megs(guest_mem_block_size)
);
let is_unaligned = new_hotplugged_mem % guest_mem_block_size != 0;
if is_unaligned {
new_hotplugged_mem = ch_config::convert::checked_next_multiple_of(
new_hotplugged_mem,
guest_mem_block_size,
)
.ok_or(anyhow!(format!(
"alignment of {} B to the block size of {} B failed",
new_hotplugged_mem, guest_mem_block_size
)))?
}
let new_total_mem_aligned = new_hotplugged_mem + current_mem_size;
let max_total_mem = megs_to_bytes(self.config.memory_info.default_maxmemory);
if new_total_mem_aligned > max_total_mem {
return Err(anyhow!(
"requested memory ({} MB) is greater than maximum allowed ({} MB)",
bytes_to_megs(new_total_mem_aligned),
self.config.memory_info.default_maxmemory
));
}
info!(
sl!(),
"hotplugged mem from {} MB to {} MB)",
bytes_to_megs(current_mem_size),
bytes_to_megs(new_total_mem_aligned)
);
let vmresize = VmResize {
desired_ram: Some(new_total_mem_aligned),
..Default::default()
};
cloud_hypervisor_vm_resize(
socket.try_clone().context("failed to clone socket")?,
vmresize,
)
.await
.context("resize memory")?;
Ok((new_mem_mb, MemoryConfig::default()))
}
}

View File

@@ -206,7 +206,7 @@ impl Hypervisor for CloudHypervisor {
async fn resize_memory(&self, new_mem_mb: u32) -> Result<(u32, MemoryConfig)> {
let inner = self.inner.read().await;
inner.resize_memory(new_mem_mb)
inner.resize_memory(new_mem_mb).await
}
async fn get_passfd_listener_addr(&self) -> Result<(String, u32)> {