vmm: Add a basic stdin loop

After starting all vCPUs, we loop for STDIN input.
We need a more scalable eventfd control loop, obviously.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2019-03-18 21:59:50 +01:00
parent 0b6ec34505
commit c6c5e10a04
3 changed files with 78 additions and 1 deletions

1
Cargo.lock generated
View File

@ -173,6 +173,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"arch 0.1.0", "arch 0.1.0",
"devices 0.1.0", "devices 0.1.0",
"epoll 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kvm-ioctls 0.1.0 (git+https://github.com/rust-vmm/kvm-ioctls)", "kvm-ioctls 0.1.0 (git+https://github.com/rust-vmm/kvm-ioctls)",
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -7,6 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
arch = { path = "../arch" } arch = { path = "../arch" }
devices = { path = "../devices" } devices = { path = "../devices" }
epoll = "=4.0.1"
kvm-bindings = "0.1" kvm-bindings = "0.1"
kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls" } kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls" }
libc = ">=0.2.39" libc = ">=0.2.39"

View File

@ -1,10 +1,11 @@
// Copyright © 2019 Intel Corporation // Copyright © 2019 Intel Corporation
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //vm
extern crate arch; extern crate arch;
extern crate devices; extern crate devices;
extern crate epoll;
extern crate kvm_ioctls; extern crate kvm_ioctls;
extern crate libc; extern crate libc;
extern crate linux_loader; extern crate linux_loader;
@ -19,6 +20,7 @@ use linux_loader::loader::KernelLoader;
use std::ffi::CString; use std::ffi::CString;
use std::fs::File; use std::fs::File;
use std::io::{self, stdout}; use std::io::{self, stdout};
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Barrier, Mutex}; use std::sync::{Arc, Barrier, Mutex};
use std::{result, str, thread}; use std::{result, str, thread};
@ -27,6 +29,7 @@ use vm_memory::{
MmapError, MmapError,
}; };
use vmm_sys_util::signal::register_signal_handler; use vmm_sys_util::signal::register_signal_handler;
use vmm_sys_util::terminal::Terminal;
use vmm_sys_util::EventFd; use vmm_sys_util::EventFd;
const VCPU_RTSIG_OFFSET: i32 = 0; const VCPU_RTSIG_OFFSET: i32 = 0;
@ -100,6 +103,12 @@ pub enum Error {
/// Cannot add legacy device to Bus. /// Cannot add legacy device to Bus.
BusError(devices::BusError), BusError(devices::BusError),
/// Cannot create epoll context.
EpollError(io::Error),
/// Write to the serial console failed.
Serial(vmm_sys_util::Error),
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -228,6 +237,34 @@ impl DeviceManager {
} }
} }
pub struct EpollContext {
raw_fd: RawFd,
}
impl EpollContext {
pub fn new() -> result::Result<EpollContext, io::Error> {
let raw_fd = epoll::create(true)?;
Ok(EpollContext { raw_fd })
}
pub fn add_stdin(&self) -> result::Result<(), io::Error> {
epoll::ctl(
self.raw_fd,
epoll::ControlOptions::EPOLL_CTL_ADD,
libc::STDIN_FILENO,
epoll::Event::new(epoll::Events::EPOLLIN, libc::STDIN_FILENO as u64),
)?;
Ok(())
}
}
impl AsRawFd for EpollContext {
fn as_raw_fd(&self) -> RawFd {
self.raw_fd
}
}
pub struct Vm<'a> { pub struct Vm<'a> {
fd: VmFd, fd: VmFd,
kernel: File, kernel: File,
@ -236,6 +273,7 @@ pub struct Vm<'a> {
devices: DeviceManager, devices: DeviceManager,
cpuid: CpuId, cpuid: CpuId,
config: VmConfig<'a>, config: VmConfig<'a>,
epoll: EpollContext,
} }
impl<'a> Vm<'a> { impl<'a> Vm<'a> {
@ -288,6 +326,10 @@ impl<'a> Vm<'a> {
let device_manager = DeviceManager::new().map_err(|_| Error::DeviceManager)?; let device_manager = DeviceManager::new().map_err(|_| Error::DeviceManager)?;
// Let's add our STDIN fd.
let epoll = EpollContext::new().map_err(Error::EpollError)?;
epoll.add_stdin().map_err(Error::EpollError)?;
Ok(Vm { Ok(Vm {
fd, fd,
kernel, kernel,
@ -296,6 +338,7 @@ impl<'a> Vm<'a> {
devices: device_manager, devices: device_manager,
cpuid, cpuid,
config, config,
epoll,
}) })
} }
@ -330,6 +373,36 @@ impl<'a> Vm<'a> {
Ok(entry_addr.kernel_load) Ok(entry_addr.kernel_load)
} }
pub fn stdin_loop(&mut self) -> Result<()> {
// Let's start the STDIN polling thread.
const EPOLL_EVENTS_LEN: usize = 10;
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
let epoll_fd = self.epoll.as_raw_fd();
loop {
let num_events =
epoll::wait(epoll_fd, -1, &mut events[..]).map_err(Error::EpollError)?;
for event in events.iter().take(num_events) {
let event_data = event.data as RawFd;
if let libc::STDIN_FILENO = event_data {
let stdin = io::stdin();
let mut out = [0u8; 64];
let stdin_lock = stdin.lock();
let count = stdin_lock.read_raw(&mut out).map_err(Error::Serial)?;
self.devices
.serial
.lock()
.expect("Failed to process stdin event due to poisoned lock")
.queue_input_bytes(&out[..count])
.map_err(Error::Serial)?;
}
}
}
}
pub fn start(&mut self, entry_addr: GuestAddress) -> Result<()> { pub fn start(&mut self, entry_addr: GuestAddress) -> Result<()> {
self.devices.register_devices()?; self.devices.register_devices()?;
@ -435,6 +508,8 @@ impl<'a> Vm<'a> {
// Unblock all CPU threads. // Unblock all CPU threads.
vcpu_thread_barrier.wait(); vcpu_thread_barrier.wait();
self.stdin_loop()?;
for vcpu_barrier in vcpus { for vcpu_barrier in vcpus {
vcpu_barrier.join().unwrap(); vcpu_barrier.join().unwrap();
} }