diff --git a/src/main.rs b/src/main.rs index 76ba05115..aa84a2769 100755 --- a/src/main.rs +++ b/src/main.rs @@ -286,7 +286,7 @@ fn main() { ); if let Err(e) = vmm::start_vm_loop(Arc::new(vm_config)) { - println!("Guest boot failed: {}", e); + println!("Guest boot failed: {:?}", e); process::exit(1); } } diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index ebd2f4ae1..1f823ed0c 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -7,12 +7,14 @@ extern crate log; extern crate vmm_sys_util; +use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload}; +use crate::vm::{Error as VmError, ExitBehaviour, Vm}; use libc::EFD_NONBLOCK; -use std::fmt::{self, Display}; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; -use std::result; +use std::sync::mpsc::{channel, Receiver, RecvError, SendError, Sender}; use std::sync::Arc; +use std::{result, thread}; use vmm_sys_util::eventfd::EventFd; pub mod api; @@ -21,38 +23,59 @@ pub mod device_manager; pub mod vm; use self::config::VmConfig; -use self::vm::{ExitBehaviour, Vm}; +//use self::vm::{ExitBehaviour, Vm}; -/// Errors associated with VM management +/// Errors associated with VMM management #[derive(Debug)] +#[allow(clippy::large_enum_variant)] pub enum Error { + /// API request receive error + ApiRequestRecv(RecvError), + + /// API response receive error + ApiResponseRecv(RecvError), + + /// API request send error + ApiRequestSend(SendError), + + /// API response send error + ApiResponseSend(SendError), + + /// Cannot create a VM from the API + ApiVmCreate(ApiError), + + /// Cannot start a VM from the API + ApiVmStart(ApiError), + + /// Cannot clone EventFd. + EventFdClone(io::Error), + /// Cannot create EventFd. - EventFd(io::Error), + EventFdCreate(io::Error), - /// Cannot create a new VM. - VmNew(vm::Error), + /// Cannot read from EventFd. + EventFdRead(io::Error), - /// Cannot start a VM. - VmStart(vm::Error), + /// Cannot write to EventFd. + EventFdWrite(io::Error), - /// Cannot stop a VM. - VmStop(vm::Error), + /// Cannot create epoll context. + Epoll(io::Error), + + /// Cannot handle the VM STDIN stream + Stdin(VmError), + + /// Cannot create a VM + VmCreate(VmError), + + /// Cannot start a VM + VmStart(VmError), + + /// Cannot stop a VM + VmStop(VmError), } pub type Result = result::Result; -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - - match self { - EventFd(e) => write!(f, "Can not create EventFd: {:?}", e), - VmNew(e) => write!(f, "Can not create a new virtual machine: {:?}", e), - VmStart(e) => write!(f, "Can not start a new virtual machine: {:?}", e), - VmStop(e) => write!(f, "Can not stop a virtual machine: {:?}", e), - } - } -} - #[derive(Debug, Clone, Copy, PartialEq)] pub enum EpollDispatch { Exit, @@ -121,9 +144,142 @@ impl AsRawFd for EpollContext { } } +pub struct Vmm { + epoll: EpollContext, + exit_evt: EventFd, + reset_evt: EventFd, + api_evt: EventFd, + vm: Option, +} + +impl Vmm { + fn new(api_evt: EventFd) -> Result { + let mut epoll = EpollContext::new().map_err(Error::Epoll)?; + let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; + let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; + + if unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0 { + epoll.add_stdin().map_err(Error::Epoll)?; + } + + epoll + .add_event(&exit_evt, EpollDispatch::Exit) + .map_err(Error::Epoll)?; + + epoll + .add_event(&reset_evt, EpollDispatch::Reset) + .map_err(Error::Epoll)?; + + epoll + .add_event(&api_evt, EpollDispatch::Api) + .map_err(Error::Epoll)?; + + Ok(Vmm { + epoll, + exit_evt, + reset_evt, + api_evt, + vm: None, + }) + } + + fn control_loop(&mut self, api_receiver: Arc>) -> Result { + const EPOLL_EVENTS_LEN: usize = 100; + + let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; + let epoll_fd = self.epoll.as_raw_fd(); + + let exit_behaviour; + + 'outer: loop { + let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { + Ok(res) => res, + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + // It's well defined from the epoll_wait() syscall + // documentation that the epoll loop can be interrupted + // before any of the requested events occurred or the + // timeout expired. In both those cases, epoll_wait() + // returns an error of type EINTR, but this should not + // be considered as a regular error. Instead it is more + // appropriate to retry, by calling into epoll_wait(). + continue; + } + return Err(Error::Epoll(e)); + } + }; + + for event in events.iter().take(num_events) { + let dispatch_idx = event.data as usize; + + if let Some(dispatch_type) = self.epoll.dispatch_table[dispatch_idx] { + match dispatch_type { + EpollDispatch::Exit => { + // Consume the event. + self.exit_evt.read().map_err(Error::EventFdRead)?; + exit_behaviour = ExitBehaviour::Shutdown; + + break 'outer; + } + EpollDispatch::Reset => { + // Consume the event. + self.reset_evt.read().map_err(Error::EventFdRead)?; + exit_behaviour = ExitBehaviour::Reset; + + break 'outer; + } + EpollDispatch::Stdin => { + if let Some(ref vm) = self.vm { + vm.handle_stdin().map_err(Error::Stdin)?; + } + } + EpollDispatch::Api => { + // Consume the event. + self.api_evt.read().map_err(Error::EventFdRead)?; + + // Read from the API receiver channel + let api_request = api_receiver.recv().map_err(Error::ApiRequestRecv)?; + + match api_request { + ApiRequest::VmCreate(config, sender) => { + let exit_evt = + self.exit_evt.try_clone().map_err(Error::EventFdClone)?; + let reset_evt = + self.reset_evt.try_clone().map_err(Error::EventFdClone)?; + let response = match Vm::new(config, exit_evt, reset_evt) { + Ok(vm) => { + self.vm = Some(vm); + Ok(ApiResponsePayload::Empty) + } + Err(e) => Err(ApiError::VmCreate(e)), + }; + + sender.send(response).map_err(Error::ApiResponseSend)?; + } + ApiRequest::VmStart(sender) => { + if let Some(ref mut vm) = self.vm { + let response = match vm.start() { + Ok(_) => Ok(ApiResponsePayload::Empty), + Err(e) => Err(ApiError::VmStart(e)), + }; + + sender.send(response).map_err(Error::ApiResponseSend)?; + } + } + } + } + } + } + } + } + + Ok(exit_behaviour) + } +} + pub fn start_vm_loop(config: Arc) -> Result<()> { - let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; - let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; + let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; + let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; loop { let mut vm = Vm::new( @@ -131,14 +287,14 @@ pub fn start_vm_loop(config: Arc) -> Result<()> { exit_evt.try_clone().unwrap(), reset_evt.try_clone().unwrap(), ) - .map_err(Error::VmNew)?; + .expect("Could not create VM"); - if vm.start().map_err(Error::VmStart)? == ExitBehaviour::Shutdown { - vm.stop().map_err(Error::VmStop)?; + if vm.start().expect("Could not start VM") == ExitBehaviour::Shutdown { + vm.stop().expect("Could not stop VM"); break; } - vm.stop().map_err(Error::VmStop)?; + vm.stop().expect("Could not stop VM"); #[cfg(not(feature = "acpi"))] break;