diff --git a/docs/api.md b/docs/api.md index 5ce0b336a..14a672cbf 100644 --- a/docs/api.md +++ b/docs/api.md @@ -88,7 +88,8 @@ Reboot the VM | `/vm.reboot` | N/A Pause the VM | `/vm.pause` | N/A | N/A | The VM is booted Resume the VM | `/vm.resume` | N/A | N/A | The VM is paused Add/remove CPUs to/from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted -Remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted +Add/remove memory from the VM | `/vm.resize` | `/schemas/VmResize` | N/A | The VM is booted +Add/remove memory from a zone | `/vm.resize-zone` | `/schemas/VmResizeZone` | N/A | The VM is booted Dump the VM information | `/vm.info` | N/A | `/schemas/VmInfo` | The VM is created Add VFIO PCI device to the VM | `/vm.add-device` | `/schemas/VmAddDevice` | `/schemas/PciDeviceInfo` | The VM is booted Add disk device to the VM | `/vm.add-disk` | `/schemas/DiskConfig` | `/schemas/PciDeviceInfo` | The VM is booted diff --git a/src/bin/ch-remote.rs b/src/bin/ch-remote.rs index 39868715a..230e9f61b 100644 --- a/src/bin/ch-remote.rs +++ b/src/bin/ch-remote.rs @@ -251,6 +251,23 @@ fn resize_api_command( ) } +fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Result<(), Error> { + let resize_zone = vmm::api::VmResizeZoneData { + id: id.to_owned(), + desired_ram: size + .parse::() + .map_err(Error::InvalidMemorySize)? + .0, + }; + + simple_api_command( + socket, + "PUT", + "resize-zone", + Some(&serde_json::to_string(&resize_zone).unwrap()), + ) +} + fn add_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { let device_config = vmm::config::DeviceConfig::parse(config).map_err(Error::AddDeviceConfig)?; @@ -374,6 +391,19 @@ fn do_command(matches: &ArgMatches) -> Result<(), Error> { .unwrap() .value_of("balloon"), ), + Some("resize-zone") => resize_zone_api_command( + &mut socket, + matches + .subcommand_matches("resize-zone") + .unwrap() + .value_of("id") + .unwrap(), + matches + .subcommand_matches("resize-zone") + .unwrap() + .value_of("size") + .unwrap(), + ), Some("add-device") => add_device_api_command( &mut socket, matches @@ -552,6 +582,24 @@ fn main() { .number_of_values(1), ), ) + .subcommand( + SubCommand::with_name("resize-zone") + .about("Resize a memory zone") + .arg( + Arg::with_name("id") + .long("id") + .help("Memory zone identifier") + .takes_value(true) + .number_of_values(1), + ) + .arg( + Arg::with_name("size") + .long("size") + .help("New memory zone size in bytes (supports K/M/G suffix)") + .takes_value(true) + .number_of_values(1), + ), + ) .subcommand(SubCommand::with_name("resume").about("Resume the VM")) .subcommand(SubCommand::with_name("shutdown").about("Shutdown the VM")) .subcommand( diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index a01aa3bbf..d0c85aa11 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -68,6 +68,9 @@ pub enum HttpError { /// Could not resize a VM VmResize(ApiError), + /// Could not resize a memory zone + VmResizeZone(ApiError), + /// Could not add a device to a VM VmAddDevice(ApiError), @@ -204,6 +207,7 @@ lazy_static! { r.routes.insert(endpoint!("/vm.reboot"), Box::new(VmActionHandler::new(VmAction::Reboot))); r.routes.insert(endpoint!("/vm.remove-device"), Box::new(VmActionHandler::new(VmAction::RemoveDevice(Arc::default())))); r.routes.insert(endpoint!("/vm.resize"), Box::new(VmActionHandler::new(VmAction::Resize(Arc::default())))); + r.routes.insert(endpoint!("/vm.resize-zone"), Box::new(VmActionHandler::new(VmAction::ResizeZone(Arc::default())))); r.routes.insert(endpoint!("/vm.restore"), Box::new(VmActionHandler::new(VmAction::Restore(Arc::default())))); r.routes.insert(endpoint!("/vm.resume"), Box::new(VmActionHandler::new(VmAction::Resume))); r.routes.insert(endpoint!("/vm.shutdown"), Box::new(VmActionHandler::new(VmAction::Shutdown))); diff --git a/vmm/src/api/http_endpoint.rs b/vmm/src/api/http_endpoint.rs index a172df260..2bc6a9463 100644 --- a/vmm/src/api/http_endpoint.rs +++ b/vmm/src/api/http_endpoint.rs @@ -7,8 +7,8 @@ use crate::api::http::{error_response, EndpointHandler, HttpError}; use crate::api::{ vm_add_device, vm_add_disk, vm_add_fs, vm_add_net, vm_add_pmem, vm_add_vsock, vm_boot, vm_counters, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_remove_device, vm_resize, - vm_restore, vm_resume, vm_shutdown, vm_snapshot, vmm_ping, vmm_shutdown, ApiRequest, VmAction, - VmConfig, + vm_resize_zone, vm_restore, vm_resume, vm_shutdown, vm_snapshot, vmm_ping, vmm_shutdown, + ApiRequest, VmAction, VmConfig, }; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use std::sync::mpsc::Sender; @@ -132,6 +132,13 @@ impl EndpointHandler for VmActionHandler { ) .map_err(HttpError::VmResize), + ResizeZone(_) => vm_resize_zone( + api_notifier, + api_sender, + Arc::new(serde_json::from_slice(body.raw())?), + ) + .map_err(HttpError::VmResizeZone), + Restore(_) => vm_restore( api_notifier, api_sender, diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index 84560d891..1d513f99d 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -109,6 +109,9 @@ pub enum ApiError { /// The VM could not be resized VmResize(VmError), + /// The memory zone could not be resized. + VmResizeZone(VmError), + /// The device could not be added to the VM. VmAddDevice(VmError), @@ -156,6 +159,12 @@ pub struct VmResizeData { pub desired_ram_w_balloon: Option, } +#[derive(Clone, Deserialize, Serialize, Default)] +pub struct VmResizeZoneData { + pub id: String, + pub desired_ram: u64, +} + #[derive(Clone, Deserialize, Serialize, Default)] pub struct VmRemoveDeviceData { pub id: String, @@ -236,6 +245,9 @@ pub enum ApiRequest { /// Resize the VM. VmResize(Arc, Sender), + /// Resize the memory zone. + VmResizeZone(Arc, Sender), + /// Add a device to the VM. VmAddDevice(Arc, Sender), @@ -331,6 +343,9 @@ pub enum VmAction { /// Resize VM Resize(Arc), + /// Resize memory zone + ResizeZone(Arc), + /// Restore VM Restore(Arc), @@ -362,6 +377,7 @@ fn vm_action( AddVsock(v) => ApiRequest::VmAddVsock(v, response_sender), RemoveDevice(v) => ApiRequest::VmRemoveDevice(v, response_sender), Resize(v) => ApiRequest::VmResize(v, response_sender), + ResizeZone(v) => ApiRequest::VmResizeZone(v, response_sender), Restore(v) => ApiRequest::VmRestore(v, response_sender), Snapshot(v) => ApiRequest::VmSnapshot(v, response_sender), }; @@ -478,6 +494,14 @@ pub fn vm_resize( vm_action(api_evt, api_sender, VmAction::Resize(data)) } +pub fn vm_resize_zone( + api_evt: EventFd, + api_sender: Sender, + data: Arc, +) -> ApiResult> { + vm_action(api_evt, api_sender, VmAction::ResizeZone(data)) +} + pub fn vm_add_device( api_evt: EventFd, api_sender: Sender, diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index 3e07fcd5d..ca1a6ee09 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -150,6 +150,22 @@ paths: 404: description: The VM instance could not be resized because it is not created. + /vm.resize-zone: + put: + summary: Resize a memory zone + requestBody: + description: The target size for the memory zone + content: + application/json: + schema: + $ref: '#/components/schemas/VmResizeZone' + required: true + responses: + 204: + description: The memory zone was successfully resized. + 500: + description: The memory zone could not be resized. + /vm.add-device: put: summary: Add a new device to the VM @@ -774,6 +790,16 @@ components: type: integer format: int64 + VmResizeZone: + type: object + properties: + id: + type: string + desired_ram: + description: desired memory zone size in bytes + type: integer + format: int64 + VmAddDevice: type: object properties: diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 4bf74ef24..e8e4066de 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -517,6 +517,19 @@ impl Vmm { } } + fn vm_resize_zone(&mut self, id: String, desired_ram: u64) -> result::Result<(), VmError> { + if let Some(ref mut vm) = self.vm { + if let Err(e) = vm.resize_zone(id, desired_ram) { + error!("Error when resizing VM: {:?}", e); + Err(e) + } else { + Ok(()) + } + } else { + Err(VmError::VmNotRunning) + } + } + fn vm_add_device(&mut self, device_cfg: DeviceConfig) -> result::Result, VmError> { if let Some(ref mut vm) = self.vm { let info = vm.add_device(device_cfg).map_err(|e| { @@ -786,6 +799,16 @@ impl Vmm { .map(|_| ApiResponsePayload::Empty); sender.send(response).map_err(Error::ApiResponseSend)?; } + ApiRequest::VmResizeZone(resize_zone_data, sender) => { + let response = self + .vm_resize_zone( + resize_zone_data.id.clone(), + resize_zone_data.desired_ram, + ) + .map_err(ApiError::VmResizeZone) + .map(|_| ApiResponsePayload::Empty); + sender.send(response).map_err(Error::ApiResponseSend)?; + } ApiRequest::VmAddDevice(add_device_data, sender) => { let response = self .vm_add_device(add_device_data.as_ref().clone()) diff --git a/vmm/src/memory_manager.rs b/vmm/src/memory_manager.rs index 1fc7a5eea..65185b1a1 100644 --- a/vmm/src/memory_manager.rs +++ b/vmm/src/memory_manager.rs @@ -218,6 +218,9 @@ pub enum Error { /// Could not find specified memory zone identifier from hash map. MissingZoneIdentifier, + + /// Resizing the memory zone failed. + ResizeZone, } const ENABLE_FLAG: usize = 0; @@ -1171,8 +1174,7 @@ impl MemoryManager { if self.use_zones { error!( "Not allowed to resize guest memory when backed with user \ - defined memory regions.\n Try adding new memory regions \ - instead." + defined memory zones." ); return Err(Error::InvalidResizeWithMemoryZones); } @@ -1197,6 +1199,41 @@ impl MemoryManager { Ok(region) } + pub fn resize_zone( + &mut self, + id: &str, + desired_ram: u64, + config: &MemoryConfig, + ) -> Result>, Error> { + if !self.use_zones { + error!( + "Not allowed to resize guest memory zone when no zone is \ + defined." + ); + return Err(Error::ResizeZone); + } + + if let Some(zones) = &config.zones { + for zone in zones.iter() { + if zone.id == id { + if desired_ram >= zone.size { + return self.virtiomem_resize(id, desired_ram - zone.size); + } else { + error!( + "Invalid to ask less ({}) than boot RAM ({}) for \ + this memory zone", + desired_ram, zone.size, + ); + return Err(Error::ResizeZone); + } + } + } + } + + error!("Could not find the memory zone {} for the resize", id); + Err(Error::ResizeZone) + } + #[cfg(target_arch = "x86_64")] pub fn setup_sgx(&mut self, sgx_epc_config: Vec) -> Result<(), Error> { // Go over each EPC section and verify its size is a 4k multiple. At diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index fc18b86ca..6a02097af 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -922,6 +922,25 @@ impl Vm { Ok(()) } + pub fn resize_zone(&mut self, id: String, desired_memory: u64) -> Result<()> { + let new_region = self + .memory_manager + .lock() + .unwrap() + .resize_zone(&id, desired_memory, &self.config.lock().unwrap().memory) + .map_err(Error::MemoryManager)?; + + if let Some(new_region) = &new_region { + self.device_manager + .lock() + .unwrap() + .update_memory(&new_region) + .map_err(Error::DeviceManager)?; + } + + Ok(()) + } + #[cfg(not(feature = "pci_support"))] pub fn add_device(&mut self, mut _device_cfg: DeviceConfig) -> Result { Err(Error::NoPciSupport)