From cf707da1a84d17b68d0030be6e82c188b5991bb2 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 21 Apr 2020 17:32:26 +0200 Subject: [PATCH] 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 --- vm-virtio/src/queue.rs | 56 +++++++++++++-------------------- vm-virtio/src/transport/mmio.rs | 21 +++++++++++-- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/vm-virtio/src/queue.rs b/vm-virtio/src/queue.rs index 845019a13..35aca45fe 100644 --- a/vm-virtio/src/queue.rs +++ b/vm-virtio/src/queue.rs @@ -17,7 +17,8 @@ use std::sync::Arc; use crate::device::VirtioIommuRemapping; 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; @@ -29,6 +30,8 @@ pub enum Error { GuestMemoryError, InvalidIndirectDescriptor, InvalidChain, + InvalidOffset(u64), + InvalidRingIndexFromMemory(GuestMemoryError), } impl Display for Error { @@ -39,6 +42,8 @@ impl Display for Error { GuestMemoryError => write!(f, "error accessing guest memory"), InvalidChain => write!(f, "invalid descriptor chain"), 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); } - /// Get latest index from available ring. - pub fn update_avail_index_from_memory(&mut self, mem: &GuestMemoryMmap) { - let index_addr = match mem.checked_offset(self.avail_ring, 2) { - Some(ret) => ret, - None => { - error!("Invalid offset 0x{:x}", self.avail_ring.raw_value() + 2); - return; - } - }; - let next_avail: u16 = match mem.read_obj::(index_addr) { - Ok(ret) => ret, - Err(e) => { - error!("Couldn't read `idx` field from memory: {}", e); - return; - } - }; + /// Get ring's index from memory. + fn index_from_memory(&self, ring: GuestAddress, mem: &GuestMemoryMmap) -> Result { + mem.read_obj::( + mem.checked_offset(ring, 2) + .ok_or_else(|| Error::InvalidOffset(ring.raw_value() + 2))?, + ) + .map_err(Error::InvalidRingIndexFromMemory) + } - self.next_avail = Wrapping(next_avail); + /// Get latest index from available ring. + pub fn avail_index_from_memory(&self, mem: &GuestMemoryMmap) -> Result { + self.index_from_memory(self.avail_ring, mem) } /// Get latest index from used ring. - pub fn update_used_index_from_memory(&mut self, mem: &GuestMemoryMmap) { - let index_addr = match mem.checked_offset(self.used_ring, 2) { - Some(ret) => ret, - None => { - error!("Invalid offset 0x{:x}", self.used_ring.raw_value() + 2); - return; - } - }; - let next_used: u16 = match mem.read_obj::(index_addr) { - Ok(ret) => ret, - Err(e) => { - error!("Couldn't read `idx` field from memory: {}", e); - return; - } - }; + pub fn used_index_from_memory(&self, mem: &GuestMemoryMmap) -> Result { + self.index_from_memory(self.used_ring, mem) + } - self.next_used = Wrapping(next_used); + pub fn available_descriptors(&self, mem: &GuestMemoryMmap) -> Result { + Ok(self.used_index_from_memory(mem)? < self.avail_index_from_memory(mem)?) } } diff --git a/vm-virtio/src/transport/mmio.rs b/vm-virtio/src/transport/mmio.rs index 417ac15d3..b8a63ceb7 100644 --- a/vm-virtio/src/transport/mmio.rs +++ b/vm-virtio/src/transport/mmio.rs @@ -12,6 +12,7 @@ use anyhow::anyhow; use byteorder::{ByteOrder, LittleEndian}; use devices::BusDevice; use libc::EFD_NONBLOCK; +use std::num::Wrapping; use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; @@ -28,6 +29,12 @@ const VENDOR_ID: u32 = 0; const MMIO_MAGIC_VALUE: u32 = 0x7472_6976; const MMIO_VERSION: u32 = 2; +#[derive(Debug)] +enum Error { + /// Failed to retrieve queue ring's index. + QueueRingIndex(crate::queue::Error), +} + pub struct VirtioInterruptIntx { interrupt_status: Arc, interrupt: Arc>, @@ -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.features_select = state.features_select; self.acked_features_select = state.acked_features_select; @@ -166,8 +173,16 @@ impl MmioDevice { if let Some(mem) = self.mem.as_ref() { let mem = mem.memory(); for queue in self.queues.iter_mut() { - queue.update_avail_index_from_memory(&mem); - queue.update_used_index_from_memory(&mem); + queue.next_avail = Wrapping( + queue + .avail_index_from_memory(&mem) + .map_err(Error::QueueRingIndex)?, + ); + queue.next_used = Wrapping( + queue + .used_index_from_memory(&mem) + .map_err(Error::QueueRingIndex)?, + ); } }