Files
cloud-hypervisor/vmm/src/lib.rs
Samuel Ortiz 8188074300 main: Start the VMM thread
We now start the main VMM thread, which will be listening for VM and IPC
related events.
In order to start the configured VM, we no longer directly call the VM
API but we use the IPC instead, to first create and then start a VM.

Fixes: #303

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2019-09-26 16:21:14 +02:00

386 lines
12 KiB
Rust

// 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<ApiRequest>),
/// API response send error
ApiResponseSend(SendError<ApiResponse>),
/// 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<T> = result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EpollDispatch {
Exit,
Reset,
Stdin,
Api,
}
pub struct EpollContext {
raw_fd: RawFd,
dispatch_table: Vec<Option<EpollDispatch>>,
}
impl EpollContext {
pub fn new() -> result::Result<EpollContext, io::Error> {
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<T>(&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<ApiRequest>,
) -> Result<thread::JoinHandle<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 fn vm_create(
api_evt: EventFd,
api_sender: Sender<ApiRequest>,
config: Arc<VmConfig>,
) -> Result<()> {
let (response_sender, response_receiver) = channel();
// Send the VM creation request.
api_sender
.send(ApiRequest::VmCreate(config, response_sender))
.map_err(Error::ApiRequestSend)?;
api_evt.write(1).map_err(Error::EventFdWrite)?;
response_receiver
.recv()
.map_err(Error::ApiResponseRecv)?
.map_err(Error::ApiVmCreate)?;
Ok(())
}
pub fn vm_start(api_evt: EventFd, api_sender: Sender<ApiRequest>) -> Result<()> {
let (response_sender, response_receiver) = channel();
// Send the VM start request.
api_sender
.send(ApiRequest::VmStart(response_sender))
.map_err(Error::ApiRequestSend)?;
api_evt.write(1).map_err(Error::EventFdWrite)?;
response_receiver
.recv()
.map_err(Error::ApiResponseRecv)?
.map_err(Error::ApiVmStart)?;
Ok(())
}
pub struct Vmm {
epoll: EpollContext,
exit_evt: EventFd,
reset_evt: EventFd,
api_evt: EventFd,
vm: Option<Vm>,
}
impl Vmm {
fn new(api_evt: EventFd) -> Result<Self> {
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<Receiver<ApiRequest>>) -> Result<ExitBehaviour> {
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)
}
}