#![deny(missing_docs)]
use std::ops::Deref;
use std::ops::DerefMut;
pub mod packed_descriptor_chain;
mod packed_queue;
pub mod split_descriptor_chain;
mod split_queue;
use std::num::Wrapping;
use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use base::warn;
use base::Event;
use cros_async::AsyncError;
use cros_async::EventAsync;
use futures::channel::oneshot;
use futures::select_biased;
use futures::FutureExt;
use packed_queue::PackedQueue;
use serde::Deserialize;
use serde::Serialize;
use snapshot::AnySnapshot;
use split_queue::SplitQueue;
use virtio_sys::virtio_config::VIRTIO_F_RING_PACKED;
use vm_memory::GuestAddress;
use vm_memory::GuestMemory;
use crate::virtio::DescriptorChain;
use crate::virtio::Interrupt;
use crate::virtio::VIRTIO_MSI_NO_VECTOR;
pub struct QueueConfig {
activated: bool,
max_size: u16,
size: u16,
ready: bool,
vector: u16,
features: u64,
acked_features: u64,
desc_table: GuestAddress,
avail_ring: GuestAddress,
used_ring: GuestAddress,
next_avail: Wrapping<u16>,
next_used: Wrapping<u16>,
}
#[derive(Serialize, Deserialize)]
struct QueueConfigSnapshot {
activated: bool,
max_size: u16,
size: u16,
ready: bool,
vector: u16,
features: u64,
acked_features: u64,
desc_table: GuestAddress,
avail_ring: GuestAddress,
used_ring: GuestAddress,
next_avail: Wrapping<u16>,
next_used: Wrapping<u16>,
}
impl QueueConfig {
pub fn new(max_size: u16, features: u64) -> Self {
assert!(max_size > 0);
assert!(max_size <= Queue::MAX_SIZE);
QueueConfig {
activated: false,
max_size,
size: max_size,
ready: false,
vector: VIRTIO_MSI_NO_VECTOR,
desc_table: GuestAddress(0),
avail_ring: GuestAddress(0),
used_ring: GuestAddress(0),
features,
acked_features: 0,
next_used: Wrapping(0),
next_avail: Wrapping(0),
}
}
pub fn max_size(&self) -> u16 {
self.max_size
}
pub fn size(&self) -> u16 {
self.size
}
pub fn set_size(&mut self, val: u16) {
if self.ready {
warn!("ignoring write to size on ready queue");
return;
}
if val > self.max_size {
warn!(
"requested queue size {} is larger than max_size {}",
val, self.max_size
);
return;
}
self.size = val;
}
pub fn vector(&self) -> u16 {
self.vector
}
pub fn set_vector(&mut self, val: u16) {
if self.ready {
warn!("ignoring write to vector on ready queue");
return;
}
self.vector = val;
}
pub fn desc_table(&self) -> GuestAddress {
self.desc_table
}
pub fn set_desc_table(&mut self, val: GuestAddress) {
if self.ready {
warn!("ignoring write to desc_table on ready queue");
return;
}
self.desc_table = val;
}
pub fn avail_ring(&self) -> GuestAddress {
self.avail_ring
}
pub fn set_avail_ring(&mut self, val: GuestAddress) {
if self.ready {
warn!("ignoring write to avail_ring on ready queue");
return;
}
self.avail_ring = val;
}
pub fn used_ring(&self) -> GuestAddress {
self.used_ring
}
pub fn set_used_ring(&mut self, val: GuestAddress) {
if self.ready {
warn!("ignoring write to used_ring on ready queue");
return;
}
self.used_ring = val;
}
pub fn next_avail(&self) -> Wrapping<u16> {
self.next_avail
}
pub fn set_next_avail(&mut self, val: Wrapping<u16>) {
if self.ready {
warn!("ignoring write to next_avail on ready queue");
return;
}
self.next_avail = val;
}
pub fn next_used(&self) -> Wrapping<u16> {
self.next_used
}
pub fn set_next_used(&mut self, val: Wrapping<u16>) {
if self.ready {
warn!("ignoring write to next_used on ready queue");
return;
}
self.next_used = val;
}
pub fn acked_features(&self) -> u64 {
self.acked_features
}
pub fn ack_features(&mut self, features: u64) {
self.acked_features |= features & self.features;
}
pub fn ready(&self) -> bool {
self.ready
}
pub fn set_ready(&mut self, enable: bool) {
self.ready = enable;
}
pub fn activate(
&mut self,
mem: &GuestMemory,
event: Event,
interrupt: Interrupt,
) -> Result<Queue> {
if !self.ready {
bail!("attempted to activate a non-ready queue");
}
if self.activated {
bail!("queue is already activated");
}
let queue: Queue = if ((self.acked_features >> VIRTIO_F_RING_PACKED) & 1) != 0 {
let pq = PackedQueue::new(self, mem, event, interrupt)
.context("Failed to create a packed queue.")?;
Queue::PackedVirtQueue(pq)
} else {
let sq = SplitQueue::new(self, mem, event, interrupt)
.context("Failed to create a split queue.")?;
Queue::SplitVirtQueue(sq)
};
self.activated = true;
Ok(queue)
}
pub fn reset(&mut self) {
self.activated = false;
self.ready = false;
self.size = self.max_size;
self.vector = VIRTIO_MSI_NO_VECTOR;
self.desc_table = GuestAddress(0);
self.avail_ring = GuestAddress(0);
self.used_ring = GuestAddress(0);
self.next_avail = Wrapping(0);
self.next_used = Wrapping(0);
self.acked_features = 0;
}
pub fn snapshot(&self) -> Result<AnySnapshot> {
AnySnapshot::to_any(QueueConfigSnapshot {
activated: self.activated,
max_size: self.max_size,
size: self.size,
ready: self.ready,
vector: self.vector,
features: self.features,
acked_features: self.acked_features,
desc_table: self.desc_table,
avail_ring: self.avail_ring,
used_ring: self.used_ring,
next_avail: self.next_avail,
next_used: self.next_used,
})
.context("error serializing")
}
pub fn restore(&mut self, data: AnySnapshot) -> Result<()> {
let snap: QueueConfigSnapshot =
AnySnapshot::from_any(data).context("error deserializing")?;
self.activated = snap.activated;
self.max_size = snap.max_size;
self.size = snap.size;
self.ready = snap.ready;
self.vector = snap.vector;
self.features = snap.features;
self.acked_features = snap.acked_features;
self.desc_table = snap.desc_table;
self.avail_ring = snap.avail_ring;
self.used_ring = snap.used_ring;
self.next_avail = snap.next_avail;
self.next_used = snap.next_used;
Ok(())
}
}
macro_rules! define_queue_method {
(
$(#[$doc:meta])*
$method:ident, $return_type:ty, $( $var:ident : $vartype:ty ),*
) => {
$(#[$doc])*
pub fn $method(&self, $($var: $vartype),*) -> $return_type {
match self {
Queue::SplitVirtQueue(sq) => sq.$method($($var),*),
Queue::PackedVirtQueue(pq) => pq.$method($($var),*),
}
}
};
(
$(#[$doc:meta])*
$method:ident, $return_type:ty, mut, $( $var:ident : $vartype:ty ),*
) => {
$(#[$doc])*
pub fn $method(&mut self, $($var: $vartype),*) -> $return_type {
match self {
Queue::SplitVirtQueue(sq) => sq.$method($($var),*),
Queue::PackedVirtQueue(pq) => pq.$method($($var),*),
}
}
};
}
#[derive(Debug)]
pub enum Queue {
SplitVirtQueue(SplitQueue),
PackedVirtQueue(PackedQueue),
}
impl Queue {
pub const MAX_SIZE: u16 = 32768;
pub async fn next_async(
&mut self,
eventfd: &mut EventAsync,
) -> std::result::Result<DescriptorChain, AsyncError> {
loop {
if let Some(chain) = self.pop() {
return Ok(chain);
}
eventfd.next_val().await?;
}
}
pub fn peek(&mut self) -> Option<PeekedDescriptorChain> {
let desc_chain = match self {
Queue::SplitVirtQueue(q) => q.peek(),
Queue::PackedVirtQueue(q) => q.peek(),
}?;
Some(PeekedDescriptorChain::new(self, desc_chain))
}
pub fn pop(&mut self) -> Option<DescriptorChain> {
self.peek().map(PeekedDescriptorChain::pop)
}
pub fn try_pop_length(&mut self, request_length: usize) -> Option<Vec<DescriptorChain>> {
match self {
Queue::SplitVirtQueue(q) => q.try_pop_length(request_length),
Queue::PackedVirtQueue(_q) => {
unimplemented!()
}
}
}
pub async fn next_async_interruptable(
&mut self,
queue_event: &mut EventAsync,
mut stop_rx: &mut oneshot::Receiver<()>,
) -> std::result::Result<Option<DescriptorChain>, AsyncError> {
select_biased! {
avail_desc_res = self.next_async(queue_event).fuse() => {
Ok(Some(avail_desc_res?))
}
_ = stop_rx => Ok(None),
}
}
pub fn trigger_interrupt(&mut self) -> bool {
match self {
Queue::SplitVirtQueue(sq) => sq.trigger_interrupt(),
Queue::PackedVirtQueue(pq) => pq.trigger_interrupt(),
}
}
pub fn restore(
queue_config: &QueueConfig,
queue_value: AnySnapshot,
mem: &GuestMemory,
event: Event,
interrupt: Interrupt,
) -> anyhow::Result<Queue> {
if queue_config.acked_features & 1 << VIRTIO_F_RING_PACKED != 0 {
PackedQueue::restore(queue_value, mem, event, interrupt).map(Queue::PackedVirtQueue)
} else {
SplitQueue::restore(queue_value, mem, event, interrupt).map(Queue::SplitVirtQueue)
}
}
pub fn vhost_user_reclaim(&mut self, vring_base: u16) {
match self {
Queue::SplitVirtQueue(q) => q.vhost_user_reclaim(vring_base),
Queue::PackedVirtQueue(q) => q.vhost_user_reclaim(vring_base),
}
}
pub fn next_avail_to_process(&self) -> u16 {
match self {
Queue::SplitVirtQueue(q) => q.next_avail_to_process(),
Queue::PackedVirtQueue(q) => q.next_avail_to_process(),
}
}
define_queue_method!(
vector,
u16,
);
define_queue_method!(
desc_table,
GuestAddress,
);
define_queue_method!(
avail_ring,
GuestAddress,
);
define_queue_method!(
used_ring,
GuestAddress,
);
define_queue_method!(
size,
u16,
);
define_queue_method!(
event,
&Event,
);
define_queue_method!(
interrupt,
&Interrupt,
);
pub fn add_used(&mut self, desc_chain: DescriptorChain) {
let len: u32 = desc_chain.writer.bytes_written().try_into().unwrap();
self.add_used_with_bytes_written(desc_chain, len);
}
pub fn add_used_with_bytes_written(&mut self, desc_chain: DescriptorChain, len: u32) {
let iter = std::iter::once((desc_chain, len));
match self {
Queue::SplitVirtQueue(q) => q.add_used_with_bytes_written_batch(iter),
Queue::PackedVirtQueue(q) => q.add_used_with_bytes_written_batch(iter),
}
}
pub fn add_used_batch(&mut self, desc_chains: impl IntoIterator<Item = DescriptorChain>) {
let iter = desc_chains.into_iter().map(|desc_chain| {
let len: u32 = desc_chain.writer.bytes_written().try_into().unwrap();
(desc_chain, len)
});
match self {
Queue::SplitVirtQueue(q) => q.add_used_with_bytes_written_batch(iter),
Queue::PackedVirtQueue(q) => q.add_used_with_bytes_written_batch(iter),
}
}
define_queue_method!(
snapshot,
Result<AnySnapshot>,
);
}
pub struct PeekedDescriptorChain<'q> {
queue: &'q mut Queue,
desc_chain: DescriptorChain,
}
impl<'q> PeekedDescriptorChain<'q> {
fn new(queue: &'q mut Queue, desc_chain: DescriptorChain) -> Self {
PeekedDescriptorChain { queue, desc_chain }
}
pub fn pop(self) -> DescriptorChain {
match self.queue {
Queue::SplitVirtQueue(q) => q.pop_peeked(&self.desc_chain),
Queue::PackedVirtQueue(q) => q.pop_peeked(&self.desc_chain),
}
self.desc_chain
}
}
impl Deref for PeekedDescriptorChain<'_> {
type Target = DescriptorChain;
fn deref(&self) -> &Self::Target {
&self.desc_chain
}
}
impl DerefMut for PeekedDescriptorChain<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.desc_chain
}
}