// Copyright © 2019 Intel Corporation // // SPDX-License-Identifier: Apache-2.0 // #[macro_use] 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::io; use std::os::unix::io::{AsRawFd, RawFd}; 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; pub mod config; pub mod device_manager; pub mod vm; use self::config::VmConfig; //use self::vm::{ExitBehaviour, Vm}; /// 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. EventFdCreate(io::Error), /// Cannot read from EventFd. EventFdRead(io::Error), /// Cannot write to EventFd. EventFdWrite(io::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), /// Cannot create VMM thread VmmThreadSpawn(io::Error), } pub type Result = result::Result; #[derive(Debug, Clone, Copy, PartialEq)] pub enum EpollDispatch { Exit, Reset, Stdin, Api, } pub struct EpollContext { raw_fd: RawFd, dispatch_table: Vec>, } impl EpollContext { pub fn new() -> result::Result { let raw_fd = epoll::create(true)?; // Initial capacity needs to be large enough to hold: // * 1 exit event // * 1 reset event // * 1 stdin event // * 1 API event let mut dispatch_table = Vec::with_capacity(5); dispatch_table.push(None); Ok(EpollContext { raw_fd, dispatch_table, }) } pub fn add_stdin(&mut self) -> result::Result<(), io::Error> { let dispatch_index = self.dispatch_table.len() as u64; epoll::ctl( self.raw_fd, epoll::ControlOptions::EPOLL_CTL_ADD, libc::STDIN_FILENO, epoll::Event::new(epoll::Events::EPOLLIN, dispatch_index), )?; self.dispatch_table.push(Some(EpollDispatch::Stdin)); Ok(()) } fn add_event(&mut self, fd: &T, token: EpollDispatch) -> result::Result<(), io::Error> where T: AsRawFd, { let dispatch_index = self.dispatch_table.len() as u64; epoll::ctl( self.raw_fd, epoll::ControlOptions::EPOLL_CTL_ADD, fd.as_raw_fd(), epoll::Event::new(epoll::Events::EPOLLIN, dispatch_index), )?; self.dispatch_table.push(Some(token)); Ok(()) } } impl AsRawFd for EpollContext { fn as_raw_fd(&self) -> RawFd { self.raw_fd } } pub fn start_vmm_thread( api_event: EventFd, api_receiver: Receiver, ) -> Result>> { thread::Builder::new() .name("vmm".to_string()) .spawn(move || { let mut vmm = Vmm::new(api_event)?; let receiver = Arc::new(api_receiver); 'outer: loop { match vmm.control_loop(Arc::clone(&receiver)) { Ok(ExitBehaviour::Reset) => { // The VMM control loop exites with a reset behaviour. // We have to reboot the VM, i.e. we create a new VM // based on the same VM config, start it and restart // the control loop. // Without ACPI, a reset is equivalent to a shutdown #[cfg(not(feature = "acpi"))] { if let Some(ref mut vm) = vmm.vm { vm.stop().map_err(Error::VmStop)?; break 'outer; } } // First we stop the current VM and create a new one. if let Some(ref mut vm) = vmm.vm { let config = vm.get_config(); vm.stop().map_err(Error::VmStop)?; let exit_evt = vmm.exit_evt.try_clone().map_err(Error::EventFdClone)?; let reset_evt = vmm.reset_evt.try_clone().map_err(Error::EventFdClone)?; vmm.vm = Some( Vm::new(config, exit_evt, reset_evt).map_err(Error::VmCreate)?, ); } // Then we start the new VM. if let Some(ref mut vm) = vmm.vm { vm.start().map_err(Error::VmStart)?; } // Continue and restart the VMM control loop continue 'outer; } Ok(ExitBehaviour::Shutdown) => { // The VMM control loop exites with a shutdown behaviour. // We have to stop the VM and we exit thr thread. if let Some(ref mut vm) = vmm.vm { vm.stop().map_err(Error::VmStop)?; } break 'outer; } Err(e) => return Err(e), } } Ok(()) }) .map_err(Error::VmmThreadSpawn) } 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::EventFdCreate)?; let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; loop { let mut vm = Vm::new( config.clone(), exit_evt.try_clone().unwrap(), reset_evt.try_clone().unwrap(), ) .expect("Could not create VM"); if vm.start().expect("Could not start VM") == ExitBehaviour::Shutdown { vm.stop().expect("Could not stop VM"); break; } vm.stop().expect("Could not stop VM"); #[cfg(not(feature = "acpi"))] break; } Ok(()) }