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 <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2019-05-08 15:59:39 +01:00 committed by Samuel Ortiz
parent 040ea5432d
commit 3b2faa9f11
3 changed files with 33 additions and 3 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}
}
}