vm: Propagate errors appropriately

In order to get meaningful error messages, we want to make sure all
errors are passed up the call stack. This patch fixes this previous
limitation by separating errors related to the DeviceManager from
errors related to the Vm.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-05-13 18:12:40 -07:00 committed by Rob Bradford
parent 6ecdd98634
commit c1f1fe713f

View File

@ -112,12 +112,12 @@ pub enum Error {
/// The call to KVM_SET_CPUID2 failed. /// The call to KVM_SET_CPUID2 failed.
SetSupportedCpusFailed(io::Error), SetSupportedCpusFailed(io::Error),
/// Cannot create a device manager.
DeviceManager(DeviceManagerError),
/// Cannot create EventFd. /// Cannot create EventFd.
EventFd(io::Error), EventFd(io::Error),
/// Cannot create a device manager.
DeviceManager,
/// Cannot add legacy device to Bus. /// Cannot add legacy device to Bus.
BusError(devices::BusError), BusError(devices::BusError),
@ -133,23 +133,22 @@ pub enum Error {
/// Cannot setup terminal in canonical mode. /// Cannot setup terminal in canonical mode.
SetTerminalCanon(vmm_sys_util::Error), SetTerminalCanon(vmm_sys_util::Error),
/// Cannot allocate IRQ.
AllocateIrq,
/// Cannot allocate PCI BARs
AllocateBars(pci::PciDeviceError),
/// Cannot register ioevent.
RegisterIoevent(io::Error),
/// Cannot configure the IRQ. /// Cannot configure the IRQ.
Irq(io::Error), Irq(io::Error),
/// Cannot create virtio device /// Cannot create the system allocator
VirtioDevice, CreateSystemAllocator,
/// Cannot add PCI device /// Failed parsing network parameters
AddPciDevice(pci::PciRootError), ParseNetworkParameters,
}
pub type Result<T> = result::Result<T, Error>;
/// Errors associated with device manager
#[derive(Debug)]
pub enum DeviceManagerError {
/// Cannot create EventFd.
EventFd(io::Error),
/// Cannot open disk path /// Cannot open disk path
Disk(io::Error), Disk(io::Error),
@ -163,22 +162,34 @@ pub enum Error {
/// Cannot create virtio-rng device /// Cannot create virtio-rng device
CreateVirtioRng(io::Error), CreateVirtioRng(io::Error),
/// Cannot create the system allocator
CreateSystemAllocator,
/// Failed parsing network parameters
ParseNetworkParameters,
/// Cannot open tap interface
OpenTap(net_util::TapError),
/// Failed parsing disk image format /// Failed parsing disk image format
DetectImageType(qcow::Error), DetectImageType(qcow::Error),
/// Cannot open qcow disk path /// Cannot open qcow disk path
QcowDeviceCreate(qcow::Error), QcowDeviceCreate(qcow::Error),
/// Cannot open tap interface
OpenTap(net_util::TapError),
/// Cannot allocate IRQ.
AllocateIrq,
/// Cannot configure the IRQ.
Irq(io::Error),
/// Cannot allocate PCI BARs
AllocateBars(pci::PciDeviceError),
/// Cannot register ioevent.
RegisterIoevent(io::Error),
/// Cannot create virtio device
VirtioDevice(vmm_sys_util::Error),
/// Cannot add PCI device
AddPciDevice(pci::PciRootError),
} }
pub type Result<T> = result::Result<T, Error>; pub type DeviceManagerResult<T> = result::Result<T, DeviceManagerError>;
/// A wrapper around creating and using a kvm-based VCPU. /// A wrapper around creating and using a kvm-based VCPU.
pub struct Vcpu { pub struct Vcpu {
@ -350,18 +361,20 @@ impl DeviceManager {
allocator: &mut SystemAllocator, allocator: &mut SystemAllocator,
vm_fd: &VmFd, vm_fd: &VmFd,
vm_cfg: &VmConfig, vm_cfg: &VmConfig,
) -> Result<Self> { ) -> DeviceManagerResult<Self> {
let io_bus = devices::Bus::new(); let io_bus = devices::Bus::new();
let mut mmio_bus = devices::Bus::new(); let mut mmio_bus = devices::Bus::new();
let serial_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; let serial_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?;
let serial = Arc::new(Mutex::new(devices::legacy::Serial::new_out( let serial = Arc::new(Mutex::new(devices::legacy::Serial::new_out(
serial_evt.try_clone().map_err(Error::EventFd)?, serial_evt
.try_clone()
.map_err(DeviceManagerError::EventFd)?,
Box::new(stdout()), Box::new(stdout()),
))); )));
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?;
let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new( let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new(
exit_evt.try_clone().map_err(Error::EventFd)?, exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?,
))); )));
let mut pci_root = PciRoot::new(None); let mut pci_root = PciRoot::new(None);
@ -371,22 +384,24 @@ impl DeviceManager {
.read(true) .read(true)
.write(true) .write(true)
.open(&vm_cfg.disk_path) .open(&vm_cfg.disk_path)
.map_err(Error::Disk)?; .map_err(DeviceManagerError::Disk)?;
// Add virtio-blk // Add virtio-blk
let image_type = qcow::detect_image_type(&raw_img).map_err(Error::DetectImageType)?; let image_type =
qcow::detect_image_type(&raw_img).map_err(DeviceManagerError::DetectImageType)?;
let disk_path = vm_cfg.disk_path.to_path_buf(); let disk_path = vm_cfg.disk_path.to_path_buf();
let block = match image_type { let block = match image_type {
ImageType::Raw => { ImageType::Raw => {
let raw_img = vm_virtio::RawFile::new(raw_img); let raw_img = vm_virtio::RawFile::new(raw_img);
let dev = vm_virtio::Block::new(raw_img, disk_path, false) let dev = vm_virtio::Block::new(raw_img, disk_path, false)
.map_err(Error::CreateVirtioBlock)?; .map_err(DeviceManagerError::CreateVirtioBlock)?;
Box::new(dev) as Box<vm_virtio::VirtioDevice> Box::new(dev) as Box<vm_virtio::VirtioDevice>
} }
ImageType::Qcow2 => { ImageType::Qcow2 => {
let qcow_img = QcowFile::from(raw_img).map_err(Error::QcowDeviceCreate)?; let qcow_img =
QcowFile::from(raw_img).map_err(DeviceManagerError::QcowDeviceCreate)?;
let dev = vm_virtio::Block::new(qcow_img, disk_path, false) let dev = vm_virtio::Block::new(qcow_img, disk_path, false)
.map_err(Error::CreateVirtioBlock)?; .map_err(DeviceManagerError::CreateVirtioBlock)?;
Box::new(dev) as Box<vm_virtio::VirtioDevice> Box::new(dev) as Box<vm_virtio::VirtioDevice>
} }
}; };
@ -406,17 +421,17 @@ impl DeviceManager {
let mut virtio_net_device: vm_virtio::Net; let mut virtio_net_device: vm_virtio::Net;
if let Some(tap_if_name) = net_params.tap_if_name { if let Some(tap_if_name) = net_params.tap_if_name {
let tap = Tap::open_named(tap_if_name).map_err(Error::OpenTap)?; let tap = Tap::open_named(tap_if_name).map_err(DeviceManagerError::OpenTap)?;
virtio_net_device = virtio_net_device =
vm_virtio::Net::new_with_tap(tap, Some(&net_params.mac_addr)) vm_virtio::Net::new_with_tap(tap, Some(&net_params.mac_addr))
.map_err(Error::CreateVirtioNet)?; .map_err(DeviceManagerError::CreateVirtioNet)?;
} else { } else {
virtio_net_device = vm_virtio::Net::new( virtio_net_device = vm_virtio::Net::new(
net_params.ip_addr, net_params.ip_addr,
net_params.net_mask, net_params.net_mask,
Some(&net_params.mac_addr), Some(&net_params.mac_addr),
) )
.map_err(Error::CreateVirtioNet)?; .map_err(DeviceManagerError::CreateVirtioNet)?;
} }
DeviceManager::add_virtio_pci_device( DeviceManager::add_virtio_pci_device(
@ -433,7 +448,7 @@ impl DeviceManager {
// Add virtio-rng if required // Add virtio-rng if required
if let Some(rng_path) = &vm_cfg.rng_path { if let Some(rng_path) = &vm_cfg.rng_path {
let virtio_rng_device = let virtio_rng_device =
vm_virtio::Rng::new(rng_path).map_err(Error::CreateVirtioRng)?; vm_virtio::Rng::new(rng_path).map_err(DeviceManagerError::CreateVirtioRng)?;
DeviceManager::add_virtio_pci_device( DeviceManager::add_virtio_pci_device(
Box::new(virtio_rng_device), Box::new(virtio_rng_device),
@ -465,26 +480,28 @@ impl DeviceManager {
vm_fd: &VmFd, vm_fd: &VmFd,
pci_root: &mut PciRoot, pci_root: &mut PciRoot,
mmio_bus: &mut devices::Bus, mmio_bus: &mut devices::Bus,
) -> Result<()> { ) -> DeviceManagerResult<()> {
let mut virtio_pci_device = let mut virtio_pci_device = VirtioPciDevice::new(memory, virtio_device)
VirtioPciDevice::new(memory, virtio_device).map_err(|_| Error::VirtioDevice)?; .map_err(DeviceManagerError::VirtioDevice)?;
let bars = virtio_pci_device let bars = virtio_pci_device
.allocate_bars(allocator) .allocate_bars(allocator)
.map_err(Error::AllocateBars)?; .map_err(DeviceManagerError::AllocateBars)?;
for (event, addr, _) in virtio_pci_device.ioeventfds() { for (event, addr, _) in virtio_pci_device.ioeventfds() {
let io_addr = IoEventAddress::Mmio(addr); let io_addr = IoEventAddress::Mmio(addr);
vm_fd vm_fd
.register_ioevent(event.as_raw_fd(), &io_addr, NoDatamatch) .register_ioevent(event.as_raw_fd(), &io_addr, NoDatamatch)
.map_err(Error::RegisterIoevent)?; .map_err(DeviceManagerError::RegisterIoevent)?;
} }
// Assign IRQ to the virtio-pci device // Assign IRQ to the virtio-pci device
let irqfd = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; let irqfd = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?;
let irq_num = allocator.allocate_irq().ok_or(Error::AllocateIrq)?; let irq_num = allocator
.allocate_irq()
.ok_or(DeviceManagerError::AllocateIrq)?;
vm_fd vm_fd
.register_irqfd(irqfd.as_raw_fd(), irq_num) .register_irqfd(irqfd.as_raw_fd(), irq_num)
.map_err(Error::Irq)?; .map_err(DeviceManagerError::Irq)?;
// Let's use irq line INTA for now. // Let's use irq line INTA for now.
virtio_pci_device.assign_irq(irqfd, irq_num as u32, PciInterruptPin::IntA); virtio_pci_device.assign_irq(irqfd, irq_num as u32, PciInterruptPin::IntA);
@ -492,7 +509,7 @@ impl DeviceManager {
pci_root pci_root
.add_device(virtio_pci_device.clone(), mmio_bus, bars) .add_device(virtio_pci_device.clone(), mmio_bus, bars)
.map_err(Error::AddPciDevice)?; .map_err(DeviceManagerError::AddPciDevice)?;
Ok(()) Ok(())
} }
@ -646,7 +663,7 @@ impl<'a> Vm<'a> {
.ok_or(Error::CreateSystemAllocator)?; .ok_or(Error::CreateSystemAllocator)?;
let device_manager = DeviceManager::new(guest_memory.clone(), &mut allocator, &fd, &config) let device_manager = DeviceManager::new(guest_memory.clone(), &mut allocator, &fd, &config)
.map_err(|_| Error::DeviceManager)?; .map_err(Error::DeviceManager)?;
fd.register_irqfd(device_manager.serial_evt.as_raw_fd(), 4) fd.register_irqfd(device_manager.serial_evt.as_raw_fd(), 4)
.map_err(Error::Irq)?; .map_err(Error::Irq)?;