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:
Sebastien Boeuf 2020-09-10 17:34:15 +02:00
parent 141df701dd
commit 015c78411e
9 changed files with 194 additions and 5 deletions

View File

@ -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

View File

@ -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(

View File

@ -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)));

View File

@ -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,

View File

@ -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>,

View File

@ -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:

View File

@ -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())

View File

@ -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

View File

@ -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)