From 3b2faa9f11e38a14e8ebd51666087fcbac7313e1 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 8 May 2019 15:59:39 +0100 Subject: [PATCH] vm-virtio: Reset underlying device on driver request If the driver triggers a reset by writing zero into the status register then reset the underlying device if supported. A device reset also requires resetting various aspects of the queue. In order to be able to do a subsequent reactivate it is required to reclaim certain resources (interrupt and queue EventFDs.) If a device reset is requested by the driver but the underlying device does not support it then generate an error as the driver would not be able to configure it anyway. Signed-off-by: Rob Bradford --- vm-virtio/src/lib.rs | 1 - vm-virtio/src/queue.rs | 6 ++++++ vm-virtio/src/transport/pci_device.rs | 29 +++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/vm-virtio/src/lib.rs b/vm-virtio/src/lib.rs index fadb3616b..b039f53f6 100644 --- a/vm-virtio/src/lib.rs +++ b/vm-virtio/src/lib.rs @@ -30,7 +30,6 @@ pub use self::block::*; pub use self::device::*; pub use self::queue::*; -#[allow(dead_code)] const DEVICE_INIT: u32 = 0x00; const DEVICE_ACKNOWLEDGE: u32 = 0x01; const DEVICE_DRIVER: u32 = 0x02; diff --git a/vm-virtio/src/queue.rs b/vm-virtio/src/queue.rs index 79bae7b20..9deab2ae3 100644 --- a/vm-virtio/src/queue.rs +++ b/vm-virtio/src/queue.rs @@ -256,6 +256,12 @@ impl Queue { min(self.size, self.max_size) } + /// Reset the queue to a state that is acceptable for a device reset + pub fn reset(&mut self) { + self.ready = false; + self.size = self.max_size; + } + pub fn is_valid(&self, mem: &GuestMemoryMmap) -> bool { let queue_size = self.actual_size() as usize; let desc_table = self.desc_table; diff --git a/vm-virtio/src/transport/pci_device.rs b/vm-virtio/src/transport/pci_device.rs index 08e0f9e68..21cac3f5a 100755 --- a/vm-virtio/src/transport/pci_device.rs +++ b/vm-virtio/src/transport/pci_device.rs @@ -29,7 +29,7 @@ use vmm_sys_util::{EventFd, Result}; use super::VirtioPciCommonConfig; use crate::{ Queue, VirtioDevice, DEVICE_ACKNOWLEDGE, DEVICE_DRIVER, DEVICE_DRIVER_OK, DEVICE_FAILED, - DEVICE_FEATURES_OK, + DEVICE_FEATURES_OK, DEVICE_INIT, }; #[allow(clippy::enum_variant_names)] @@ -241,6 +241,11 @@ impl VirtioPciDevice { && self.common_config.driver_status & DEVICE_FAILED as u8 == 0 } + /// Determines if the driver has requested the device (re)init / reset itself + fn is_driver_init(&self) -> bool { + self.common_config.driver_status == DEVICE_INIT as u8 + } + fn are_queues_valid(&self) -> bool { if let Some(mem) = self.memory.as_ref() { self.queues.iter().all(|q| q.is_valid(mem)) @@ -441,7 +446,8 @@ impl PciDevice for VirtioPciDevice { if !self.device_activated && self.is_driver_ready() && self.are_queues_valid() { if let Some(interrupt_evt) = self.interrupt_evt.take() { - if let Some(mem) = self.memory.take() { + if self.memory.is_some() { + let mem = self.memory.as_ref().unwrap().clone(); self.device .activate( mem, @@ -455,6 +461,25 @@ impl PciDevice for VirtioPciDevice { } } } + + // Device has been reset by the driver + if self.device_activated && self.is_driver_init() { + if let Some((interrupt_evt, mut queue_evts)) = self.device.reset() { + // Upon reset the device returns its interrupt EventFD and it's queue EventFDs + self.interrupt_evt = Some(interrupt_evt); + self.queue_evts.append(&mut queue_evts); + + self.device_activated = false; + + // Reset queue readiness (changes queue_enable), queue sizes + // and selected_queue as per spec for reset + self.queues.iter_mut().for_each(Queue::reset); + self.common_config.queue_select = 0; + } else { + error!("Attempt to reset device when not implemented in underlying device"); + self.common_config.driver_status = crate::DEVICE_FAILED as u8; + } + } } }