2019-02-22 16:04:35 +00:00
|
|
|
// Copyright © 2019 Intel Corporation
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//
|
|
|
|
|
2019-09-18 09:14:49 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate lazy_static;
|
|
|
|
|
2019-05-19 02:24:47 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
2019-09-27 09:40:50 +00:00
|
|
|
extern crate serde;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate serde_derive;
|
|
|
|
extern crate serde_json;
|
2019-09-25 12:09:33 +00:00
|
|
|
extern crate vmm_sys_util;
|
2019-02-22 16:04:35 +00:00
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload};
|
2019-09-30 14:17:28 +00:00
|
|
|
use crate::config::VmConfig;
|
2019-09-25 12:40:14 +00:00
|
|
|
use crate::vm::{Error as VmError, ExitBehaviour, Vm};
|
2019-09-25 12:09:33 +00:00
|
|
|
use libc::EFD_NONBLOCK;
|
|
|
|
use std::io;
|
2019-09-25 12:25:08 +00:00
|
|
|
use std::os::unix::io::{AsRawFd, RawFd};
|
2019-09-26 17:00:43 +00:00
|
|
|
use std::sync::mpsc::{Receiver, RecvError, SendError, Sender};
|
2019-09-24 14:00:00 +00:00
|
|
|
use std::sync::Arc;
|
2019-09-25 12:40:14 +00:00
|
|
|
use std::{result, thread};
|
2019-09-25 12:09:33 +00:00
|
|
|
use vmm_sys_util::eventfd::EventFd;
|
2019-03-07 13:56:43 +00:00
|
|
|
|
2019-09-25 12:14:15 +00:00
|
|
|
pub mod api;
|
2019-05-23 19:48:05 +00:00
|
|
|
pub mod config;
|
2019-09-04 13:55:14 +00:00
|
|
|
pub mod device_manager;
|
2019-02-28 13:16:58 +00:00
|
|
|
pub mod vm;
|
2019-02-22 16:04:35 +00:00
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
/// Errors associated with VMM management
|
2019-05-10 08:46:27 +00:00
|
|
|
#[derive(Debug)]
|
2019-09-25 12:40:14 +00:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2019-05-10 08:46:27 +00:00
|
|
|
pub enum Error {
|
2019-09-25 12:40:14 +00:00
|
|
|
/// 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),
|
|
|
|
|
2019-09-30 09:53:49 +00:00
|
|
|
/// Cannot boot a VM from the API
|
|
|
|
ApiVmBoot(ApiError),
|
2019-09-25 12:40:14 +00:00
|
|
|
|
2019-09-27 14:48:16 +00:00
|
|
|
/// Cannot shut a VM down from the API
|
|
|
|
ApiVmShutdown(ApiError),
|
|
|
|
|
|
|
|
/// Cannot reboot a VM from the API
|
|
|
|
ApiVmReboot(ApiError),
|
|
|
|
|
2019-09-18 09:14:49 +00:00
|
|
|
/// Cannot bind to the UNIX domain socket path
|
|
|
|
Bind(io::Error),
|
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
/// Cannot clone EventFd.
|
|
|
|
EventFdClone(io::Error),
|
|
|
|
|
2019-09-25 12:09:33 +00:00
|
|
|
/// Cannot create EventFd.
|
2019-09-25 12:40:14 +00:00
|
|
|
EventFdCreate(io::Error),
|
2019-09-25 12:09:33 +00:00
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
/// Cannot read from EventFd.
|
|
|
|
EventFdRead(io::Error),
|
2019-05-10 08:46:27 +00:00
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
/// Cannot write to EventFd.
|
|
|
|
EventFdWrite(io::Error),
|
2019-09-24 14:14:04 +00:00
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
/// Cannot create epoll context.
|
|
|
|
Epoll(io::Error),
|
2019-05-10 08:46:27 +00:00
|
|
|
|
2019-09-18 09:14:49 +00:00
|
|
|
/// Cannot create HTTP thread
|
|
|
|
HttpThreadSpawn(io::Error),
|
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
/// Cannot handle the VM STDIN stream
|
|
|
|
Stdin(VmError),
|
2019-05-10 08:46:27 +00:00
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
/// Cannot create a VM
|
|
|
|
VmCreate(VmError),
|
|
|
|
|
2019-09-30 09:53:49 +00:00
|
|
|
/// Cannot boot a VM
|
|
|
|
VmBoot(VmError),
|
2019-09-25 12:40:14 +00:00
|
|
|
|
2019-09-30 09:53:49 +00:00
|
|
|
/// Cannot shut a VM down
|
|
|
|
VmShutdown(VmError),
|
2019-09-25 12:45:23 +00:00
|
|
|
|
|
|
|
/// Cannot create VMM thread
|
|
|
|
VmmThreadSpawn(io::Error),
|
2019-05-10 08:46:27 +00:00
|
|
|
}
|
2019-09-25 12:40:14 +00:00
|
|
|
pub type Result<T> = result::Result<T, Error>;
|
2019-02-22 16:04:35 +00:00
|
|
|
|
2019-09-25 12:25:08 +00:00
|
|
|
#[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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 12:45:23 +00:00
|
|
|
pub fn start_vmm_thread(
|
2019-09-26 16:19:00 +00:00
|
|
|
http_path: &str,
|
2019-09-25 12:45:23 +00:00
|
|
|
api_event: EventFd,
|
2019-09-26 16:19:00 +00:00
|
|
|
api_sender: Sender<ApiRequest>,
|
2019-09-25 12:45:23 +00:00
|
|
|
api_receiver: Receiver<ApiRequest>,
|
|
|
|
) -> Result<thread::JoinHandle<Result<()>>> {
|
2019-09-26 16:19:00 +00:00
|
|
|
let http_api_event = api_event.try_clone().map_err(Error::EventFdClone)?;
|
|
|
|
|
|
|
|
let thread = thread::Builder::new()
|
2019-09-25 12:45:23 +00:00
|
|
|
.name("vmm".to_string())
|
|
|
|
.spawn(move || {
|
2019-09-26 16:19:00 +00:00
|
|
|
// let vmm_api_event = api_event.try_clone().map_err(Error::EventFdClone)?;
|
2019-09-25 12:45:23 +00:00
|
|
|
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
|
2019-09-30 09:53:49 +00:00
|
|
|
// based on the same VM config, boot it and restart
|
2019-09-25 12:45:23 +00:00
|
|
|
// the control loop.
|
|
|
|
|
2019-09-27 14:48:16 +00:00
|
|
|
vmm.vm_reboot()?;
|
2019-09-25 12:45:23 +00:00
|
|
|
|
|
|
|
// 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 {
|
2019-09-30 09:53:49 +00:00
|
|
|
vm.shutdown().map_err(Error::VmShutdown)?;
|
2019-09-25 12:45:23 +00:00
|
|
|
}
|
|
|
|
break 'outer;
|
|
|
|
}
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
2019-09-26 16:19:00 +00:00
|
|
|
.map_err(Error::VmmThreadSpawn)?;
|
|
|
|
|
|
|
|
// The VMM thread is started, we can start serving HTTP requests
|
|
|
|
api::start_http_thread(http_path, http_api_event, api_sender)?;
|
|
|
|
|
|
|
|
Ok(thread)
|
2019-09-25 12:45:23 +00:00
|
|
|
}
|
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
pub struct Vmm {
|
|
|
|
epoll: EpollContext,
|
|
|
|
exit_evt: EventFd,
|
|
|
|
reset_evt: EventFd,
|
|
|
|
api_evt: EventFd,
|
|
|
|
vm: Option<Vm>,
|
2019-09-30 14:17:28 +00:00
|
|
|
vm_config: Option<Arc<VmConfig>>,
|
2019-09-25 12:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2019-09-30 14:17:28 +00:00
|
|
|
vm_config: None,
|
2019-09-25 12:40:14 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-09-27 14:48:16 +00:00
|
|
|
fn vm_reboot(&mut self) -> Result<()> {
|
|
|
|
// Without ACPI, a reset is equivalent to a shutdown
|
|
|
|
#[cfg(not(feature = "acpi"))]
|
|
|
|
{
|
|
|
|
if let Some(ref mut vm) = self.vm {
|
|
|
|
vm.shutdown().map_err(Error::VmShutdown)?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// First we stop the current VM and create a new one.
|
|
|
|
if let Some(ref mut vm) = self.vm {
|
|
|
|
let config = vm.get_config();
|
|
|
|
vm.shutdown().map_err(Error::VmShutdown)?;
|
|
|
|
|
|
|
|
let exit_evt = self.exit_evt.try_clone().map_err(Error::EventFdClone)?;
|
|
|
|
let reset_evt = self.reset_evt.try_clone().map_err(Error::EventFdClone)?;
|
|
|
|
|
|
|
|
self.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) = self.vm {
|
|
|
|
vm.boot().map_err(Error::VmBoot)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-09-25 12:40:14 +00:00
|
|
|
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) => {
|
2019-09-30 14:17:28 +00:00
|
|
|
// We only store the passed VM config.
|
|
|
|
// The VM will be created when being asked to boot it.
|
|
|
|
let response = if self.vm_config.is_none() {
|
|
|
|
self.vm_config = Some(config);
|
|
|
|
Ok(ApiResponsePayload::Empty)
|
|
|
|
} else {
|
|
|
|
Err(ApiError::VmAlreadyCreated)
|
2019-09-25 12:40:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
sender.send(response).map_err(Error::ApiResponseSend)?;
|
|
|
|
}
|
2019-09-30 09:53:49 +00:00
|
|
|
ApiRequest::VmBoot(sender) => {
|
2019-09-30 14:17:28 +00:00
|
|
|
// If we don't have a config, we can not boot a VM.
|
|
|
|
if self.vm_config.is_none() {
|
|
|
|
sender
|
|
|
|
.send(Err(ApiError::VmMissingConfig))
|
|
|
|
.map_err(Error::ApiResponseSend)?;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new VM is we don't have one yet.
|
|
|
|
if self.vm.is_none() {
|
|
|
|
let exit_evt = self
|
|
|
|
.exit_evt
|
|
|
|
.try_clone()
|
|
|
|
.map_err(Error::EventFdClone)?;
|
|
|
|
let reset_evt = self
|
|
|
|
.reset_evt
|
|
|
|
.try_clone()
|
|
|
|
.map_err(Error::EventFdClone)?;
|
|
|
|
|
|
|
|
if let Some(ref vm_config) = self.vm_config {
|
|
|
|
match Vm::new(
|
|
|
|
Arc::clone(vm_config),
|
|
|
|
exit_evt,
|
|
|
|
reset_evt,
|
|
|
|
) {
|
|
|
|
Ok(vm) => {
|
|
|
|
self.vm = Some(vm);
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
sender
|
|
|
|
.send(Err(ApiError::VmCreate(e)))
|
|
|
|
.map_err(Error::ApiResponseSend)?;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now let's boot it.
|
2019-09-25 12:40:14 +00:00
|
|
|
if let Some(ref mut vm) = self.vm {
|
2019-09-30 09:53:49 +00:00
|
|
|
let response = match vm.boot() {
|
2019-09-25 12:40:14 +00:00
|
|
|
Ok(_) => Ok(ApiResponsePayload::Empty),
|
2019-09-30 09:53:49 +00:00
|
|
|
Err(e) => Err(ApiError::VmBoot(e)),
|
2019-09-25 12:40:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
sender.send(response).map_err(Error::ApiResponseSend)?;
|
|
|
|
}
|
|
|
|
}
|
2019-09-27 14:48:16 +00:00
|
|
|
ApiRequest::VmShutdown(sender) => {
|
|
|
|
if let Some(ref mut vm) = self.vm {
|
|
|
|
let response = match vm.shutdown() {
|
|
|
|
Ok(_) => Ok(ApiResponsePayload::Empty),
|
|
|
|
Err(e) => Err(ApiError::VmShutdown(e)),
|
|
|
|
};
|
|
|
|
|
|
|
|
sender.send(response).map_err(Error::ApiResponseSend)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ApiRequest::VmReboot(sender) => {
|
|
|
|
let response = match self.vm_reboot() {
|
|
|
|
Ok(_) => Ok(ApiResponsePayload::Empty),
|
|
|
|
Err(_) => Err(ApiError::VmReboot),
|
|
|
|
};
|
|
|
|
|
|
|
|
sender.send(response).map_err(Error::ApiResponseSend)?;
|
|
|
|
}
|
2019-09-25 12:40:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(exit_behaviour)
|
|
|
|
}
|
|
|
|
}
|