mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-22 13:45:20 +00:00
vmm: Add a 'resize-zone' action to the API actions
Implement a new VM action called 'resize-zone' allowing the user to resize one specific memory zone at a time. This relies on all the preliminary work from the previous commits to resize each virtio-mem device independently from each others. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
parent
141df701dd
commit
015c78411e
@ -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
|
||||
|
@ -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::<ByteSized>()
|
||||
.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(
|
||||
|
@ -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)));
|
||||
|
@ -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,
|
||||
|
@ -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<u64>,
|
||||
}
|
||||
|
||||
#[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<VmResizeData>, Sender<ApiResponse>),
|
||||
|
||||
/// Resize the memory zone.
|
||||
VmResizeZone(Arc<VmResizeZoneData>, Sender<ApiResponse>),
|
||||
|
||||
/// Add a device to the VM.
|
||||
VmAddDevice(Arc<DeviceConfig>, Sender<ApiResponse>),
|
||||
|
||||
@ -331,6 +343,9 @@ pub enum VmAction {
|
||||
/// Resize VM
|
||||
Resize(Arc<VmResizeData>),
|
||||
|
||||
/// Resize memory zone
|
||||
ResizeZone(Arc<VmResizeZoneData>),
|
||||
|
||||
/// Restore VM
|
||||
Restore(Arc<RestoreConfig>),
|
||||
|
||||
@ -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<ApiRequest>,
|
||||
data: Arc<VmResizeZoneData>,
|
||||
) -> ApiResult<Option<Body>> {
|
||||
vm_action(api_evt, api_sender, VmAction::ResizeZone(data))
|
||||
}
|
||||
|
||||
pub fn vm_add_device(
|
||||
api_evt: EventFd,
|
||||
api_sender: Sender<ApiRequest>,
|
||||
|
@ -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:
|
||||
|
@ -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<Vec<u8>, 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())
|
||||
|
@ -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<Option<Arc<GuestRegionMmap>>, 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<SgxEpcConfig>) -> Result<(), Error> {
|
||||
// Go over each EPC section and verify its size is a 4k multiple. At
|
||||
|
@ -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<PciDeviceInfo> {
|
||||
Err(Error::NoPciSupport)
|
||||
|
Loading…
Reference in New Issue
Block a user