vmm: Simplify the vcpu run switch

Use a catchall case for all reasons that we do not handle, and
move the vCPU run switch into its own function.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2019-05-19 04:24:47 +02:00 committed by Sebastien Boeuf
parent 6615d55223
commit 8bb71fad76
4 changed files with 51 additions and 67 deletions

1
Cargo.lock generated
View File

@ -461,6 +461,7 @@ dependencies = [
"kvm-ioctls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "kvm-ioctls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"linux-loader 0.1.0 (git+https://github.com/bjzhjing/linux-loader)", "linux-loader 0.1.0 (git+https://github.com/bjzhjing/linux-loader)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"net_util 0.1.0", "net_util 0.1.0",
"pci 0.1.0", "pci 0.1.0",
"qcow 0.1.0", "qcow 0.1.0",

View File

@ -11,6 +11,7 @@ epoll = "=4.0.1"
kvm-bindings = "0.1" kvm-bindings = "0.1"
kvm-ioctls = "0.1.0" kvm-ioctls = "0.1.0"
libc = ">=0.2.39" libc = ">=0.2.39"
log = "*"
net_util = { path = "../net_util" } net_util = { path = "../net_util" }
pci = {path = "../pci"} pci = {path = "../pci"}
qcow = { path = "../qcow" } qcow = { path = "../qcow" }

View File

@ -4,6 +4,8 @@
// //
extern crate kvm_ioctls; extern crate kvm_ioctls;
#[macro_use]
extern crate log;
use kvm_ioctls::*; use kvm_ioctls::*;
use std::fmt::{self, Display}; use std::fmt::{self, Display};

View File

@ -141,6 +141,9 @@ pub enum Error {
/// Failed parsing network parameters /// Failed parsing network parameters
ParseNetworkParameters, ParseNetworkParameters,
/// Unexpected KVM_RUN exit reason
VcpuUnhandledKvmExit,
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -195,6 +198,8 @@ pub type DeviceManagerResult<T> = result::Result<T, DeviceManagerError>;
pub struct Vcpu { pub struct Vcpu {
fd: VcpuFd, fd: VcpuFd,
id: u8, id: u8,
io_bus: devices::Bus,
mmio_bus: devices::Bus,
} }
impl Vcpu { impl Vcpu {
@ -204,10 +209,15 @@ impl Vcpu {
/// ///
/// * `id` - Represents the CPU number between [0, max vcpus). /// * `id` - Represents the CPU number between [0, max vcpus).
/// * `vm` - The virtual machine this vcpu will get attached to. /// * `vm` - The virtual machine this vcpu will get attached to.
pub fn new(id: u8, vm: &Vm) -> Result<Self> { pub fn new(id: u8, vm: &Vm, io_bus: devices::Bus, mmio_bus: devices::Bus) -> Result<Self> {
let kvm_vcpu = vm.fd.create_vcpu(id).map_err(Error::VcpuFd)?; let kvm_vcpu = vm.fd.create_vcpu(id).map_err(Error::VcpuFd)?;
// Initially the cpuid per vCPU is the one supported by this VM. // Initially the cpuid per vCPU is the one supported by this VM.
Ok(Vcpu { fd: kvm_vcpu, id }) Ok(Vcpu {
fd: kvm_vcpu,
id,
io_bus,
mmio_bus,
})
} }
/// Configures a x86_64 specific vcpu and should be called once per vcpu from the vcpu's thread. /// Configures a x86_64 specific vcpu and should be called once per vcpu from the vcpu's thread.
@ -242,8 +252,39 @@ impl Vcpu {
/// ///
/// Note that the state of the VCPU and associated VM must be setup first for this to do /// Note that the state of the VCPU and associated VM must be setup first for this to do
/// anything useful. /// anything useful.
pub fn run(&self) -> Result<VcpuExit> { pub fn run(&self) -> Result<()> {
self.fd.run().map_err(Error::VcpuRun) match self.fd.run() {
Ok(run) => match run {
VcpuExit::IoIn(addr, data) => {
self.io_bus.read(u64::from(addr), data);
Ok(())
}
VcpuExit::IoOut(addr, data) => {
self.io_bus.write(u64::from(addr), data);
Ok(())
}
VcpuExit::MmioRead(addr, data) => {
self.mmio_bus.read(addr as u64, data);
Ok(())
}
VcpuExit::MmioWrite(addr, data) => {
self.mmio_bus.write(addr as u64, data);
Ok(())
}
r => {
error!("Unexpected exit reason on vcpu run: {:?}", r);
Err(Error::VcpuUnhandledKvmExit)
}
},
Err(ref e) => match e.raw_os_error().unwrap() {
libc::EAGAIN | libc::EINTR => Ok(()),
_ => {
error!("VCPU {:?} error {:?}", self.id, e);
Err(Error::VcpuUnhandledKvmExit)
}
},
}
} }
} }
@ -782,10 +823,9 @@ impl<'a> Vm<'a> {
let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count + 1) as usize)); let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count + 1) as usize));
for cpu_id in 0..vcpu_count { for cpu_id in 0..vcpu_count {
println!("Starting VCPU {:?}", cpu_id);
let io_bus = self.devices.io_bus.clone(); let io_bus = self.devices.io_bus.clone();
let mmio_bus = self.devices.mmio_bus.clone(); let mmio_bus = self.devices.mmio_bus.clone();
let mut vcpu = Vcpu::new(cpu_id, &self)?; let mut vcpu = Vcpu::new(cpu_id, &self, io_bus, mmio_bus)?;
vcpu.configure(entry_addr, &self)?; vcpu.configure(entry_addr, &self)?;
let vcpu_thread_barrier = vcpu_thread_barrier.clone(); let vcpu_thread_barrier = vcpu_thread_barrier.clone();
@ -810,67 +850,7 @@ impl<'a> Vm<'a> {
// Block until all CPUs are ready. // Block until all CPUs are ready.
vcpu_thread_barrier.wait(); vcpu_thread_barrier.wait();
loop { while vcpu.run().is_ok() {}
match vcpu.run() {
Ok(run) => match run {
VcpuExit::IoIn(addr, data) => {
io_bus.read(u64::from(addr), data);
}
VcpuExit::IoOut(addr, data) => {
io_bus.write(u64::from(addr), data);
}
VcpuExit::MmioRead(addr, data) => {
mmio_bus.read(addr as u64, data);
}
VcpuExit::MmioWrite(addr, data) => {
mmio_bus.write(addr as u64, data);
}
VcpuExit::Unknown => {
println!("Unknown");
}
VcpuExit::Exception => {
println!("Exception");
}
VcpuExit::Hypercall => {}
VcpuExit::Debug => {}
VcpuExit::Hlt => {
println!("HLT");
}
VcpuExit::IrqWindowOpen => {}
VcpuExit::Shutdown => {}
VcpuExit::FailEntry => {}
VcpuExit::Intr => {}
VcpuExit::SetTpr => {}
VcpuExit::TprAccess => {}
VcpuExit::S390Sieic => {}
VcpuExit::S390Reset => {}
VcpuExit::Dcr => {}
VcpuExit::Nmi => {}
VcpuExit::InternalError => {}
VcpuExit::Osi => {}
VcpuExit::PaprHcall => {}
VcpuExit::S390Ucontrol => {}
VcpuExit::Watchdog => {}
VcpuExit::S390Tsch => {}
VcpuExit::Epr => {}
VcpuExit::SystemEvent => {}
VcpuExit::S390Stsi => {}
VcpuExit::IoapicEoi => {}
VcpuExit::Hyperv => {}
},
Err(Error::VcpuRun(ref e)) => {
match e.raw_os_error().unwrap() {
// Why do we check for these if we only return EINVAL?
libc::EAGAIN | libc::EINTR => {}
_ => {
println! {"VCPU {:?} error {:?}", cpu_id, e};
break;
}
}
}
_ => (),
}
}
}) })
.map_err(Error::VcpuSpawn)?, .map_err(Error::VcpuSpawn)?,
); );