mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
virtio-devices: pci: Clear all config state on reset
Per the virtio spec a device reset must return the device to its power-on state. The reset path was only zeroing queue_select. Add VirtioPciCommonConfig::reset() and call it from the transport's reset path so the configuration is cleared. Also relax the condition to allow the device to be reset at any time to match the spec. Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
@@ -160,6 +160,19 @@ impl VirtioPciCommonConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the common configuration to its power-on state. Per the virtio
|
||||
/// spec a device reset must restore the values that a fresh driver would
|
||||
/// observe.
|
||||
pub fn reset(&mut self) {
|
||||
self.driver_status.store(0, Ordering::Release);
|
||||
self.device_feature_select = 0;
|
||||
self.driver_feature_select = 0;
|
||||
self.queue_select = 0;
|
||||
self.msix_config
|
||||
.store(VIRTQ_MSI_NO_VECTOR, Ordering::Release);
|
||||
self.msix_queues.lock().unwrap().fill(VIRTQ_MSI_NO_VECTOR);
|
||||
}
|
||||
|
||||
pub fn read(&mut self, offset: u64, data: &mut [u8], queues: &[Queue]) {
|
||||
assert!(data.len() <= 8);
|
||||
|
||||
@@ -521,4 +534,38 @@ mod unit_tests {
|
||||
// Write queue_msix_vector — must not panic.
|
||||
regs.write(0x1a, &[0xAB, 0xCD], &mut queues);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_returns_initial_state() {
|
||||
let dev: Arc<Mutex<dyn VirtioDevice>> = Arc::new(Mutex::new(DummyDevice(0)));
|
||||
let mut regs = VirtioPciCommonConfig {
|
||||
device: dev,
|
||||
driver_status: Arc::new(AtomicU8::new(0x55)),
|
||||
config_generation: 0xab,
|
||||
device_feature_select: 1,
|
||||
driver_feature_select: 1,
|
||||
queue_select: 7,
|
||||
msix_config: Arc::new(AtomicU16::new(3)),
|
||||
msix_queues: Arc::new(Mutex::new(vec![1, 2, 3])),
|
||||
};
|
||||
|
||||
regs.reset();
|
||||
|
||||
assert_eq!(regs.driver_status.load(Ordering::Acquire), 0);
|
||||
assert_eq!(regs.config_generation, 0xab); // unchanged across reset
|
||||
assert_eq!(regs.device_feature_select, 0);
|
||||
assert_eq!(regs.driver_feature_select, 0);
|
||||
assert_eq!(regs.queue_select, 0);
|
||||
assert_eq!(
|
||||
regs.msix_config.load(Ordering::Acquire),
|
||||
VIRTQ_MSI_NO_VECTOR
|
||||
);
|
||||
assert!(
|
||||
regs.msix_queues
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.all(|v| *v == VIRTQ_MSI_NO_VECTOR)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1245,24 +1245,20 @@ impl PciDevice for VirtioPciDevice {
|
||||
return Some(barrier);
|
||||
}
|
||||
|
||||
// Device has been reset by the driver
|
||||
if self.device_activated.load(Ordering::SeqCst) && self.is_driver_init() {
|
||||
let mut device = self.device.lock().unwrap();
|
||||
if let Some(virtio_interrupt) = device.reset() {
|
||||
// Upon reset the device returns its interrupt EventFD
|
||||
self.virtio_interrupt = Some(virtio_interrupt);
|
||||
self.device_activated.store(false, Ordering::SeqCst);
|
||||
|
||||
// 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
|
||||
.store(crate::DEVICE_FAILED as u8, Ordering::SeqCst);
|
||||
// The driver requested a reset by writing 0 to device_status. Per the
|
||||
// virtio spec this is permitted at any point in initialisation.
|
||||
if self.is_driver_init() {
|
||||
if self.device_activated.swap(false, Ordering::SeqCst) {
|
||||
let mut device = self.device.lock().unwrap();
|
||||
if let Some(virtio_interrupt) = device.reset() {
|
||||
// Upon reset the device returns its interrupt EventFD
|
||||
self.virtio_interrupt = Some(virtio_interrupt);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset queue readiness and the common configuration
|
||||
self.queues.iter_mut().for_each(Queue::reset);
|
||||
self.common_config.reset();
|
||||
}
|
||||
|
||||
None
|
||||
|
||||
Reference in New Issue
Block a user