From dbbd04a4cfd718a4b4f6953f14f84978ecd0b95c Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 10 Oct 2019 17:34:55 +0200 Subject: [PATCH] vmm: Implement VM resume To resume a VM, we unpark all its vCPU threads. Fixes: #333 Signed-off-by: Samuel Ortiz --- vmm/src/api/http.rs | 1 + vmm/src/api/http_endpoint.rs | 9 +++++++-- vmm/src/api/mod.rs | 14 ++++++++++++++ vmm/src/api/openapi/cloud-hypervisor.yaml | 15 +++++++++++++++ vmm/src/lib.rs | 16 ++++++++++++++++ vmm/src/vm.rs | 19 +++++++++++++++++++ 6 files changed, 72 insertions(+), 2 deletions(-) diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index ac31183e1..be4b80101 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -59,6 +59,7 @@ lazy_static! { r.routes.insert(endpoint!("/vm.delete"), Box::new(VmActionHandler::new(VmAction::Delete))); r.routes.insert(endpoint!("/vm.info"), Box::new(VmInfo {})); r.routes.insert(endpoint!("/vm.pause"), Box::new(VmActionHandler::new(VmAction::Pause))); + r.routes.insert(endpoint!("/vm.resume"), Box::new(VmActionHandler::new(VmAction::Resume))); r.routes.insert(endpoint!("/vm.shutdown"), Box::new(VmActionHandler::new(VmAction::Shutdown))); r.routes.insert(endpoint!("/vm.reboot"), Box::new(VmActionHandler::new(VmAction::Reboot))); r.routes.insert(endpoint!("/vmm.shutdown"), Box::new(VmmShutdown {})); diff --git a/vmm/src/api/http_endpoint.rs b/vmm/src/api/http_endpoint.rs index ce26041ec..2854f2fae 100644 --- a/vmm/src/api/http_endpoint.rs +++ b/vmm/src/api/http_endpoint.rs @@ -5,8 +5,8 @@ use crate::api::http::EndpointHandler; use crate::api::{ - vm_boot, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_shutdown, vmm_shutdown, - ApiError, ApiRequest, ApiResult, VmAction, VmConfig, + vm_boot, vm_create, vm_delete, vm_info, vm_pause, vm_reboot, vm_resume, vm_shutdown, + vmm_shutdown, ApiError, ApiRequest, ApiResult, VmAction, VmConfig, }; use micro_http::{Body, Method, Request, Response, StatusCode, Version}; use serde_json::Error as SerdeError; @@ -32,6 +32,9 @@ pub enum HttpError { /// Could not pause the VM VmPause(ApiError), + /// Could not pause the VM + VmResume(ApiError), + /// Could not shut a VM down VmShutdown(ApiError), @@ -107,6 +110,7 @@ impl VmActionHandler { VmAction::Shutdown => vm_shutdown, VmAction::Reboot => vm_reboot, VmAction::Pause => vm_pause, + VmAction::Resume => vm_resume, }); VmActionHandler { action_fn } @@ -127,6 +131,7 @@ impl EndpointHandler for VmActionHandler { ApiError::VmShutdown(_) => HttpError::VmShutdown(e), ApiError::VmReboot(_) => HttpError::VmReboot(e), ApiError::VmPause(_) => HttpError::VmPause(e), + ApiError::VmResume(_) => HttpError::VmResume(e), _ => HttpError::VmAction(e), }) { Ok(_) => Response::new(Version::Http11, StatusCode::OK), diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index 2154dd3bf..46769051f 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -79,6 +79,9 @@ pub enum ApiError { /// The VM could not be paused. VmPause(VmError), + /// The VM could not resume. + VmResume(VmError), + /// The VM is not booted. VmNotBooted, @@ -138,6 +141,9 @@ pub enum ApiRequest { /// Pause a VM. VmPause(Sender), + /// Resume a VM. + VmResume(Sender), + /// Shut the previously booted virtual machine down. /// If the VM was not previously booted or created, the VMM API server /// will send a VmShutdown error back. @@ -190,6 +196,9 @@ pub enum VmAction { /// Pause a VM Pause, + + /// Resume a VM + Resume, } fn vm_action(api_evt: EventFd, api_sender: Sender, action: VmAction) -> ApiResult<()> { @@ -201,6 +210,7 @@ fn vm_action(api_evt: EventFd, api_sender: Sender, action: VmAction) VmAction::Shutdown => ApiRequest::VmShutdown(response_sender), VmAction::Reboot => ApiRequest::VmReboot(response_sender), VmAction::Pause => ApiRequest::VmPause(response_sender), + VmAction::Resume => ApiRequest::VmResume(response_sender), }; // Send the VM request. @@ -232,6 +242,10 @@ pub fn vm_pause(api_evt: EventFd, api_sender: Sender) -> ApiResult<( vm_action(api_evt, api_sender, VmAction::Pause) } +pub fn vm_resume(api_evt: EventFd, api_sender: Sender) -> ApiResult<()> { + vm_action(api_evt, api_sender, VmAction::Resume) +} + pub fn vm_info(api_evt: EventFd, api_sender: Sender) -> ApiResult { let (response_sender, response_receiver) = channel(); diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index 812181a14..85171d357 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -95,6 +95,21 @@ paths: description: The VM instance could not pause because it is not booted. content: {} + /vm.resume: + put: + summary: Resume a previously paused VM instance. + operationId: resumeVM + responses: + 201: + description: The VM instance successfully paused. + content: {} + 404: + description: The VM instance could not resume because it is not booted yet + content: {} + 405: + description: The VM instance could not resume because it is not paused. + content: {} + /vm.shutdown: put: summary: Shut the VM instance down. diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 4060ae014..f9a576f2b 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -235,6 +235,14 @@ impl Vmm { } } + fn vm_resume(&mut self) -> result::Result<(), VmError> { + if let Some(ref mut vm) = self.vm { + vm.resume() + } else { + Err(VmError::VmNotBooted) + } + } + fn vm_shutdown(&mut self) -> result::Result<(), VmError> { if let Some(ref mut vm) = self.vm.take() { vm.shutdown() @@ -430,6 +438,14 @@ impl Vmm { sender.send(response).map_err(Error::ApiResponseSend)?; } + ApiRequest::VmResume(sender) => { + let response = self + .vm_resume() + .map_err(ApiError::VmResume) + .map(|_| ApiResponsePayload::Empty); + + sender.send(response).map_err(Error::ApiResponseSend)?; + } ApiRequest::VmmShutdown(sender) => { let response = self .vmm_shutdown() diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index abe15f837..bd25bf1bf 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -831,6 +831,25 @@ impl Vm { Ok(()) } + pub fn resume(&mut self) -> Result<()> { + // Toggle the vCPUs pause boolean + self.vcpus_pause_signalled.store(false, Ordering::SeqCst); + + // Unpark all the VCPU threads. + // Once unparked, the next thing they will do is checking for the pause + // boolean. Since it'll be set to false, they will exit their pause loop + // and go back to vmx root. + for vcpu_thread in self.threads.iter() { + vcpu_thread.thread().unpark(); + } + + // And we're back to Booted state. + let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?; + *state = VmState::Booted; + + Ok(()) + } + fn os_signal_handler(signals: Signals, console_input_clone: Arc) { for signal in signals.forever() { if signal == SIGWINCH {