vm-virtio: Extend Queue helpers

First, this modifies the existing helpers on how to get indexes for
available and used rings from memory. Instead of updating the queue
through each helper, they are now used as simple getters.

Based on these new getters, we could create a new helper to determine if
the queue has some available descriptors already queued from the driver
side. This helper is going to be particularly helpful when trying to
determine from a virtio thread if a queue is already loaded with some
available buffers that can be used to send information to the guest.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf
2020-04-21 17:32:26 +02:00
parent c22fd39170
commit cf707da1a8
2 changed files with 40 additions and 37 deletions
+22 -34
View File
@@ -17,7 +17,8 @@ use std::sync::Arc;
use crate::device::VirtioIommuRemapping; use crate::device::VirtioIommuRemapping;
use vm_memory::{ use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestUsize, Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap,
GuestUsize,
}; };
pub(super) const VIRTQ_DESC_F_NEXT: u16 = 0x1; pub(super) const VIRTQ_DESC_F_NEXT: u16 = 0x1;
@@ -29,6 +30,8 @@ pub enum Error {
GuestMemoryError, GuestMemoryError,
InvalidIndirectDescriptor, InvalidIndirectDescriptor,
InvalidChain, InvalidChain,
InvalidOffset(u64),
InvalidRingIndexFromMemory(GuestMemoryError),
} }
impl Display for Error { impl Display for Error {
@@ -39,6 +42,8 @@ impl Display for Error {
GuestMemoryError => write!(f, "error accessing guest memory"), GuestMemoryError => write!(f, "error accessing guest memory"),
InvalidChain => write!(f, "invalid descriptor chain"), InvalidChain => write!(f, "invalid descriptor chain"),
InvalidIndirectDescriptor => write!(f, "invalid indirect descriptor"), InvalidIndirectDescriptor => write!(f, "invalid indirect descriptor"),
InvalidOffset(o) => write!(f, "invalid offset {}", o),
InvalidRingIndexFromMemory(e) => write!(f, "invalid ring index from memory: {}", e),
} }
} }
} }
@@ -649,44 +654,27 @@ impl Queue {
self.next_avail -= Wrapping(1); self.next_avail -= Wrapping(1);
} }
/// Get latest index from available ring. /// Get ring's index from memory.
pub fn update_avail_index_from_memory(&mut self, mem: &GuestMemoryMmap) { fn index_from_memory(&self, ring: GuestAddress, mem: &GuestMemoryMmap) -> Result<u16, Error> {
let index_addr = match mem.checked_offset(self.avail_ring, 2) { mem.read_obj::<u16>(
Some(ret) => ret, mem.checked_offset(ring, 2)
None => { .ok_or_else(|| Error::InvalidOffset(ring.raw_value() + 2))?,
error!("Invalid offset 0x{:x}", self.avail_ring.raw_value() + 2); )
return; .map_err(Error::InvalidRingIndexFromMemory)
} }
};
let next_avail: u16 = match mem.read_obj::<u16>(index_addr) {
Ok(ret) => ret,
Err(e) => {
error!("Couldn't read `idx` field from memory: {}", e);
return;
}
};
self.next_avail = Wrapping(next_avail); /// Get latest index from available ring.
pub fn avail_index_from_memory(&self, mem: &GuestMemoryMmap) -> Result<u16, Error> {
self.index_from_memory(self.avail_ring, mem)
} }
/// Get latest index from used ring. /// Get latest index from used ring.
pub fn update_used_index_from_memory(&mut self, mem: &GuestMemoryMmap) { pub fn used_index_from_memory(&self, mem: &GuestMemoryMmap) -> Result<u16, Error> {
let index_addr = match mem.checked_offset(self.used_ring, 2) { self.index_from_memory(self.used_ring, mem)
Some(ret) => ret, }
None => {
error!("Invalid offset 0x{:x}", self.used_ring.raw_value() + 2);
return;
}
};
let next_used: u16 = match mem.read_obj::<u16>(index_addr) {
Ok(ret) => ret,
Err(e) => {
error!("Couldn't read `idx` field from memory: {}", e);
return;
}
};
self.next_used = Wrapping(next_used); pub fn available_descriptors(&self, mem: &GuestMemoryMmap) -> Result<bool, Error> {
Ok(self.used_index_from_memory(mem)? < self.avail_index_from_memory(mem)?)
} }
} }
+18 -3
View File
@@ -12,6 +12,7 @@ use anyhow::anyhow;
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use devices::BusDevice; use devices::BusDevice;
use libc::EFD_NONBLOCK; use libc::EFD_NONBLOCK;
use std::num::Wrapping;
use std::result; use std::result;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -28,6 +29,12 @@ const VENDOR_ID: u32 = 0;
const MMIO_MAGIC_VALUE: u32 = 0x7472_6976; const MMIO_MAGIC_VALUE: u32 = 0x7472_6976;
const MMIO_VERSION: u32 = 2; const MMIO_VERSION: u32 = 2;
#[derive(Debug)]
enum Error {
/// Failed to retrieve queue ring's index.
QueueRingIndex(crate::queue::Error),
}
pub struct VirtioInterruptIntx { pub struct VirtioInterruptIntx {
interrupt_status: Arc<AtomicUsize>, interrupt_status: Arc<AtomicUsize>,
interrupt: Arc<Box<dyn InterruptSourceGroup>>, interrupt: Arc<Box<dyn InterruptSourceGroup>>,
@@ -152,7 +159,7 @@ impl MmioDevice {
} }
} }
fn set_state(&mut self, state: &VirtioMmioDeviceState) -> Result<()> { fn set_state(&mut self, state: &VirtioMmioDeviceState) -> std::result::Result<(), Error> {
self.device_activated = state.device_activated; self.device_activated = state.device_activated;
self.features_select = state.features_select; self.features_select = state.features_select;
self.acked_features_select = state.acked_features_select; self.acked_features_select = state.acked_features_select;
@@ -166,8 +173,16 @@ impl MmioDevice {
if let Some(mem) = self.mem.as_ref() { if let Some(mem) = self.mem.as_ref() {
let mem = mem.memory(); let mem = mem.memory();
for queue in self.queues.iter_mut() { for queue in self.queues.iter_mut() {
queue.update_avail_index_from_memory(&mem); queue.next_avail = Wrapping(
queue.update_used_index_from_memory(&mem); queue
.avail_index_from_memory(&mem)
.map_err(Error::QueueRingIndex)?,
);
queue.next_used = Wrapping(
queue
.used_index_from_memory(&mem)
.map_err(Error::QueueRingIndex)?,
);
} }
} }