diff --git a/Cargo.lock b/Cargo.lock index 01185dadb..f42b2bcc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1111,6 +1111,7 @@ name = "vmm" version = "0.1.0" dependencies = [ "acpi_tables 0.1.0", + "anyhow 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "arch 0.1.0", "devices 0.1.0", "epoll 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index 83f54ecc1..c0a35fdd8 100644 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -13,6 +13,7 @@ cmos = ["devices/cmos"] [dependencies] acpi_tables = { path = "../acpi_tables", optional = true } +anyhow = "1.0" arch = { path = "../arch" } devices = { path = "../devices" } epoll = ">=4.0.1" diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 247744c37..ac41295cb 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -23,6 +23,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::mpsc::{Receiver, RecvError, SendError, Sender}; use std::sync::{Arc, Mutex}; use std::{result, thread}; +use vm_device::Pausable; use vmm_sys_util::eventfd::EventFd; pub mod api; @@ -236,7 +237,7 @@ impl Vmm { fn vm_pause(&mut self) -> result::Result<(), VmError> { if let Some(ref mut vm) = self.vm { - vm.pause() + vm.pause().map_err(VmError::Pause) } else { Err(VmError::VmNotRunning) } @@ -244,7 +245,7 @@ impl Vmm { fn vm_resume(&mut self) -> result::Result<(), VmError> { if let Some(ref mut vm) = self.vm { - vm.resume() + vm.resume().map_err(VmError::Resume) } else { Err(VmError::VmNotRunning) } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 894ef1de2..4881c9f0b 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -9,6 +9,7 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause // +extern crate anyhow; extern crate arch; extern crate devices; extern crate epoll; @@ -26,6 +27,7 @@ extern crate vm_virtio; use crate::config::VmConfig; use crate::cpu; use crate::device_manager::{get_win_size, Console, DeviceManager, DeviceManagerError}; +use anyhow::anyhow; use arch::RegionType; use devices::{ioapic, HotPlugNotificationType}; use kvm_bindings::{kvm_enable_cap, kvm_userspace_memory_region, KVM_CAP_SPLIT_IRQCHIP}; @@ -43,7 +45,7 @@ use std::os::unix::io::FromRawFd; use std::sync::{Arc, Mutex, RwLock}; use std::{result, str, thread}; use vm_allocator::{GsiApic, SystemAllocator}; -use vm_device::{MigratableError, Pausable}; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::guest_memory::FileOffset; use vm_memory::{ Address, Bytes, Error as MmapError, GuestAddress, GuestMemory, GuestMemoryMmap, @@ -158,6 +160,12 @@ pub enum Error { /// Cannot resume cpus ResumeCpus(MigratableError), + + /// Cannot pause VM + Pause(MigratableError), + + /// Cannot resume VM + Resume(MigratableError), } pub type Result = result::Result; @@ -629,43 +637,6 @@ impl Vm { Ok(()) } - pub fn pause(&mut self) -> Result<()> { - let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?; - let new_state = VmState::Paused; - - state.valid_transition(new_state)?; - - self.cpu_manager - .lock() - .unwrap() - .pause() - .map_err(Error::PauseCpus)?; - self.devices.pause().map_err(Error::PauseDevices)?; - - *state = new_state; - - Ok(()) - } - - pub fn resume(&mut self) -> Result<()> { - let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?; - let new_state = VmState::Running; - - state.valid_transition(new_state)?; - - self.devices.resume().map_err(Error::ResumeDevices)?; - self.cpu_manager - .lock() - .unwrap() - .resume() - .map_err(Error::ResumeCpus)?; - - // And we're back to the Running state. - *state = new_state; - - Ok(()) - } - pub fn resize(&mut self, desired_vcpus: u8) -> Result<()> { self.cpu_manager .lock() @@ -703,7 +674,7 @@ impl Vm { pub fn boot(&mut self) -> Result<()> { let current_state = self.get_state()?; if current_state == VmState::Paused { - return self.resume(); + return self.resume().map_err(Error::Resume); } let new_state = VmState::Running; @@ -785,6 +756,50 @@ impl Vm { } } +impl Pausable for Vm { + fn pause(&mut self) -> std::result::Result<(), MigratableError> { + let mut state = self + .state + .try_write() + .map_err(|e| MigratableError::Pause(anyhow!("Could not get VM state: {}", e)))?; + let new_state = VmState::Paused; + + state + .valid_transition(new_state) + .map_err(|e| MigratableError::Pause(anyhow!("Invalid transition: {:?}", e)))?; + + self.cpu_manager.lock().unwrap().pause()?; + self.devices.pause()?; + + *state = new_state; + + Ok(()) + } + + fn resume(&mut self) -> std::result::Result<(), MigratableError> { + let mut state = self + .state + .try_write() + .map_err(|e| MigratableError::Resume(anyhow!("Could not get VM state: {}", e)))?; + let new_state = VmState::Running; + + state + .valid_transition(new_state) + .map_err(|e| MigratableError::Pause(anyhow!("Invalid transition: {:?}", e)))?; + + self.devices.resume()?; + self.cpu_manager.lock().unwrap().resume()?; + + // And we're back to the Running state. + *state = new_state; + + Ok(()) + } +} + +impl Snapshotable for Vm {} +impl Migratable for Vm {} + #[cfg(test)] mod tests { use super::*;