virtio-devices: Support driver programming fewer queues

It is permissable for the driver to program fewer queues than offered by
the device. Filter the queues so that only the ready ones are included
and check that they have valid addresses configured.

Fixes: #2136

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2021-01-18 17:52:45 +00:00 committed by Sebastien Boeuf
parent c366efc19e
commit a105089702
2 changed files with 34 additions and 56 deletions

View File

@ -250,10 +250,10 @@ impl VirtioCommon {
queue_evts: &[EventFd],
interrupt_cb: &Arc<dyn VirtioInterrupt>,
) -> ActivateResult {
if queues.len() != self.queue_sizes.len() || queue_evts.len() != self.queue_sizes.len() {
if queues.len() != queue_evts.len() {
error!(
"Cannot perform activate. Expected {} queue(s), got {}",
self.queue_sizes.len(),
"Cannot activate: length mismatch: queue_evts={} queues={}",
queue_evts.len(),
queues.len()
);
return Err(ActivateError::BadActivate);

View File

@ -14,7 +14,7 @@ extern crate vmm_sys_util;
use super::VirtioPciCommonConfig;
use crate::transport::VirtioTransport;
use crate::{
Queue, VirtioDevice, VirtioDeviceType, VirtioInterrupt, VirtioInterruptType,
ActivateResult, Queue, VirtioDevice, VirtioDeviceType, VirtioInterrupt, VirtioInterruptType,
DEVICE_ACKNOWLEDGE, DEVICE_DRIVER, DEVICE_DRIVER_OK, DEVICE_FAILED, DEVICE_FEATURES_OK,
DEVICE_INIT, VIRTIO_MSI_NO_VECTOR,
};
@ -515,14 +515,6 @@ impl VirtioPciDevice {
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.memory()))
} else {
false
}
}
// This function is used by the caller to provide the expected base address
// for the virtio-pci configuration BAR.
pub fn set_config_bar_addr(&mut self, bar_addr: u64) {
@ -653,35 +645,40 @@ impl VirtioPciDevice {
self.device.clone()
}
fn activate(&mut self) -> ActivateResult {
if let Some(virtio_interrupt) = self.virtio_interrupt.take() {
if self.memory.is_some() {
let mem = self.memory.as_ref().unwrap().clone();
let mut device = self.device.lock().unwrap();
let mut queue_evts = Vec::new();
let mut queues = self.queues.clone();
queues.retain(|q| q.ready);
for (i, queue) in queues.iter().enumerate() {
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
if !queue.is_valid(&mem.memory()) {
error!("Queue {} is not valid", i);
}
}
return device.activate(mem, virtio_interrupt, queues, queue_evts);
}
}
Ok(())
}
pub fn maybe_activate(&mut self) {
if self.needs_activation() {
if let Some(virtio_interrupt) = self.virtio_interrupt.take() {
if self.memory.is_some() {
let mem = self.memory.as_ref().unwrap().clone();
let mut device = self.device.lock().unwrap();
let mut queue_evts = Vec::new();
let queues = self.queues.clone();
for i in 0..queues.len() {
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
}
device
.activate(mem, virtio_interrupt, queues, queue_evts)
.expect("Failed to activate device");
self.device_activated.store(true, Ordering::SeqCst);
info!("{}: Waiting for barrier", self.id);
self.activate_barrier.wait();
info!("{}: Barrier released", self.id);
}
}
self.activate().expect("Failed to activate device");
self.device_activated.store(true, Ordering::SeqCst);
info!("{}: Waiting for barrier", self.id);
self.activate_barrier.wait();
info!("{}: Barrier released", self.id);
} else {
info!("{}: Device does not need activation", self.id)
}
}
fn needs_activation(&self) -> bool {
!self.device_activated.load(Ordering::SeqCst)
&& self.is_driver_ready()
&& self.are_queues_valid()
!self.device_activated.load(Ordering::SeqCst) && self.is_driver_ready()
}
}
@ -1152,29 +1149,10 @@ impl Snapshottable for VirtioPciDevice {
// Then we can activate the device, as we know at this point that
// the virtqueues are in the right state and the device is ready
// to be activated, which will spawn each virtio worker thread.
if self.device_activated.load(Ordering::SeqCst)
&& self.is_driver_ready()
&& self.are_queues_valid()
{
if let Some(virtio_interrupt) = self.virtio_interrupt.take() {
if self.memory.is_some() {
let mem = self.memory.as_ref().unwrap().clone();
let mut device = self.device.lock().unwrap();
let mut queue_evts = Vec::new();
let queues = self.queues.clone();
for i in 0..queues.len() {
queue_evts.push(self.queue_evts[i].try_clone().unwrap());
}
device
.activate(mem, virtio_interrupt, queues, queue_evts)
.map_err(|e| {
MigratableError::Restore(anyhow!(
"Failed activating the device: {:?}",
e
))
})?;
}
}
if self.device_activated.load(Ordering::SeqCst) && self.is_driver_ready() {
self.activate().map_err(|e| {
MigratableError::Restore(anyhow!("Failed activating the device: {:?}", e))
})?;
}
return Ok(());