virtio-queue: Move to upstream crate from rust-vmm

Now that all the preliminary work has been merged to make Cloud
Hypervisor work with the upstream crate virtio-queue from
rust-vmm/vm-virtio repository, we can move the whole codebase and remove
the local copy of the virtio-queue crate.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2022-01-28 18:00:07 +01:00 committed by Rob Bradford
parent e9eb2c5dbc
commit 9bd1ece9cf
22 changed files with 13 additions and 3414 deletions

11
Cargo.lock generated
View File

@ -481,15 +481,6 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "micro_http"
version = "0.1.0"
@ -1211,9 +1202,9 @@ dependencies = [
[[package]]
name = "virtio-queue"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vm-virtio?branch=main#2ca74a6d776c1b61989aae8c05d2dee4ab3c47a6"
dependencies = [
"log",
"memoffset",
"vm-memory",
"vmm-sys-util",
]

View File

@ -79,7 +79,6 @@ members = [
"vhost_user_block",
"vhost_user_net",
"virtio-devices",
"virtio-queue",
"vmm",
"vm-allocator",
"vm-device",

View File

@ -17,7 +17,7 @@ versionize = "0.1.6"
versionize_derive = "0.1.4"
vhdx = { path = "../vhdx" }
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
virtio-queue = { path = "../virtio-queue" }
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", branch = "main" }
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.9.0"

1
fuzz/Cargo.lock generated
View File

@ -816,6 +816,7 @@ dependencies = [
[[package]]
name = "virtio-queue"
version = "0.1.0"
source = "git+https://github.com/rust-vmm/vm-virtio?branch=main#2ca74a6d776c1b61989aae8c05d2dee4ab3c47a6"
dependencies = [
"log",
"vm-memory",

View File

@ -16,7 +16,7 @@ qcow = { path = "../qcow" }
seccompiler = "0.2.0"
vhdx = { path = "../vhdx" }
virtio-devices = { path = "../virtio-devices" }
virtio-queue = { path = "../virtio-queue" }
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", branch = "main" }
vmm-sys-util = "0.9.0"
vm-virtio = { path = "../vm-virtio" }
vm-memory = "0.7.0"

View File

@ -15,7 +15,7 @@ serde = "1.0.136"
versionize = "0.1.6"
versionize_derive = "0.1.4"
virtio-bindings = "0.1.0"
virtio-queue = { path = "../virtio-queue" }
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", branch = "main" }
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.9.0"

View File

@ -12,7 +12,7 @@ epoll = "4.3.1"
libc = "0.2.116"
log = "0.4.14"
virtio-bindings = "0.1.0"
virtio-queue = { path = "../virtio-queue" }
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", branch = "main" }
vm-memory = { version = "0.7.0", features = ["backend-bitmap"] }
vm-virtio = { path = "../vm-virtio" }
vmm-sys-util = "0.9.0"

View File

@ -30,7 +30,7 @@ versionize = "0.1.6"
versionize_derive = "0.1.4"
vhost = { version = "0.3.0", features = ["vhost-user-master", "vhost-user-slave", "vhost-kern"] }
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
virtio-queue = { path = "../virtio-queue" }
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", branch = "main" }
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }

View File

@ -29,7 +29,7 @@ use std::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering};
use std::sync::{Arc, Barrier, Mutex};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use virtio_queue::{defs::VIRTQ_MSI_NO_VECTOR, Error as QueueError, Queue};
use virtio_queue::{Error as QueueError, Queue};
use vm_allocator::{AddressAllocator, SystemAllocator};
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceGroup, MsiIrqGroupConfig,
@ -42,6 +42,9 @@ use vm_migration::{
use vm_virtio::AccessPlatform;
use vmm_sys_util::{errno::Result, eventfd::EventFd};
/// Vector value used to disable MSI for a queue.
const VIRTQ_MSI_NO_VECTOR: u16 = 0xffff;
#[derive(Debug)]
enum Error {
/// Failed to retrieve queue ring's index.

View File

@ -1,19 +0,0 @@
[package]
name = "virtio-queue"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
description = "virtio queue implementation"
repository = "https://github.com/rust-vmm/vm-virtio"
keywords = ["virtio"]
readme = "README.md"
license = "Apache-2.0 OR BSD-3-Clause"
edition = "2018"
[dependencies]
vm-memory = "0.7.0"
vmm-sys-util = ">=0.8.0"
log = ">=0.4.6"
[dev-dependencies]
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic"] }
memoffset = "~0"

View File

@ -1,459 +0,0 @@
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Copyright © 2019 Intel Corporation
//
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::convert::TryFrom;
use std::fmt::{self, Debug};
use std::mem::size_of;
use std::ops::Deref;
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory};
use crate::defs::VIRTQ_DESCRIPTOR_SIZE;
use crate::{Descriptor, Error};
/// A virtio descriptor chain.
#[derive(Clone, Debug)]
pub struct DescriptorChain<M> {
mem: M,
desc_table: GuestAddress,
queue_size: u16,
head_index: u16,
next_index: u16,
ttl: u16,
is_indirect: bool,
}
impl<M> DescriptorChain<M>
where
M: Deref,
M::Target: GuestMemory,
{
fn with_ttl(
mem: M,
desc_table: GuestAddress,
queue_size: u16,
ttl: u16,
head_index: u16,
) -> Self {
DescriptorChain {
mem,
desc_table,
queue_size,
head_index,
next_index: head_index,
ttl,
is_indirect: false,
}
}
/// Create a new `DescriptorChain` instance.
///
/// # Arguments
/// * `mem` - the `GuestMemory` object that can be used to access the buffers pointed to by the
/// descriptor chain.
/// * `desc_table` - the address of the descriptor table.
/// * `queue_size` - the size of the queue, which is also the maximum size of a descriptor
/// chain.
/// * `head_index` - the descriptor index of the chain head.
pub(crate) fn new(mem: M, desc_table: GuestAddress, queue_size: u16, head_index: u16) -> Self {
Self::with_ttl(mem, desc_table, queue_size, queue_size, head_index)
}
/// Get the descriptor index of the chain head.
pub fn head_index(&self) -> u16 {
self.head_index
}
/// Return a `GuestMemory` object that can be used to access the buffers pointed to by the
/// descriptor chain.
pub fn memory(&self) -> &M::Target {
self.mem.deref()
}
/// Return an iterator that only yields the readable descriptors in the chain.
pub fn readable(self) -> DescriptorChainRwIter<M> {
DescriptorChainRwIter {
chain: self,
writable: false,
}
}
/// Return an iterator that only yields the writable descriptors in the chain.
pub fn writable(self) -> DescriptorChainRwIter<M> {
DescriptorChainRwIter {
chain: self,
writable: true,
}
}
// Alters the internal state of the `DescriptorChain` to switch iterating over an
// indirect descriptor table defined by `desc`.
fn switch_to_indirect_table(&mut self, desc: Descriptor) -> Result<(), Error> {
// Check the VIRTQ_DESC_F_INDIRECT flag (i.e., is_indirect) is not set inside
// an indirect descriptor.
// (see VIRTIO Spec, Section 2.6.5.3.1 Driver Requirements: Indirect Descriptors)
if self.is_indirect {
return Err(Error::InvalidIndirectDescriptor);
}
// Check the target indirect descriptor table is correctly aligned.
if desc.addr().raw_value() & (VIRTQ_DESCRIPTOR_SIZE as u64 - 1) != 0
|| desc.len() & (VIRTQ_DESCRIPTOR_SIZE as u32 - 1) != 0
{
return Err(Error::InvalidIndirectDescriptorTable);
}
// It is safe to do a plain division since we checked above that desc.len() is a multiple of
// VIRTQ_DESCRIPTOR_SIZE, and VIRTQ_DESCRIPTOR_SIZE is != 0.
let table_len = (desc.len() as usize) / VIRTQ_DESCRIPTOR_SIZE;
if table_len > usize::from(u16::MAX) {
return Err(Error::InvalidIndirectDescriptorTable);
}
self.desc_table = desc.addr();
// try_from cannot fail as we've checked table_len above
self.queue_size = u16::try_from(table_len).expect("invalid table_len");
self.next_index = 0;
self.ttl = self.queue_size;
self.is_indirect = true;
Ok(())
}
}
impl<M> Iterator for DescriptorChain<M>
where
M: Deref,
M::Target: GuestMemory,
{
type Item = Descriptor;
/// Return the next descriptor in this descriptor chain, if there is one.
///
/// Note that this is distinct from the next descriptor chain returned by
/// [`AvailIter`](struct.AvailIter.html), which is the head of the next
/// _available_ descriptor chain.
fn next(&mut self) -> Option<Self::Item> {
if self.ttl == 0 || self.next_index >= self.queue_size {
return None;
}
let desc_addr = self
.desc_table
// The multiplication can not overflow an u64 since we are multiplying an u16 with a
// small number.
.checked_add(self.next_index as u64 * size_of::<Descriptor>() as u64)?;
// The guest device driver should not touch the descriptor once submitted, so it's safe
// to use read_obj() here.
let desc = self.mem.read_obj::<Descriptor>(desc_addr).ok()?;
if desc.refers_to_indirect_table() {
self.switch_to_indirect_table(desc).ok()?;
return self.next();
}
if desc.has_next() {
self.next_index = desc.next();
// It's ok to decrement `self.ttl` here because we check at the start of the method
// that it's greater than 0.
self.ttl -= 1;
} else {
self.ttl = 0;
}
Some(desc)
}
}
/// An iterator for readable or writable descriptors.
#[derive(Clone)]
pub struct DescriptorChainRwIter<M> {
chain: DescriptorChain<M>,
writable: bool,
}
impl<M> Iterator for DescriptorChainRwIter<M>
where
M: Deref,
M::Target: GuestMemory,
{
type Item = Descriptor;
/// Return the next readable/writeable descriptor (depending on the `writable` value) in this
/// descriptor chain, if there is one.
///
/// Note that this is distinct from the next descriptor chain returned by
/// [`AvailIter`](struct.AvailIter.html), which is the head of the next
/// _available_ descriptor chain.
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.chain.next() {
Some(v) => {
if v.is_write_only() == self.writable {
return Some(v);
}
}
None => return None,
}
}
}
}
// We can't derive Debug, because rustc doesn't generate the `M::T: Debug` constraint
impl<M> Debug for DescriptorChainRwIter<M>
where
M: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DescriptorChainRwIter")
.field("chain", &self.chain)
.field("writable", &self.writable)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::defs::{VIRTQ_DESC_F_INDIRECT, VIRTQ_DESC_F_NEXT};
use crate::mock::{DescriptorTable, MockSplitQueue};
use vm_memory::GuestMemoryMmap;
#[test]
fn test_checked_new_descriptor_chain() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
assert!(vq.end().0 < 0x1000);
// index >= queue_size
assert!(
DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 16)
.next()
.is_none()
);
// desc_table address is way off
assert!(
DescriptorChain::<&GuestMemoryMmap>::new(m, GuestAddress(0x00ff_ffff_ffff), 16, 0,)
.next()
.is_none()
);
{
// the first desc has a normal len, and the next_descriptor flag is set
// but the the index of the next descriptor is too large
let desc = Descriptor::new(0x1000, 0x1000, VIRTQ_DESC_F_NEXT, 16);
vq.desc_table().store(0, desc);
let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0);
c.next().unwrap();
assert!(c.next().is_none());
}
// finally, let's test an ok chain
{
let desc = Descriptor::new(0x1000, 0x1000, VIRTQ_DESC_F_NEXT, 1);
vq.desc_table().store(0, desc);
let desc = Descriptor::new(0x2000, 0x1000, 0, 0);
vq.desc_table().store(1, desc);
let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0);
assert_eq!(
c.memory() as *const GuestMemoryMmap,
m as *const GuestMemoryMmap
);
assert_eq!(c.desc_table, vq.start());
assert_eq!(c.queue_size, 16);
assert_eq!(c.ttl, c.queue_size);
let desc = c.next().unwrap();
assert_eq!(desc.addr(), GuestAddress(0x1000));
assert_eq!(desc.len(), 0x1000);
assert_eq!(desc.flags(), VIRTQ_DESC_F_NEXT);
assert_eq!(desc.next(), 1);
assert_eq!(c.ttl, c.queue_size - 1);
assert!(c.next().is_some());
// The descriptor above was the last from the chain, so `ttl` should be 0 now.
assert_eq!(c.ttl, 0);
assert!(c.next().is_none());
assert_eq!(c.ttl, 0);
}
}
#[test]
fn test_ttl_wrap_around() {
const QUEUE_SIZE: u16 = 16;
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x100000)]).unwrap();
let vq = MockSplitQueue::new(m, QUEUE_SIZE);
// Populate the entire descriptor table with entries. Only the last one should not have the
// VIRTQ_DESC_F_NEXT set.
for i in 0..QUEUE_SIZE - 1 {
let desc = Descriptor::new(0x1000 * (i + 1) as u64, 0x1000, VIRTQ_DESC_F_NEXT, i + 1);
vq.desc_table().store(i, desc);
}
let desc = Descriptor::new((0x1000 * 16) as u64, 0x1000, 0, 0);
vq.desc_table().store(QUEUE_SIZE - 1, desc);
let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), QUEUE_SIZE, 0);
assert_eq!(c.ttl, c.queue_size);
// Validate that `ttl` wraps around even when the entire descriptor table is populated.
for i in 0..QUEUE_SIZE {
let _desc = c.next().unwrap();
assert_eq!(c.ttl, c.queue_size - i - 1);
}
assert!(c.next().is_none());
}
#[test]
fn test_new_from_indirect_descriptor() {
// This is testing that chaining an indirect table works as expected. It is also a negative
// test for the following requirement from the spec:
// `A driver MUST NOT set both VIRTQ_DESC_F_INDIRECT and VIRTQ_DESC_F_NEXT in flags.`. In
// case the driver is setting both of these flags, we check that the device doesn't panic.
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let dtable = vq.desc_table();
// Create a chain with one normal descriptor and one pointing to an indirect table.
let desc = Descriptor::new(0x6000, 0x1000, VIRTQ_DESC_F_NEXT, 1);
dtable.store(0, desc);
// The spec forbids setting both VIRTQ_DESC_F_INDIRECT and VIRTQ_DESC_F_NEXT in flags. We do
// not currently enforce this rule, we just ignore the VIRTQ_DESC_F_NEXT flag.
let desc = Descriptor::new(0x7000, 0x1000, VIRTQ_DESC_F_INDIRECT | VIRTQ_DESC_F_NEXT, 2);
dtable.store(1, desc);
let desc = Descriptor::new(0x8000, 0x1000, 0, 0);
dtable.store(2, desc);
let mut c: DescriptorChain<&GuestMemoryMmap> = DescriptorChain::new(m, vq.start(), 16, 0);
// create an indirect table with 4 chained descriptors
let idtable = DescriptorTable::new(m, GuestAddress(0x7000), 4);
for i in 0..4u16 {
let desc = if i < 3 {
Descriptor::new(0x1000 * i as u64, 0x1000, VIRTQ_DESC_F_NEXT, i + 1)
} else {
Descriptor::new(0x1000 * i as u64, 0x1000, 0, 0)
};
idtable.store(i, desc);
}
assert_eq!(c.head_index(), 0);
// Consume the first descriptor.
c.next().unwrap();
// The chain logic hasn't parsed the indirect descriptor yet.
assert!(!c.is_indirect);
// Try to iterate through the indirect descriptor chain.
for i in 0..4 {
let desc = c.next().unwrap();
assert!(c.is_indirect);
if i < 3 {
assert_eq!(desc.flags(), VIRTQ_DESC_F_NEXT);
assert_eq!(desc.next(), i + 1);
}
}
// Even though we added a new descriptor after the one that is pointing to the indirect
// table, this descriptor won't be available when parsing the chain.
assert!(c.next().is_none());
}
#[test]
fn test_indirect_descriptor_err() {
// We are testing here different misconfigurations of the indirect table. For these error
// case scenarios, the iterator over the descriptor chain won't return a new descriptor.
{
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
// Create a chain with a descriptor pointing to an invalid indirect table: addr not a
// multiple of descriptor size.
let desc = Descriptor::new(0x1001, 0x1000, VIRTQ_DESC_F_INDIRECT, 0);
vq.desc_table().store(0, desc);
let mut c: DescriptorChain<&GuestMemoryMmap> =
DescriptorChain::new(m, vq.start(), 16, 0);
assert!(c.next().is_none());
}
{
let m = &GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
// Create a chain with a descriptor pointing to an invalid indirect table: len not a
// multiple of descriptor size.
let desc = Descriptor::new(0x1000, 0x1001, VIRTQ_DESC_F_INDIRECT, 0);
vq.desc_table().store(0, desc);
let mut c: DescriptorChain<&GuestMemoryMmap> =
DescriptorChain::new(m, vq.start(), 16, 0);
assert!(c.next().is_none());
}
{
let m = &GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
// Create a chain with a descriptor pointing to an invalid indirect table: table len >
// u16::MAX.
let desc = Descriptor::new(
0x1000,
(u16::MAX as u32 + 1) * VIRTQ_DESCRIPTOR_SIZE as u32,
VIRTQ_DESC_F_INDIRECT,
0,
);
vq.desc_table().store(0, desc);
let mut c: DescriptorChain<&GuestMemoryMmap> =
DescriptorChain::new(m, vq.start(), 16, 0);
assert!(c.next().is_none());
}
{
let m = &GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
// Create a chain with a descriptor pointing to an indirect table.
let desc = Descriptor::new(0x1000, 0x1000, VIRTQ_DESC_F_INDIRECT, 0);
vq.desc_table().store(0, desc);
// It's ok for an indirect descriptor to have flags = 0.
let desc = Descriptor::new(0x3000, 0x1000, 0, 0);
m.write_obj(desc, GuestAddress(0x1000)).unwrap();
let mut c: DescriptorChain<&GuestMemoryMmap> =
DescriptorChain::new(m, vq.start(), 16, 0);
assert!(c.next().is_some());
// But it's not allowed to have an indirect descriptor that points to another indirect
// table.
let desc = Descriptor::new(0x3000, 0x1000, VIRTQ_DESC_F_INDIRECT, 0);
m.write_obj(desc, GuestAddress(0x1000)).unwrap();
let mut c: DescriptorChain<&GuestMemoryMmap> =
DescriptorChain::new(m, vq.start(), 16, 0);
assert!(c.next().is_none());
}
}
}

View File

@ -1,59 +0,0 @@
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//! Virtio queue related constant definitions
/// Marks a buffer as continuing via the next field.
pub const VIRTQ_DESC_F_NEXT: u16 = 0x1;
/// Marks a buffer as device write-only.
pub const VIRTQ_DESC_F_WRITE: u16 = 0x2;
/// Marks a buffer as containing a list of buffer descriptors.
pub const VIRTQ_DESC_F_INDIRECT: u16 = 0x4;
/// Flag to disable guest notification for used descriptors.
pub const VIRTQ_USED_F_NO_NOTIFY: u16 = 0x1;
/// Size of one element in the used ring, id (le32) + len (le32).
pub(crate) const VIRTQ_USED_ELEMENT_SIZE: u64 = 8;
/// Size of used ring header: flags (u16) + idx (u16)
pub(crate) const VIRTQ_USED_RING_HEADER_SIZE: u64 = 4;
/// Size of the used ring metadata: header + avail_event (le16).
///
/// The total size of the used ring is:
/// VIRTQ_USED_RING_META_SIZE + VIRTQ_USED_ELEMENT_SIZE * queue_size.
pub(crate) const VIRTQ_USED_RING_META_SIZE: u64 = VIRTQ_USED_RING_HEADER_SIZE + 2;
/// Size of one element in the available ring (le16).
pub(crate) const VIRTQ_AVAIL_ELEMENT_SIZE: u64 = 2;
/// Size of available ring header: flags(u16) + idx(u16)
pub(crate) const VIRTQ_AVAIL_RING_HEADER_SIZE: u64 = 4;
/// Size of the available ring metadata: header + used_event (le16).
///
/// The total size of the available ring is:
/// VIRTQ_AVAIL_RING_META_SIZE + VIRTQ_AVAIL_ELEMENT_SIZE * queue_size.
pub(crate) const VIRTQ_AVAIL_RING_META_SIZE: u64 = VIRTQ_AVAIL_RING_HEADER_SIZE + 2;
/// Size of virtio descriptor.
///
/// The Virtio Spec 1.0 defines the alignment of VirtIO descriptor is 16 bytes,
/// which fulfills the explicit constraint of GuestMemory::read_obj().
pub(crate) const VIRTQ_DESCRIPTOR_SIZE: usize = 16;
/// Default guest physical address for descriptor table.
pub(crate) const DEFAULT_DESC_TABLE_ADDR: u64 = 0x0;
/// Default guest physical address for available ring.
pub(crate) const DEFAULT_AVAIL_RING_ADDR: u64 = 0x0;
/// Default guest physical address for used ring.
pub(crate) const DEFAULT_USED_RING_ADDR: u64 = 0x0;
/// Vector value used to disable MSI for a queue.
pub const VIRTQ_MSI_NO_VECTOR: u16 = 0xffff;

View File

@ -1,270 +0,0 @@
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Copyright © 2019 Intel Corporation
//
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use vm_memory::{ByteValued, GuestAddress, Le16, Le32, Le64};
use crate::defs::{VIRTQ_DESC_F_INDIRECT, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE};
/// A virtio descriptor constraints with C representation.
///
/// # Example
///
/// ```rust
/// # use virtio_queue::defs::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE};
/// # use virtio_queue::mock::MockSplitQueue;
/// use virtio_queue::{Descriptor, Queue};
/// use vm_memory::{GuestAddress, GuestMemoryMmap};
///
/// # fn populate_queue(m: &GuestMemoryMmap) -> Queue<&GuestMemoryMmap> {
/// # let vq = MockSplitQueue::new(m, 16);
/// # let mut q = vq.create_queue(m);
/// #
/// # // We have only one chain: (0, 1).
/// # let desc = Descriptor::new(0x1000, 0x1000, VIRTQ_DESC_F_NEXT, 1);
/// # vq.desc_table().store(0, desc);
/// # let desc = Descriptor::new(0x2000, 0x1000, VIRTQ_DESC_F_WRITE, 0);
/// # vq.desc_table().store(1, desc);
/// #
/// # vq.avail().ring().ref_at(0).store(u16::to_le(0));
/// # vq.avail().idx().store(u16::to_le(1));
/// # q
/// # }
/// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
/// // Populate the queue with descriptor chains and update the available ring accordingly.
/// let mut queue = populate_queue(m);
/// let mut i = queue.iter().unwrap();
/// let mut c = i.next().unwrap();
///
/// // Get the first descriptor and access its fields.
/// let desc = c.next().unwrap();
/// let _addr = desc.addr();
/// let _len = desc.len();
/// let _flags = desc.flags();
/// let _next = desc.next();
/// let _is_write_only = desc.is_write_only();
/// let _has_next = desc.has_next();
/// let _refers_to_ind_table = desc.refers_to_indirect_table();
/// ```
// Note that the `ByteValued` implementation of this structure expects the `Descriptor` to store
// only plain old data types.
#[repr(C)]
#[derive(Default, Clone, Copy, Debug)]
pub struct Descriptor {
/// Guest physical address of device specific data.
addr: Le64,
/// Length of device specific data.
len: Le32,
/// Includes next, write, and indirect bits.
flags: Le16,
/// Index into the descriptor table of the next descriptor if flags has the `next` bit set.
next: Le16,
}
#[allow(clippy::len_without_is_empty)]
impl Descriptor {
/// Return the guest physical address of the descriptor buffer.
pub fn addr(&self) -> GuestAddress {
GuestAddress(self.addr.into())
}
/// Return the length of the descriptor buffer.
pub fn len(&self) -> u32 {
self.len.into()
}
/// Return the flags for this descriptor, including next, write and indirect bits.
pub fn flags(&self) -> u16 {
self.flags.into()
}
/// Return the value stored in the `next` field of the descriptor.
pub fn next(&self) -> u16 {
self.next.into()
}
/// Check whether this descriptor refers to a buffer containing an indirect descriptor table.
pub fn refers_to_indirect_table(&self) -> bool {
self.flags() & VIRTQ_DESC_F_INDIRECT != 0
}
/// Check whether the `VIRTQ_DESC_F_NEXT` is set for the descriptor.
pub fn has_next(&self) -> bool {
self.flags() & VIRTQ_DESC_F_NEXT != 0
}
/// Check if the driver designated this as a write only descriptor.
///
/// If this is false, this descriptor is read only.
/// Write only means the the emulated device can write and the driver can read.
pub fn is_write_only(&self) -> bool {
self.flags() & VIRTQ_DESC_F_WRITE != 0
}
/// Set the guest physical address of the descriptor buffer.
pub fn set_addr(&mut self, addr: u64) {
self.addr = addr.into();
}
}
impl Descriptor {
/// Create a new descriptor.
///
/// # Arguments
/// * `addr` - the guest physical address of the descriptor buffer.
/// * `len` - the length of the descriptor buffer.
/// * `flags` - the `flags` for the descriptor.
/// * `next` - the `next` field of the descriptor.
pub fn new(addr: u64, len: u32, flags: u16, next: u16) -> Self {
Descriptor {
addr: addr.into(),
len: len.into(),
flags: flags.into(),
next: next.into(),
}
}
/// Set the length of the descriptor buffer.
pub fn set_len(&mut self, len: u32) {
self.len = len.into();
}
/// Set the flags for this descriptor.
pub fn set_flags(&mut self, flags: u16) {
self.flags = flags.into();
}
/// Set the value stored in the `next` field of the descriptor.
pub fn set_next(&mut self, next: u16) {
self.next = next.into();
}
}
// This is safe because `Descriptor` contains only wrappers over POD types and all accesses through
// safe `vm-memory` API will validate any garbage that could be included in there.
unsafe impl ByteValued for Descriptor {}
/// Represents the contents of an element from the used virtqueue ring.
// Note that the `ByteValued` implementation of this structure expects the `VirtqUsedElem` to store
// only plain old data types.
#[repr(C)]
#[derive(Clone, Copy, Default, Debug)]
pub struct VirtqUsedElem {
id: Le32,
len: Le32,
}
impl VirtqUsedElem {
/// Create a new `VirtqUsedElem` instance.
///
/// # Arguments
/// * `id` - the index of the used descriptor chain.
/// * `len` - the total length of the descriptor chain which was used (written to).
pub(crate) fn new(id: u32, len: u32) -> Self {
VirtqUsedElem {
id: id.into(),
len: len.into(),
}
}
}
#[allow(clippy::len_without_is_empty)]
impl VirtqUsedElem {
/// Get the index of the used descriptor chain.
pub fn id(&self) -> u32 {
self.id.into()
}
/// Get `length` field of the used ring entry.
pub fn len(&self) -> u32 {
self.len.into()
}
}
// This is safe because `VirtqUsedElem` contains only wrappers over POD types and all accesses
// through safe `vm-memory` API will validate any garbage that could be included in there.
unsafe impl ByteValued for VirtqUsedElem {}
#[cfg(test)]
mod tests {
use super::*;
use memoffset::offset_of;
use std::mem::{align_of, size_of};
#[test]
fn test_descriptor_offset() {
assert_eq!(size_of::<Descriptor>(), 16);
assert_eq!(offset_of!(Descriptor, addr), 0);
assert_eq!(offset_of!(Descriptor, len), 8);
assert_eq!(offset_of!(Descriptor, flags), 12);
assert_eq!(offset_of!(Descriptor, next), 14);
assert!(align_of::<Descriptor>() <= 16);
}
#[test]
fn test_descriptor_getter_setter() {
let mut desc = Descriptor::new(0, 0, 0, 0);
desc.set_addr(0x1000);
assert_eq!(desc.addr(), GuestAddress(0x1000));
desc.set_len(0x2000);
assert_eq!(desc.len(), 0x2000);
desc.set_flags(VIRTQ_DESC_F_NEXT);
assert_eq!(desc.flags(), VIRTQ_DESC_F_NEXT);
assert!(desc.has_next());
assert!(!desc.is_write_only());
assert!(!desc.refers_to_indirect_table());
desc.set_flags(VIRTQ_DESC_F_WRITE);
assert_eq!(desc.flags(), VIRTQ_DESC_F_WRITE);
assert!(!desc.has_next());
assert!(desc.is_write_only());
assert!(!desc.refers_to_indirect_table());
desc.set_flags(VIRTQ_DESC_F_INDIRECT);
assert_eq!(desc.flags(), VIRTQ_DESC_F_INDIRECT);
assert!(!desc.has_next());
assert!(!desc.is_write_only());
assert!(desc.refers_to_indirect_table());
desc.set_next(3);
assert_eq!(desc.next(), 3);
}
#[test]
fn test_descriptor_copy() {
let e1 = Descriptor::new(1, 2, VIRTQ_DESC_F_NEXT, 3);
let mut e2 = Descriptor::default();
e2.as_mut_slice().copy_from_slice(e1.as_slice());
assert_eq!(e1.addr(), e2.addr());
assert_eq!(e1.len(), e2.len());
assert_eq!(e1.flags(), e2.flags());
assert_eq!(e1.next(), e2.next());
}
#[test]
fn test_used_elem_offset() {
assert_eq!(offset_of!(VirtqUsedElem, id), 0);
assert_eq!(offset_of!(VirtqUsedElem, len), 4);
assert_eq!(size_of::<VirtqUsedElem>(), 8);
}
#[test]
fn test_used_elem_copy() {
let e1 = VirtqUsedElem::new(3, 15);
let mut e2 = VirtqUsedElem::new(0, 0);
e2.as_mut_slice().copy_from_slice(e1.as_slice());
assert_eq!(e1.id, e2.id);
assert_eq!(e1.len, e2.len);
}
}

View File

@ -1,319 +0,0 @@
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Copyright © 2019 Intel Corporation
//
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::num::Wrapping;
use std::ops::Deref;
use std::sync::atomic::Ordering;
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory};
use crate::defs::{VIRTQ_AVAIL_ELEMENT_SIZE, VIRTQ_AVAIL_RING_HEADER_SIZE};
use crate::{error, DescriptorChain, QueueState};
/// Consuming iterator over all available descriptor chain heads in the queue.
///
/// # Example
///
/// ```rust
/// # use virtio_queue::defs::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE};
/// # use virtio_queue::mock::MockSplitQueue;
/// use virtio_queue::{Descriptor, Queue};
/// use vm_memory::{GuestAddress, GuestMemoryMmap};
///
/// # fn populate_queue(m: &GuestMemoryMmap) -> Queue<&GuestMemoryMmap> {
/// # let vq = MockSplitQueue::new(m, 16);
/// # let mut q = vq.create_queue(m);
/// #
/// # // The chains are (0, 1), (2, 3, 4) and (5, 6).
/// # for i in 0..7 {
/// # let flags = match i {
/// # 1 | 6 => 0,
/// # 2 | 5 => VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE,
/// # 4 => VIRTQ_DESC_F_WRITE,
/// # _ => VIRTQ_DESC_F_NEXT,
/// # };
/// #
/// # let desc = Descriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags, i + 1);
/// # vq.desc_table().store(i, desc);
/// # }
/// #
/// # vq.avail().ring().ref_at(0).store(u16::to_le(0));
/// # vq.avail().ring().ref_at(1).store(u16::to_le(2));
/// # vq.avail().ring().ref_at(2).store(u16::to_le(5));
/// # vq.avail().idx().store(u16::to_le(3));
/// # q
/// # }
/// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
/// // Populate the queue with descriptor chains and update the available ring accordingly.
/// let mut queue = populate_queue(m);
/// let mut i = queue.iter().unwrap();
///
/// {
/// let mut c = i.next().unwrap();
/// let _first_head_index = c.head_index();
/// // We should have two descriptors in the first chain.
/// let _desc1 = c.next().unwrap();
/// let _desc2 = c.next().unwrap();
/// }
///
/// {
/// let c = i.next().unwrap();
/// let _second_head_index = c.head_index();
///
/// let mut iter = c.writable();
/// // We should have two writable descriptors in the second chain.
/// let _desc1 = iter.next().unwrap();
/// let _desc2 = iter.next().unwrap();
/// }
///
/// {
/// let c = i.next().unwrap();
/// let _third_head_index = c.head_index();
///
/// let mut iter = c.readable();
/// // We should have one readable descriptor in the third chain.
/// let _desc1 = iter.next().unwrap();
/// }
/// // Let's go back one position in the available ring.
/// i.go_to_previous_position();
/// // We should be able to access again the third descriptor chain.
/// let c = i.next().unwrap();
/// let _third_head_index = c.head_index();
/// ```
#[derive(Debug)]
pub struct AvailIter<'b, M> {
mem: M,
desc_table: GuestAddress,
avail_ring: GuestAddress,
queue_size: u16,
last_index: Wrapping<u16>,
next_avail: &'b mut Wrapping<u16>,
}
impl<'b, M> AvailIter<'b, M>
where
M: Deref,
M::Target: GuestMemory + Sized,
{
/// Create a new instance of `AvailInter`.
///
/// # Arguments
/// * `mem` - the `GuestMemory` object that can be used to access the queue buffers.
/// * `idx` - the index of the available ring entry where the driver would put the next
/// available descriptor chain.
/// * `state` - the `QueueState` object from which the needed data to create the `AvailIter` can
/// be retrieved.
pub(crate) fn new(mem: M, idx: Wrapping<u16>, state: &'b mut QueueState) -> Self {
AvailIter {
mem,
desc_table: state.desc_table,
avail_ring: state.avail_ring,
queue_size: state.size,
last_index: idx,
next_avail: &mut state.next_avail,
}
}
/// Goes back one position in the available descriptor chain offered by the driver.
///
/// Rust does not support bidirectional iterators. This is the only way to revert the effect
/// of an iterator increment on the queue.
///
/// Note: this method assumes there's only one thread manipulating the queue, so it should only
/// be invoked in single-threaded context.
pub fn go_to_previous_position(&mut self) {
*self.next_avail -= Wrapping(1);
}
}
impl<'b, M> Iterator for AvailIter<'b, M>
where
M: Clone + Deref,
M::Target: GuestMemory,
{
type Item = DescriptorChain<M>;
fn next(&mut self) -> Option<Self::Item> {
if *self.next_avail == self.last_index {
return None;
}
// These two operations can not overflow an u64 since they're working with relatively small
// numbers compared to u64::MAX.
let elem_off = u64::from(self.next_avail.0 % self.queue_size) * VIRTQ_AVAIL_ELEMENT_SIZE;
let offset = VIRTQ_AVAIL_RING_HEADER_SIZE + elem_off;
let addr = self.avail_ring.checked_add(offset)?;
let head_index: u16 = self
.mem
.load(addr, Ordering::Acquire)
.map(u16::from_le)
.map_err(|_| error!("Failed to read from memory {:x}", addr.raw_value()))
.ok()?;
*self.next_avail += Wrapping(1);
Some(DescriptorChain::new(
self.mem.clone(),
self.desc_table,
self.queue_size,
head_index,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::defs::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE};
use crate::mock::MockSplitQueue;
use crate::Descriptor;
use vm_memory::GuestMemoryMmap;
#[test]
fn test_descriptor_and_iterator() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
// q is currently valid
assert!(q.is_valid());
// the chains are (0, 1), (2, 3, 4) and (5, 6)
for j in 0..7 {
let flags = match j {
1 | 6 => 0,
2 | 5 => VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE,
4 => VIRTQ_DESC_F_WRITE,
_ => VIRTQ_DESC_F_NEXT,
};
let desc = Descriptor::new((0x1000 * (j + 1)) as u64, 0x1000, flags, j + 1);
vq.desc_table().store(j, desc);
}
vq.avail().ring().ref_at(0).store(u16::to_le(0));
vq.avail().ring().ref_at(1).store(u16::to_le(2));
vq.avail().ring().ref_at(2).store(u16::to_le(5));
vq.avail().idx().store(u16::to_le(3));
let mut i = q.iter().unwrap();
{
let c = i.next().unwrap();
assert_eq!(c.head_index(), 0);
let mut iter = c;
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
{
let c = i.next().unwrap();
assert_eq!(c.head_index(), 2);
let mut iter = c.writable();
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
{
let c = i.next().unwrap();
assert_eq!(c.head_index(), 5);
let mut iter = c.readable();
assert!(iter.next().is_some());
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
}
#[test]
fn test_iterator() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
q.state.size = q.state.max_size;
q.state.desc_table = vq.desc_table_addr();
q.state.avail_ring = vq.avail_addr();
q.state.used_ring = vq.used_addr();
assert!(q.is_valid());
{
// an invalid queue should return an iterator with no next
q.state.ready = false;
let mut i = q.iter().unwrap();
assert!(i.next().is_none());
}
q.state.ready = true;
// now let's create two simple descriptor chains
// the chains are (0, 1) and (2, 3, 4)
{
for j in 0..5u16 {
let flags = match j {
1 | 4 => 0,
_ => VIRTQ_DESC_F_NEXT,
};
let desc = Descriptor::new((0x1000 * (j + 1)) as u64, 0x1000, flags, j + 1);
vq.desc_table().store(j, desc);
}
vq.avail().ring().ref_at(0).store(u16::to_le(0));
vq.avail().ring().ref_at(1).store(u16::to_le(2));
vq.avail().idx().store(u16::to_le(2));
let mut i = q.iter().unwrap();
{
let mut c = i.next().unwrap();
assert_eq!(c.head_index(), 0);
c.next().unwrap();
assert!(c.next().is_some());
assert!(c.next().is_none());
assert_eq!(c.head_index(), 0);
}
{
let mut c = i.next().unwrap();
assert_eq!(c.head_index(), 2);
c.next().unwrap();
c.next().unwrap();
c.next().unwrap();
assert!(c.next().is_none());
assert_eq!(c.head_index(), 2);
}
// also test go_to_previous_position() works as expected
{
assert!(i.next().is_none());
i.go_to_previous_position();
let mut c = q.iter().unwrap().next().unwrap();
c.next().unwrap();
c.next().unwrap();
c.next().unwrap();
assert!(c.next().is_none());
}
}
}
}

View File

@ -1,192 +0,0 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright © 2019 Intel Corporation
//
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
//! Virtio queue API for backend device drivers to access virtio queues.
#![deny(missing_docs)]
use std::fmt::{self, Debug, Display};
use std::num::Wrapping;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::Ordering;
use log::error;
use vm_memory::{GuestMemory, GuestMemoryError};
pub use self::chain::{DescriptorChain, DescriptorChainRwIter};
pub use self::descriptor::{Descriptor, VirtqUsedElem};
pub use self::iterator::AvailIter;
pub use self::queue::Queue;
pub use self::queue_guard::QueueGuard;
pub use self::state::QueueState;
pub use self::state_sync::QueueStateSync;
pub mod defs;
pub mod mock;
mod chain;
mod descriptor;
mod iterator;
mod queue;
mod queue_guard;
mod state;
mod state_sync;
/// Virtio Queue related errors.
#[derive(Debug)]
pub enum Error {
/// Address overflow.
AddressOverflow,
/// Failed to access guest memory.
GuestMemory(GuestMemoryError),
/// Invalid indirect descriptor.
InvalidIndirectDescriptor,
/// Invalid indirect descriptor table.
InvalidIndirectDescriptorTable,
/// Invalid descriptor chain.
InvalidChain,
/// Invalid descriptor index.
InvalidDescriptorIndex,
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
AddressOverflow => write!(f, "address overflow"),
GuestMemory(_) => write!(f, "error accessing guest memory"),
InvalidChain => write!(f, "invalid descriptor chain"),
InvalidIndirectDescriptor => write!(f, "invalid indirect descriptor"),
InvalidIndirectDescriptorTable => write!(f, "invalid indirect descriptor table"),
InvalidDescriptorIndex => write!(f, "invalid descriptor index"),
}
}
}
impl std::error::Error for Error {}
/// Trait for objects returned by `QueueStateT::lock()`.
pub trait QueueStateGuard<'a> {
/// Type for guard returned by `Self::lock()`.
type G: DerefMut<Target = QueueState>;
}
/// Trait to access and manipulate a virtio queue.
///
/// To optimize for performance, different implementations of the `QueueStateT` trait may be
/// provided for single-threaded context and multi-threaded context.
///
/// Using Higher-Rank Trait Bounds (HRTBs) to effectively define an associated type that has a
/// lifetime parameter, without tagging the `QueueStateT` trait with a lifetime as well.
pub trait QueueStateT: for<'a> QueueStateGuard<'a> {
/// Construct an empty virtio queue state object with the given `max_size`.
fn new(max_size: u16) -> Self;
/// Check whether the queue configuration is valid.
fn is_valid<M: GuestMemory>(&self, mem: &M) -> bool;
/// Reset the queue to the initial state.
fn reset(&mut self);
/// Get an exclusive reference to the underlying `QueueState` object.
///
/// Logically this method will acquire the underlying lock protecting the `QueueState` Object.
/// The lock will be released when the returned object gets dropped.
fn lock(&mut self) -> <Self as QueueStateGuard>::G;
/// Get an exclusive reference to the underlying `QueueState` object with an associated
/// `GuestMemory` object.
///
/// Logically this method will acquire the underlying lock protecting the `QueueState` Object.
/// The lock will be released when the returned object gets dropped.
fn lock_with_memory<M>(&mut self, mem: M) -> QueueGuard<M, <Self as QueueStateGuard>::G>
where
M: Deref + Clone,
M::Target: GuestMemory + Sized,
{
QueueGuard::new(self.lock(), mem)
}
/// Get the maximum size of the virtio queue.
fn max_size(&self) -> u16;
/// Configure the queue size for the virtio queue.
fn set_size(&mut self, size: u16);
/// Check whether the queue is ready to be processed.
fn ready(&self) -> bool;
/// Configure the queue to `ready for processing` state.
fn set_ready(&mut self, ready: bool);
/// Set the descriptor table address for the queue.
///
/// The descriptor table address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>);
/// Set the available ring address for the queue.
///
/// The available ring address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>);
/// Set the used ring address for the queue.
///
/// The used ring address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>);
/// Enable/disable the VIRTIO_F_RING_EVENT_IDX feature for interrupt coalescing.
fn set_event_idx(&mut self, enabled: bool);
/// Read the `idx` field from the available ring.
fn avail_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>;
/// Read the `idx` field from the used ring.
fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>;
/// Put a used descriptor head into the used ring.
fn add_used<M: GuestMemory>(&mut self, mem: &M, head_index: u16, len: u32)
-> Result<(), Error>;
/// Enable notification events from the guest driver.
///
/// Return true if one or more descriptors can be consumed from the available ring after
/// notifications were enabled (and thus it's possible there will be no corresponding
/// notification).
fn enable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error>;
/// Disable notification events from the guest driver.
fn disable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<(), Error>;
/// Check whether a notification to the guest is needed.
///
/// Please note this method has side effects: once it returns `true`, it considers the
/// driver will actually be notified, remember the associated index in the used ring, and
/// won't return `true` again until the driver updates `used_event` and/or the notification
/// conditions hold once more.
fn needs_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error>;
/// Return the index of the next entry in the available ring.
fn next_avail(&self) -> u16;
/// Return the index for the next descriptor in the used ring.
fn next_used(&self) -> u16;
/// Set the index of the next entry in the available ring.
fn set_next_avail(&mut self, next_avail: u16);
/// Set the index for the next descriptor in the used ring.
fn set_next_used(&mut self, next_used: u16);
}

View File

@ -1,370 +0,0 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//! Utilities used by unit tests and benchmarks for mocking the driver side
//! of the virtio protocol.
use std::marker::PhantomData;
use std::mem::size_of;
use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestUsize,
};
use crate::defs::{VIRTQ_DESC_F_INDIRECT, VIRTQ_DESC_F_NEXT};
use crate::{Descriptor, Queue, QueueState, VirtqUsedElem};
/// Wrapper struct used for accessing a particular address of a GuestMemory area.
pub struct Ref<'a, M, T> {
mem: &'a M,
addr: GuestAddress,
phantom: PhantomData<*const T>,
}
impl<'a, M: GuestMemory, T: ByteValued> Ref<'a, M, T> {
fn new(mem: &'a M, addr: GuestAddress) -> Self {
Ref {
mem,
addr,
phantom: PhantomData,
}
}
/// Read an object of type T from the underlying memory found at self.addr.
pub fn load(&self) -> T {
self.mem.read_obj(self.addr).unwrap()
}
/// Write an object of type T from the underlying memory found at self.addr.
pub fn store(&self, val: T) {
self.mem.write_obj(val, self.addr).unwrap()
}
}
/// Wrapper struct used for accessing a subregion of a GuestMemory area.
pub struct ArrayRef<'a, M, T> {
mem: &'a M,
addr: GuestAddress,
len: usize,
phantom: PhantomData<*const T>,
}
impl<'a, M: GuestMemory, T: ByteValued> ArrayRef<'a, M, T> {
fn new(mem: &'a M, addr: GuestAddress, len: usize) -> Self {
ArrayRef {
mem,
addr,
len,
phantom: PhantomData,
}
}
/// Return a `Ref` object pointing to an address defined by a particular
/// index offset in the region.
pub fn ref_at(&self, index: usize) -> Ref<'a, M, T> {
// TODO: add better error handling to the mock logic.
assert!(index < self.len);
let addr = self
.addr
.checked_add((index * size_of::<T>()) as u64)
.unwrap();
Ref::new(self.mem, addr)
}
}
/// Represents a virtio queue ring. The only difference between the used and available rings,
/// is the ring element type.
pub struct SplitQueueRing<'a, M, T: ByteValued> {
flags: Ref<'a, M, u16>,
// The value stored here should more precisely be a `Wrapping<u16>`, but that would require a
// `ByteValued` impl for this type, which is not provided in vm-memory. Implementing the trait
// here would require defining a wrapper for `Wrapping<u16>` and that would be too much for a
// mock framework that is only used in tests.
idx: Ref<'a, M, u16>,
ring: ArrayRef<'a, M, T>,
// `used_event` for `AvailRing`, `avail_event` for `UsedRing`.
event: Ref<'a, M, u16>,
}
impl<'a, M: GuestMemory, T: ByteValued> SplitQueueRing<'a, M, T> {
/// Create a new `SplitQueueRing` instance
pub fn new(mem: &'a M, base: GuestAddress, len: u16) -> Self {
let event_addr = base
.checked_add(4)
.and_then(|a| a.checked_add((size_of::<u16>() * len as usize) as u64))
.unwrap();
let split_queue_ring = SplitQueueRing {
flags: Ref::new(mem, base),
idx: Ref::new(mem, base.checked_add(2).unwrap()),
ring: ArrayRef::new(mem, base.checked_add(4).unwrap(), len as usize),
event: Ref::new(mem, event_addr),
};
split_queue_ring.flags.store(0);
split_queue_ring.idx.store(0);
split_queue_ring.event.store(0);
split_queue_ring
}
/// Return the starting address of the `SplitQueueRing`.
pub fn start(&self) -> GuestAddress {
self.ring.addr
}
/// Return the end address of the `SplitQueueRing`.
pub fn end(&self) -> GuestAddress {
self.start()
.checked_add(self.ring.len as GuestUsize)
.unwrap()
}
/// Return a reference to the idx field.
pub fn idx(&self) -> &Ref<'a, M, u16> {
&self.idx
}
/// Return a reference to the ring field.
pub fn ring(&self) -> &ArrayRef<'a, M, T> {
&self.ring
}
}
/// The available ring is used by the driver to offer buffers to the device.
pub type AvailRing<'a, M> = SplitQueueRing<'a, M, u16>;
/// The used ring is where the device returns buffers once it is done with them.
pub type UsedRing<'a, M> = SplitQueueRing<'a, M, VirtqUsedElem>;
/// Refers to the buffers the driver is using for the device.
pub struct DescriptorTable<'a, M> {
table: ArrayRef<'a, M, Descriptor>,
len: u16,
free_descriptors: Vec<u16>,
}
impl<'a, M: GuestMemory> DescriptorTable<'a, M> {
/// Create a new `DescriptorTable` instance
pub fn new(mem: &'a M, addr: GuestAddress, len: u16) -> Self {
let table = ArrayRef::new(mem, addr, len as usize);
let free_descriptors = (0..len).rev().collect();
DescriptorTable {
table,
len,
free_descriptors,
}
}
/// Read one descriptor from the specified index.
pub fn load(&self, index: u16) -> Descriptor {
self.table.ref_at(index as usize).load()
}
/// Write one descriptor at the specified index.
pub fn store(&self, index: u16, value: Descriptor) {
self.table.ref_at(index as usize).store(value)
}
/// Return the total size of the DescriptorTable in bytes.
pub fn total_size(&self) -> u64 {
(self.len as usize * size_of::<Descriptor>()) as u64
}
/// Create a chain of descriptors.
pub fn build_chain(&mut self, len: u16) -> u16 {
let indices = self
.free_descriptors
.iter()
.copied()
.rev()
.take(usize::from(len))
.collect::<Vec<_>>();
assert_eq!(indices.len(), len as usize);
for (pos, index_value) in indices.iter().copied().enumerate() {
// Addresses and lens constant for now.
let mut desc = Descriptor::new(0x1000, 0x1000, 0, 0);
// It's not the last descriptor in the chain.
if pos < indices.len() - 1 {
desc.set_flags(VIRTQ_DESC_F_NEXT);
desc.set_next(indices[pos + 1]);
} else {
desc.set_flags(0);
}
self.store(index_value, desc);
}
indices[0]
}
}
trait GuestAddressExt {
fn align_up(&self, x: GuestUsize) -> GuestAddress;
}
impl GuestAddressExt for GuestAddress {
fn align_up(&self, x: GuestUsize) -> GuestAddress {
Self((self.0 + (x - 1)) & !(x - 1))
}
}
/// A mock version of the virtio queue implemented from the perspective of the driver.
pub struct MockSplitQueue<'a, M> {
mem: &'a M,
len: u16,
desc_table_addr: GuestAddress,
desc_table: DescriptorTable<'a, M>,
avail_addr: GuestAddress,
avail: AvailRing<'a, M>,
used_addr: GuestAddress,
used: UsedRing<'a, M>,
indirect_addr: GuestAddress,
}
impl<'a, M: GuestMemory> MockSplitQueue<'a, M> {
/// Create a new `MockSplitQueue` instance with 0 as the default guest
/// physical starting address.
pub fn new(mem: &'a M, len: u16) -> Self {
Self::create(mem, GuestAddress(0), len)
}
/// Create a new `MockSplitQueue` instance.
pub fn create(mem: &'a M, start: GuestAddress, len: u16) -> Self {
const AVAIL_ALIGN: GuestUsize = 2;
const USED_ALIGN: GuestUsize = 4;
let desc_table_addr = start;
let desc_table = DescriptorTable::new(mem, desc_table_addr, len);
let avail_addr = start
.checked_add(16 * len as GuestUsize)
.unwrap()
.align_up(AVAIL_ALIGN);
let avail = AvailRing::new(mem, avail_addr, len);
let used_addr = avail.end().align_up(USED_ALIGN);
let used = UsedRing::new(mem, used_addr, len);
let indirect_addr = GuestAddress(0x3000_0000);
MockSplitQueue {
mem,
len,
desc_table_addr,
desc_table,
avail_addr,
avail,
used_addr,
used,
indirect_addr,
}
}
/// Return the starting address of the queue.
pub fn start(&self) -> GuestAddress {
self.desc_table_addr
}
/// Return the end address of the queue.
pub fn end(&self) -> GuestAddress {
self.used.end()
}
/// Descriptor table accessor.
pub fn desc_table(&self) -> &DescriptorTable<'a, M> {
&self.desc_table
}
/// Available ring accessor.
pub fn avail(&self) -> &AvailRing<M> {
&self.avail
}
/// Used ring accessor.
pub fn used(&self) -> &UsedRing<M> {
&self.used
}
/// Return the starting address of the descriptor table.
pub fn desc_table_addr(&self) -> GuestAddress {
self.desc_table_addr
}
/// Return the starting address of the available ring.
pub fn avail_addr(&self) -> GuestAddress {
self.avail_addr
}
/// Return the starting address of the used ring.
pub fn used_addr(&self) -> GuestAddress {
self.used_addr
}
fn update_avail_idx(&mut self, value: u16) {
let avail_idx = self.avail.idx.load();
self.avail.ring.ref_at(avail_idx as usize).store(value);
self.avail.idx.store(avail_idx.wrapping_add(1));
}
fn alloc_indirect_chain(&mut self, len: u16) -> GuestAddress {
// To simplify things for now, we round up the table len as a multiple of 16. When this is
// no longer the case, we should make sure the starting address of the descriptor table
// we're creating below is properly aligned.
let table_len = if len % 16 == 0 {
len
} else {
16 * (len / 16 + 1)
};
let mut table = DescriptorTable::new(self.mem, self.indirect_addr, table_len);
let head_decriptor_index = table.build_chain(len);
// When building indirect descriptor tables, the descriptor at index 0 is supposed to be
// first in the resulting chain. Just making sure our logic actually makes that happen.
assert_eq!(head_decriptor_index, 0);
let table_addr = self.indirect_addr;
self.indirect_addr = self.indirect_addr.checked_add(table.total_size()).unwrap();
table_addr
}
/// Add a descriptor chain to the table.
pub fn add_chain(&mut self, len: u16) {
let head_idx = self.desc_table.build_chain(len);
self.update_avail_idx(head_idx);
}
/// Add an indirect descriptor chain to the table.
pub fn add_indirect_chain(&mut self, len: u16) {
let head_idx = self.desc_table.build_chain(1);
// We just allocate the indirect table and forget about it for now.
let indirect_addr = self.alloc_indirect_chain(len);
let mut desc = self.desc_table.load(head_idx);
desc.set_flags(VIRTQ_DESC_F_INDIRECT);
desc.set_addr(indirect_addr.raw_value());
desc.set_len(u32::from(len) * size_of::<Descriptor>() as u32);
self.desc_table.store(head_idx, desc);
self.update_avail_idx(head_idx);
}
/// Creates a new `Queue`, using the underlying memory regions represented
/// by the `MockSplitQueue`.
pub fn create_queue<A: GuestAddressSpace>(&self, a: A) -> Queue<A, QueueState> {
let mut q = Queue::<A, QueueState>::new(a, self.len);
q.state.size = self.len;
q.state.ready = true;
q.state.desc_table = self.desc_table_addr;
q.state.avail_ring = self.avail_addr;
q.state.used_ring = self.used_addr;
q
}
}

View File

@ -1,677 +0,0 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright © 2019 Intel Corporation
//
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::num::Wrapping;
use std::ops::Deref;
use std::sync::atomic::Ordering;
use vm_memory::GuestAddressSpace;
use crate::{AvailIter, Error, QueueGuard, QueueState, QueueStateGuard, QueueStateT};
/// A convenient wrapper struct for a virtio queue, with associated `GuestMemory` object.
///
/// # Example
///
/// ```rust
/// use virtio_queue::{Queue, QueueState};
/// use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
///
/// let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
/// let mut queue = Queue::<&GuestMemoryMmap, QueueState>::new(&m, 1024);
///
/// // First, the driver sets up the queue; this set up is done via writes on the bus (PCI, MMIO).
/// queue.set_size(8);
/// queue.set_desc_table_address(Some(0x1000), None);
/// queue.set_avail_ring_address(Some(0x2000), None);
/// queue.set_used_ring_address(Some(0x3000), None);
/// queue.set_event_idx(true);
/// queue.set_ready(true);
/// // The user should check if the queue is valid before starting to use it.
/// assert!(queue.is_valid());
///
/// // Here the driver would add entries in the available ring and then update the `idx` field of
/// // the available ring (address = 0x2000 + 2).
/// m.write_obj(3, GuestAddress(0x2002));
///
/// loop {
/// queue.disable_notification().unwrap();
///
/// // Consume entries from the available ring.
/// while let Some(chain) = queue.iter().unwrap().next() {
/// // Process the descriptor chain, and then add an entry in the used ring and optionally
/// // notify the driver.
/// queue.add_used(chain.head_index(), 0x100).unwrap();
///
/// if queue.needs_notification().unwrap() {
/// // Here we would notify the driver it has new entries in the used ring to consume.
/// }
/// }
/// if !queue.enable_notification().unwrap() {
/// break;
/// }
/// }
///
/// // We can reset the queue at some point.
/// queue.reset();
/// // The queue should not be ready after reset.
/// assert!(!queue.ready());
/// ```
#[derive(Clone, Debug)]
pub struct Queue<M: GuestAddressSpace, S: QueueStateT = QueueState> {
/// Guest memory object associated with the queue.
pub mem: M,
/// Virtio queue state.
pub state: S,
}
impl<M: GuestAddressSpace, S: QueueStateT> Queue<M, S> {
/// Construct an empty virtio queue with the given `max_size`.
///
/// # Arguments
/// * `mem` - the guest memory object that can be used to access the queue buffers.
/// * `max_size` - the maximum size (and the default one) of the queue.
pub fn new(mem: M, max_size: u16) -> Self {
Queue {
mem,
state: S::new(max_size),
}
}
/// Check whether the queue configuration is valid.
pub fn is_valid(&self) -> bool {
self.state.is_valid(self.mem.memory().deref())
}
/// Reset the queue to the initial state.
pub fn reset(&mut self) {
self.state.reset()
}
/// Get an exclusive reference to the underlying `QueueState` object.
///
/// Logically this method will acquire the underlying lock protecting the `QueueState` Object.
/// The lock will be released when the returned object gets dropped.
pub fn lock(&mut self) -> <S as QueueStateGuard>::G {
self.state.lock()
}
/// Get an exclusive reference to the underlying `QueueState` object with an associated
/// `GuestMemory` object.
///
/// Logically this method will acquire the underlying lock protecting the `QueueState` Object.
/// The lock will be released when the returned object gets dropped.
pub fn lock_with_memory(
&mut self,
) -> QueueGuard<<M as GuestAddressSpace>::T, <S as QueueStateGuard>::G> {
QueueGuard::new(self.state.lock(), self.mem.memory())
}
/// Get the maximum size of the virtio queue.
pub fn max_size(&self) -> u16 {
self.state.max_size()
}
/// Configure the queue size for the virtio queue.
///
/// # Arguments
/// * `size` - the queue size; it should be a power of two, different than 0 and less than or
/// equal to the value reported by `max_size()`, otherwise the queue size remains the
/// default one (which is the maximum one).
pub fn set_size(&mut self, size: u16) {
self.state.set_size(size);
}
/// Check whether the queue is ready to be processed.
pub fn ready(&self) -> bool {
self.state.ready()
}
/// Configure the queue to the `ready for processing` state.
///
/// # Arguments
/// * `ready` - a boolean to indicate whether the queue is ready to be used or not.
pub fn set_ready(&mut self, ready: bool) {
self.state.set_ready(ready)
}
/// Set the descriptor table address for the queue.
///
/// The descriptor table address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
///
/// # Arguments
/// * `low` - an optional value for the lowest 32 bits of the address.
/// * `high` - an optional value for the highest 32 bits of the address.
pub fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.state.set_desc_table_address(low, high);
}
/// Set the available ring address for the queue.
///
/// The available ring address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
///
/// # Arguments
/// * `low` - an optional value for the lowest 32 bits of the address.
/// * `high` - an optional value for the highest 32 bits of the address.
pub fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.state.set_avail_ring_address(low, high);
}
/// Set the used ring address for the queue.
///
/// The used ring address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
///
/// # Arguments
/// * `low` - an optional value for the lowest 32 bits of the address.
/// * `high` - an optional value for the highest 32 bits of the address.
pub fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.state.set_used_ring_address(low, high);
}
/// Enable/disable the VIRTIO_F_RING_EVENT_IDX feature for interrupt coalescing.
///
/// # Arguments
/// * `enabled` - a boolean to indicate whether the VIRTIO_F_RING_EVENT_IDX feature was
/// successfully negotiated or not.
pub fn set_event_idx(&mut self, enabled: bool) {
self.state.set_event_idx(enabled)
}
/// Read the `idx` field from the available ring.
///
/// # Arguments
/// * `order` - the memory ordering used to access the `idx` field from memory.
pub fn avail_idx(&self, order: Ordering) -> Result<Wrapping<u16>, Error> {
self.state.avail_idx(self.mem.memory().deref(), order)
}
/// Reads the `idx` field from the used ring.
///
/// # Arguments
/// * `order` - the memory ordering used to access the `idx` field from memory.
pub fn used_idx(&self, order: Ordering) -> Result<Wrapping<u16>, Error> {
self.state.used_idx(self.mem.memory().deref(), order)
}
/// Put a used descriptor head into the used ring.
///
/// # Arguments
/// * `head_index` - the index of the used descriptor chain.
/// * `len` - the total length of the descriptor chain which was used (written to).
pub fn add_used(&mut self, head_index: u16, len: u32) -> Result<(), Error> {
self.state
.add_used(self.mem.memory().deref(), head_index, len)
}
/// Enable notification events from the guest driver.
///
/// Return true if one or more descriptors can be consumed from the available ring after
/// notifications were enabled (and thus it's possible there will be no corresponding
/// notification).
pub fn enable_notification(&mut self) -> Result<bool, Error> {
self.state.enable_notification(self.mem.memory().deref())
}
/// Disable notification events from the guest driver.
pub fn disable_notification(&mut self) -> Result<(), Error> {
self.state.disable_notification(self.mem.memory().deref())
}
/// Check whether a notification to the guest is needed.
///
/// Please note this method has side effects: once it returns `true`, it considers the
/// driver will actually be notified, remember the associated index in the used ring, and
/// won't return `true` again until the driver updates `used_event` and/or the notification
/// conditions hold once more.
pub fn needs_notification(&mut self) -> Result<bool, Error> {
self.state.needs_notification(self.mem.memory().deref())
}
/// Return the index of the next entry in the available ring.
pub fn next_avail(&self) -> u16 {
self.state.next_avail()
}
/// Returns the index for the next descriptor in the used ring.
pub fn next_used(&self) -> u16 {
self.state.next_used()
}
/// Set the index of the next entry in the available ring.
///
/// # Arguments
/// * `next_avail` - the index of the next available ring entry.
pub fn set_next_avail(&mut self, next_avail: u16) {
self.state.set_next_avail(next_avail);
}
/// Sets the index for the next descriptor in the used ring.
///
/// # Arguments
/// * `next_used` - the index of the next used ring entry.
pub fn set_next_used(&mut self, next_used: u16) {
self.state.set_next_used(next_used);
}
}
impl<M: GuestAddressSpace> Queue<M, QueueState> {
/// A consuming iterator over all available descriptor chain heads offered by the driver.
pub fn iter(&mut self) -> Result<AvailIter<'_, M::T>, Error> {
self.state.iter(self.mem.memory())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::defs::{
DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR,
VIRTQ_DESC_F_NEXT, VIRTQ_USED_F_NO_NOTIFY,
};
use crate::mock::MockSplitQueue;
use crate::Descriptor;
use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap};
#[test]
fn test_queue_is_valid() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
// q is currently valid
assert!(q.is_valid());
// shouldn't be valid when not marked as ready
q.set_ready(false);
assert!(!q.ready());
assert!(!q.is_valid());
q.set_ready(true);
// shouldn't be allowed to set a size > max_size
q.set_size(q.max_size() << 1);
assert_eq!(q.state.size, q.max_size());
// or set the size to 0
q.set_size(0);
assert_eq!(q.state.size, q.max_size());
// or set a size which is not a power of 2
q.set_size(11);
assert_eq!(q.state.size, q.max_size());
// but should be allowed to set a size if 0 < size <= max_size and size is a power of two
q.set_size(4);
assert_eq!(q.state.size, 4);
q.state.size = q.max_size();
// shouldn't be allowed to set an address that breaks the alignment constraint
q.set_desc_table_address(Some(0xf), None);
assert_eq!(q.state.desc_table.0, vq.desc_table_addr().0);
// should be allowed to set an aligned out of bounds address
q.set_desc_table_address(Some(0xffff_fff0), None);
assert_eq!(q.state.desc_table.0, 0xffff_fff0);
// but shouldn't be valid
assert!(!q.is_valid());
// but should be allowed to set a valid description table address
q.set_desc_table_address(Some(0x10), None);
assert_eq!(q.state.desc_table.0, 0x10);
assert!(q.is_valid());
let addr = vq.desc_table_addr().0;
q.set_desc_table_address(Some(addr as u32), Some((addr >> 32) as u32));
// shouldn't be allowed to set an address that breaks the alignment constraint
q.set_avail_ring_address(Some(0x1), None);
assert_eq!(q.state.avail_ring.0, vq.avail_addr().0);
// should be allowed to set an aligned out of bounds address
q.set_avail_ring_address(Some(0xffff_fffe), None);
assert_eq!(q.state.avail_ring.0, 0xffff_fffe);
// but shouldn't be valid
assert!(!q.is_valid());
// but should be allowed to set a valid available ring address
q.set_avail_ring_address(Some(0x2), None);
assert_eq!(q.state.avail_ring.0, 0x2);
assert!(q.is_valid());
let addr = vq.avail_addr().0;
q.set_avail_ring_address(Some(addr as u32), Some((addr >> 32) as u32));
// shouldn't be allowed to set an address that breaks the alignment constraint
q.set_used_ring_address(Some(0x3), None);
assert_eq!(q.state.used_ring.0, vq.used_addr().0);
// should be allowed to set an aligned out of bounds address
q.set_used_ring_address(Some(0xffff_fffc), None);
assert_eq!(q.state.used_ring.0, 0xffff_fffc);
// but shouldn't be valid
assert!(!q.is_valid());
// but should be allowed to set a valid used ring address
q.set_used_ring_address(Some(0x4), None);
assert_eq!(q.state.used_ring.0, 0x4);
let addr = vq.used_addr().0;
q.set_used_ring_address(Some(addr as u32), Some((addr >> 32) as u32));
assert!(q.is_valid());
}
#[test]
fn test_add_used() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
assert_eq!(u16::from_le(vq.used().idx().load()), 0);
// index too large
assert!(q.add_used(16, 0x1000).is_err());
assert_eq!(u16::from_le(vq.used().idx().load()), 0);
// should be ok
q.add_used(1, 0x1000).unwrap();
assert_eq!(q.state.next_used, Wrapping(1));
assert_eq!(u16::from_le(vq.used().idx().load()), 1);
let x = vq.used().ring().ref_at(0).load();
assert_eq!(x.id(), 1);
assert_eq!(x.len(), 0x1000);
}
#[test]
fn test_reset_queue() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
q.set_size(8);
// The address set by `MockSplitQueue` for the descriptor table is DEFAULT_DESC_TABLE_ADDR,
// so let's change it for testing the reset.
q.set_desc_table_address(Some(0x5000), None);
// Same for `event_idx_enabled`, `next_avail` `next_used` and `signalled_used`.
q.set_event_idx(true);
q.set_next_avail(2);
q.add_used(1, 200).unwrap();
q.state.signalled_used = Some(Wrapping(15));
assert_eq!(q.state.size, 8);
// `create_queue` also marks the queue as ready.
assert!(q.state.ready);
assert_ne!(q.state.desc_table, GuestAddress(DEFAULT_DESC_TABLE_ADDR));
assert_ne!(q.state.avail_ring, GuestAddress(DEFAULT_AVAIL_RING_ADDR));
assert_ne!(q.state.used_ring, GuestAddress(DEFAULT_USED_RING_ADDR));
assert_ne!(q.state.next_avail, Wrapping(0));
assert_ne!(q.state.next_used, Wrapping(0));
assert_ne!(q.state.signalled_used, None);
assert!(q.state.event_idx_enabled);
q.reset();
assert_eq!(q.state.size, 16);
assert!(!q.state.ready);
assert_eq!(q.state.desc_table, GuestAddress(DEFAULT_DESC_TABLE_ADDR));
assert_eq!(q.state.avail_ring, GuestAddress(DEFAULT_AVAIL_RING_ADDR));
assert_eq!(q.state.used_ring, GuestAddress(DEFAULT_USED_RING_ADDR));
assert_eq!(q.state.next_avail, Wrapping(0));
assert_eq!(q.state.next_used, Wrapping(0));
assert_eq!(q.state.signalled_used, None);
assert!(!q.state.event_idx_enabled);
}
#[test]
fn test_needs_notification() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let qsize = 16;
let vq = MockSplitQueue::new(m, qsize);
let mut q = vq.create_queue(m);
let avail_addr = vq.avail_addr();
// It should always return true when EVENT_IDX isn't enabled.
for i in 0..qsize {
q.state.next_used = Wrapping(i);
assert!(q.needs_notification().unwrap());
}
m.write_obj::<u16>(
u16::to_le(4),
avail_addr.unchecked_add(4 + qsize as u64 * 2),
)
.unwrap();
q.state.set_event_idx(true);
// Incrementing up to this value causes an `u16` to wrap back to 0.
let wrap = u32::from(u16::MAX) + 1;
for i in 0..wrap + 12 {
q.state.next_used = Wrapping(i as u16);
// Let's test wrapping around the maximum index value as well.
let expected = i == 5 || i == (5 + wrap) || q.state.signalled_used.is_none();
assert_eq!(q.needs_notification().unwrap(), expected);
}
m.write_obj::<u16>(8, avail_addr.unchecked_add(4 + qsize as u64 * 2))
.unwrap();
// Returns `false` because `signalled_used` already passed this value.
assert!(!q.needs_notification().unwrap());
m.write_obj::<u16>(15, avail_addr.unchecked_add(4 + qsize as u64 * 2))
.unwrap();
assert!(!q.needs_notification().unwrap());
q.state.next_used = Wrapping(15);
assert!(!q.needs_notification().unwrap());
q.state.next_used = Wrapping(0);
assert!(q.needs_notification().unwrap());
assert!(!q.needs_notification().unwrap());
m.write_obj::<u16>(u16::MAX - 3, avail_addr.unchecked_add(4 + qsize as u64 * 2))
.unwrap();
q.state.next_used = Wrapping(u16::MAX - 2);
// Returns `true` because the value we wrote in the `used_event` < the next used value and
// the last `signalled_used` is 0.
assert!(q.needs_notification().unwrap());
}
#[test]
fn test_enable_disable_notification() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
let used_addr = vq.used_addr();
assert!(!q.state.event_idx_enabled);
q.enable_notification().unwrap();
let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
assert_eq!(v, 0);
q.disable_notification().unwrap();
let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
assert_eq!(v, VIRTQ_USED_F_NO_NOTIFY);
q.enable_notification().unwrap();
let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
assert_eq!(v, 0);
q.set_event_idx(true);
let avail_addr = vq.avail_addr();
m.write_obj::<u16>(u16::to_le(2), avail_addr.unchecked_add(2))
.unwrap();
assert!(q.enable_notification().unwrap());
q.state.next_avail = Wrapping(2);
assert!(!q.enable_notification().unwrap());
m.write_obj::<u16>(u16::to_le(8), avail_addr.unchecked_add(2))
.unwrap();
assert!(q.enable_notification().unwrap());
q.state.next_avail = Wrapping(8);
assert!(!q.enable_notification().unwrap());
}
#[test]
fn test_consume_chains_with_notif() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
// q is currently valid.
assert!(q.is_valid());
// The chains are (0, 1), (2, 3, 4), (5, 6), (7, 8), (9, 10, 11, 12).
for i in 0..13 {
let flags = match i {
1 | 4 | 6 | 8 | 12 => 0,
_ => VIRTQ_DESC_F_NEXT,
};
let desc = Descriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags, i + 1);
vq.desc_table().store(i, desc);
}
vq.avail().ring().ref_at(0).store(u16::to_le(0));
vq.avail().ring().ref_at(1).store(u16::to_le(2));
vq.avail().ring().ref_at(2).store(u16::to_le(5));
vq.avail().ring().ref_at(3).store(u16::to_le(7));
vq.avail().ring().ref_at(4).store(u16::to_le(9));
// Let the device know it can consume chains with the index < 2.
vq.avail().idx().store(u16::to_le(2));
// No descriptor chains are consumed at this point.
assert_eq!(q.next_avail(), 0);
let mut i = 0;
loop {
i += 1;
q.disable_notification().unwrap();
while let Some(_chain) = q.iter().unwrap().next() {
// Here the device would consume entries from the available ring, add an entry in
// the used ring and optionally notify the driver. For the purpose of this test, we
// don't need to do anything with the chain, only consume it.
}
if !q.enable_notification().unwrap() {
break;
}
}
// The chains should be consumed in a single loop iteration because there's nothing updating
// the `idx` field of the available ring in the meantime.
assert_eq!(i, 1);
// The next chain that can be consumed should have index 2.
assert_eq!(q.next_avail(), 2);
// Let the device know it can consume one more chain.
vq.avail().idx().store(u16::to_le(3));
i = 0;
loop {
i += 1;
q.disable_notification().unwrap();
while let Some(_chain) = q.iter().unwrap().next() {
// In a real use case, we would do something with the chain here.
}
// For the simplicity of the test we are updating here the `idx` value of the available
// ring. Ideally this should be done on a separate thread.
// Because of this update, the loop should be iterated again to consume the new
// available descriptor chains.
vq.avail().idx().store(u16::to_le(4));
if !q.enable_notification().unwrap() {
break;
}
}
assert_eq!(i, 2);
// The next chain that can be consumed should have index 4.
assert_eq!(q.next_avail(), 4);
// Set an `idx` that is bigger than the number of entries added in the ring.
// This is an allowed scenario, but the indexes of the chain will have unexpected values.
vq.avail().idx().store(u16::to_le(7));
loop {
q.disable_notification().unwrap();
while let Some(_chain) = q.iter().unwrap().next() {
// In a real use case, we would do something with the chain here.
}
if !q.enable_notification().unwrap() {
break;
}
}
assert_eq!(q.next_avail(), 7);
}
#[test]
fn test_invalid_avail_idx() {
// This is a negative test for the following MUST from the spec: `A driver MUST NOT
// decrement the available idx on a virtqueue (ie. there is no way to “unexpose” buffers).`.
// We validate that for this misconfiguration, the device does not panic.
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 16);
let mut q = vq.create_queue(m);
// q is currently valid.
assert!(q.is_valid());
// The chains are (0, 1), (2, 3, 4), (5, 6).
for i in 0..7 {
let flags = match i {
1 | 4 | 6 => 0,
_ => VIRTQ_DESC_F_NEXT,
};
let desc = Descriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags, i + 1);
vq.desc_table().store(i, desc);
}
vq.avail().ring().ref_at(0).store(u16::to_le(0));
vq.avail().ring().ref_at(1).store(u16::to_le(2));
vq.avail().ring().ref_at(2).store(u16::to_le(5));
// Let the device know it can consume chains with the index < 2.
vq.avail().idx().store(u16::to_le(3));
// No descriptor chains are consumed at this point.
assert_eq!(q.next_avail(), 0);
loop {
q.disable_notification().unwrap();
while let Some(_chain) = q.iter().unwrap().next() {
// Here the device would consume entries from the available ring, add an entry in
// the used ring and optionally notify the driver. For the purpose of this test, we
// don't need to do anything with the chain, only consume it.
}
if !q.enable_notification().unwrap() {
break;
}
}
// The next chain that can be consumed should have index 3.
assert_eq!(q.next_avail(), 3);
assert_eq!(q.avail_idx(Ordering::Acquire).unwrap(), Wrapping(3));
assert!(q.lock().ready());
// Decrement `idx` which should be forbidden. We don't enforce this thing, but we should
// test that we don't panic in case the driver decrements it.
vq.avail().idx().store(u16::to_le(1));
loop {
q.disable_notification().unwrap();
while let Some(_chain) = q.iter().unwrap().next() {
// In a real use case, we would do something with the chain here.
}
if !q.enable_notification().unwrap() {
break;
}
}
}
}

View File

@ -1,260 +0,0 @@
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::num::Wrapping;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::Ordering;
use vm_memory::GuestMemory;
use crate::{AvailIter, Error, QueueState, QueueStateT};
/// A guard object to exclusively access an `Queue` object.
///
/// The guard object holds an exclusive lock to the underlying `QueueState` object, with an
/// associated guest memory object. It helps to guarantee that the whole session is served
/// with the same guest memory object.
///
/// # Example
///
/// ```rust
/// use virtio_queue::{Queue, QueueState};
/// use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
///
/// let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
/// let mut queue = Queue::<&GuestMemoryMmap, QueueState>::new(&m, 1024);
/// let mut queue_guard = queue.lock_with_memory();
///
/// // First, the driver sets up the queue; this set up is done via writes on the bus (PCI, MMIO).
/// queue_guard.set_size(8);
/// queue_guard.set_desc_table_address(Some(0x1000), None);
/// queue_guard.set_avail_ring_address(Some(0x2000), None);
/// queue_guard.set_used_ring_address(Some(0x3000), None);
/// queue_guard.set_event_idx(true);
/// queue_guard.set_ready(true);
/// // The user should check if the queue is valid before starting to use it.
/// assert!(queue_guard.is_valid());
///
/// // Here the driver would add entries in the available ring and then update the `idx` field of
/// // the available ring (address = 0x2000 + 2).
/// m.write_obj(3, GuestAddress(0x2002));
///
/// loop {
/// queue_guard.disable_notification().unwrap();
///
/// // Consume entries from the available ring.
/// while let Some(chain) = queue_guard.iter().unwrap().next() {
/// // Process the descriptor chain, and then add an entry in the used ring and optionally
/// // notify the driver.
/// queue_guard.add_used(chain.head_index(), 0x100).unwrap();
///
/// if queue_guard.needs_notification().unwrap() {
/// // Here we would notify the driver it has new entries in the used ring to consume.
/// }
/// }
/// if !queue_guard.enable_notification().unwrap() {
/// break;
/// }
/// }
/// ```
pub struct QueueGuard<M, S> {
state: S,
mem: M,
}
impl<M, S> QueueGuard<M, S>
where
M: Deref + Clone,
M::Target: GuestMemory + Sized,
S: DerefMut<Target = QueueState>,
{
/// Create a new instance of `QueueGuard`.
pub fn new(state: S, mem: M) -> Self {
QueueGuard { state, mem }
}
/// Check whether the queue configuration is valid.
pub fn is_valid(&self) -> bool {
self.state.is_valid(self.mem.deref())
}
/// Reset the queue to the initial state.
pub fn reset(&mut self) {
self.state.reset()
}
/// Get the maximum size of the virtio queue.
pub fn max_size(&self) -> u16 {
self.state.max_size()
}
/// Configure the queue size for the virtio queue.
pub fn set_size(&mut self, size: u16) {
self.state.set_size(size);
}
/// Check whether the queue is ready to be processed.
pub fn ready(&self) -> bool {
self.state.ready()
}
/// Configure the queue to `ready for processing` state.
pub fn set_ready(&mut self, ready: bool) {
self.state.set_ready(ready)
}
/// Set the descriptor table address for the queue.
///
/// The descriptor table address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
pub fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.state.set_desc_table_address(low, high);
}
/// Set the available ring address for the queue.
///
/// The available ring address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
pub fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.state.set_avail_ring_address(low, high);
}
/// Set the used ring address for the queue.
///
/// The used ring address is 64-bit, the corresponding part will be updated if 'low'
/// and/or `high` is `Some` and valid.
pub fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.state.set_used_ring_address(low, high);
}
/// Enable/disable the VIRTIO_F_RING_EVENT_IDX feature for interrupt coalescing.
pub fn set_event_idx(&mut self, enabled: bool) {
self.state.set_event_idx(enabled)
}
/// Read the `idx` field from the available ring.
pub fn avail_idx(&self, order: Ordering) -> Result<Wrapping<u16>, Error> {
self.state.avail_idx(self.mem.deref(), order)
}
/// Put a used descriptor head into the used ring.
pub fn add_used(&mut self, head_index: u16, len: u32) -> Result<(), Error> {
self.state.add_used(self.mem.deref(), head_index, len)
}
/// Enable notification events from the guest driver.
///
/// Return true if one or more descriptors can be consumed from the available ring after
/// notifications were enabled (and thus it's possible there will be no corresponding
/// notification).
pub fn enable_notification(&mut self) -> Result<bool, Error> {
self.state.enable_notification(self.mem.deref())
}
/// Disable notification events from the guest driver.
pub fn disable_notification(&mut self) -> Result<(), Error> {
self.state.disable_notification(self.mem.deref())
}
/// Check whether a notification to the guest is needed.
///
/// Please note this method has side effects: once it returns `true`, it considers the
/// driver will actually be notified, remember the associated index in the used ring, and
/// won't return `true` again until the driver updates `used_event` and/or the notification
/// conditions hold once more.
pub fn needs_notification(&mut self) -> Result<bool, Error> {
self.state.needs_notification(self.mem.deref())
}
/// Return the index of the next entry in the available ring.
pub fn next_avail(&self) -> u16 {
self.state.next_avail()
}
/// Set the index of the next entry in the available ring.
pub fn set_next_avail(&mut self, next_avail: u16) {
self.state.set_next_avail(next_avail);
}
/// Get a consuming iterator over all available descriptor chain heads offered by the driver.
pub fn iter(&mut self) -> Result<AvailIter<'_, M>, Error> {
self.state.deref_mut().iter(self.mem.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::defs::VIRTQ_DESC_F_NEXT;
use crate::mock::MockSplitQueue;
use crate::Descriptor;
use vm_memory::{GuestAddress, GuestMemoryMmap};
#[test]
fn test_queue_guard_object() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let vq = MockSplitQueue::new(m, 0x100);
let mut q = vq.create_queue(m);
let mut g = q.lock_with_memory();
// g is currently valid.
assert!(g.is_valid());
assert!(g.ready());
assert_eq!(g.max_size(), 0x100);
g.set_size(16);
// The chains are (0, 1), (2, 3, 4), (5, 6).
for i in 0..7 {
let flags = match i {
1 | 4 | 6 => 0,
_ => VIRTQ_DESC_F_NEXT,
};
let desc = Descriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags, i + 1);
vq.desc_table().store(i, desc);
}
vq.avail().ring().ref_at(0).store(0);
vq.avail().ring().ref_at(1).store(2);
vq.avail().ring().ref_at(2).store(5);
// Let the device know it can consume chains with the index < 2.
vq.avail().idx().store(3);
// No descriptor chains are consumed at this point.
assert_eq!(g.next_avail(), 0);
loop {
g.disable_notification().unwrap();
while let Some(_chain) = g.iter().unwrap().next() {
// Here the device would consume entries from the available ring, add an entry in
// the used ring and optionally notify the driver. For the purpose of this test, we
// don't need to do anything with the chain, only consume it.
}
if !g.enable_notification().unwrap() {
break;
}
}
// The next chain that can be consumed should have index 3.
assert_eq!(g.next_avail(), 3);
assert_eq!(g.avail_idx(Ordering::Acquire).unwrap(), Wrapping(3));
assert!(g.ready());
// Decrement `idx` which should be forbidden. We don't enforce this thing, but we should
// test that we don't panic in case the driver decrements it.
vq.avail().idx().store(1);
loop {
g.disable_notification().unwrap();
while let Some(_chain) = g.iter().unwrap().next() {
// In a real use case, we would do something with the chain here.
}
if !g.enable_notification().unwrap() {
break;
}
}
}
}

View File

@ -1,437 +0,0 @@
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::mem::size_of;
use std::num::Wrapping;
use std::ops::Deref;
use std::sync::atomic::{fence, Ordering};
use vm_memory::{Address, Bytes, GuestAddress, GuestMemory};
use crate::defs::{
DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR,
VIRTQ_AVAIL_ELEMENT_SIZE, VIRTQ_AVAIL_RING_HEADER_SIZE, VIRTQ_AVAIL_RING_META_SIZE,
VIRTQ_USED_ELEMENT_SIZE, VIRTQ_USED_F_NO_NOTIFY, VIRTQ_USED_RING_HEADER_SIZE,
VIRTQ_USED_RING_META_SIZE,
};
use crate::{error, AvailIter, Descriptor, Error, QueueStateGuard, QueueStateT, VirtqUsedElem};
/// Struct to maintain information and manipulate state of a virtio queue.
#[derive(Clone, Debug)]
pub struct QueueState {
/// The maximum size in elements offered by the device.
pub max_size: u16,
/// Tail position of the available ring.
pub next_avail: Wrapping<u16>,
/// Head position of the used ring.
pub next_used: Wrapping<u16>,
/// VIRTIO_F_RING_EVENT_IDX negotiated.
pub event_idx_enabled: bool,
/// The last used value when using VIRTIO_F_EVENT_IDX.
pub signalled_used: Option<Wrapping<u16>>,
/// The queue size in elements the driver selected.
pub size: u16,
/// Indicates if the queue is finished with configuration.
pub ready: bool,
/// Guest physical address of the descriptor table.
pub desc_table: GuestAddress,
/// Guest physical address of the available ring.
pub avail_ring: GuestAddress,
/// Guest physical address of the used ring.
pub used_ring: GuestAddress,
}
impl QueueState {
/// Get a consuming iterator over all available descriptor chain heads offered by the driver.
///
/// # Arguments
/// * `mem` - the `GuestMemory` object that can be used to access the queue buffers.
pub fn iter<M>(&mut self, mem: M) -> Result<AvailIter<'_, M>, Error>
where
M: Deref,
M::Target: GuestMemory + Sized,
{
self.avail_idx(mem.deref(), Ordering::Acquire)
.map(move |idx| AvailIter::new(mem, idx, self))
}
// Helper method that writes `val` to the `avail_event` field of the used ring, using
// the provided ordering.
fn set_avail_event<M: GuestMemory>(
&self,
mem: &M,
val: u16,
order: Ordering,
) -> Result<(), Error> {
// This can not overflow an u64 since it is working with relatively small numbers compared
// to u64::MAX.
let avail_event_offset =
VIRTQ_USED_RING_HEADER_SIZE + VIRTQ_USED_ELEMENT_SIZE * u64::from(self.size);
let addr = self
.used_ring
.checked_add(avail_event_offset)
.ok_or(Error::AddressOverflow)?;
mem.store(u16::to_le(val), addr, order)
.map_err(Error::GuestMemory)
}
// Set the value of the `flags` field of the used ring, applying the specified ordering.
fn set_used_flags<M: GuestMemory>(
&mut self,
mem: &M,
val: u16,
order: Ordering,
) -> Result<(), Error> {
mem.store(u16::to_le(val), self.used_ring, order)
.map_err(Error::GuestMemory)
}
// Write the appropriate values to enable or disable notifications from the driver.
//
// Every access in this method uses `Relaxed` ordering because a fence is added by the caller
// when appropriate.
fn set_notification<M: GuestMemory>(&mut self, mem: &M, enable: bool) -> Result<(), Error> {
if enable {
if self.event_idx_enabled {
// We call `set_avail_event` using the `next_avail` value, instead of reading
// and using the current `avail_idx` to avoid missing notifications. More
// details in `enable_notification`.
self.set_avail_event(mem, self.next_avail.0, Ordering::Relaxed)
} else {
self.set_used_flags(mem, 0, Ordering::Relaxed)
}
} else if !self.event_idx_enabled {
self.set_used_flags(mem, VIRTQ_USED_F_NO_NOTIFY, Ordering::Relaxed)
} else {
// Notifications are effectively disabled by default after triggering once when
// `VIRTIO_F_EVENT_IDX` is negotiated, so we don't do anything in that case.
Ok(())
}
}
// Return the value present in the used_event field of the avail ring.
//
// If the VIRTIO_F_EVENT_IDX feature bit is not negotiated, the flags field in the available
// ring offers a crude mechanism for the driver to inform the device that it doesnt want
// interrupts when buffers are used. Otherwise virtq_avail.used_event is a more performant
// alternative where the driver specifies how far the device can progress before interrupting.
//
// Neither of these interrupt suppression methods are reliable, as they are not synchronized
// with the device, but they serve as useful optimizations. So we only ensure access to the
// virtq_avail.used_event is atomic, but do not need to synchronize with other memory accesses.
fn used_event<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
// This can not overflow an u64 since it is working with relatively small numbers compared
// to u64::MAX.
let used_event_offset =
VIRTQ_AVAIL_RING_HEADER_SIZE + u64::from(self.size) * VIRTQ_AVAIL_ELEMENT_SIZE;
let used_event_addr = self
.avail_ring
.checked_add(used_event_offset)
.ok_or(Error::AddressOverflow)?;
mem.load(used_event_addr, order)
.map(u16::from_le)
.map(Wrapping)
.map_err(Error::GuestMemory)
}
}
impl<'a> QueueStateGuard<'a> for QueueState {
type G = &'a mut Self;
}
impl QueueStateT for QueueState {
fn new(max_size: u16) -> Self {
QueueState {
max_size,
size: max_size,
ready: false,
desc_table: GuestAddress(DEFAULT_DESC_TABLE_ADDR),
avail_ring: GuestAddress(DEFAULT_AVAIL_RING_ADDR),
used_ring: GuestAddress(DEFAULT_USED_RING_ADDR),
next_avail: Wrapping(0),
next_used: Wrapping(0),
event_idx_enabled: false,
signalled_used: None,
}
}
fn is_valid<M: GuestMemory>(&self, mem: &M) -> bool {
let queue_size = self.size as u64;
let desc_table = self.desc_table;
// The multiplication can not overflow an u64 since we are multiplying an u16 with a
// small number.
let desc_table_size = size_of::<Descriptor>() as u64 * queue_size;
let avail_ring = self.avail_ring;
// The operations below can not overflow an u64 since they're working with relatively small
// numbers compared to u64::MAX.
let avail_ring_size = VIRTQ_AVAIL_RING_META_SIZE + VIRTQ_AVAIL_ELEMENT_SIZE * queue_size;
let used_ring = self.used_ring;
let used_ring_size = VIRTQ_USED_RING_META_SIZE + VIRTQ_USED_ELEMENT_SIZE * queue_size;
if !self.ready {
error!("attempt to use virtio queue that is not marked ready");
false
} else if desc_table
.checked_add(desc_table_size)
.map_or(true, |v| !mem.address_in_range(v))
{
error!(
"virtio queue descriptor table goes out of bounds: start:0x{:08x} size:0x{:08x}",
desc_table.raw_value(),
desc_table_size
);
false
} else if avail_ring
.checked_add(avail_ring_size)
.map_or(true, |v| !mem.address_in_range(v))
{
error!(
"virtio queue available ring goes out of bounds: start:0x{:08x} size:0x{:08x}",
avail_ring.raw_value(),
avail_ring_size
);
false
} else if used_ring
.checked_add(used_ring_size)
.map_or(true, |v| !mem.address_in_range(v))
{
error!(
"virtio queue used ring goes out of bounds: start:0x{:08x} size:0x{:08x}",
used_ring.raw_value(),
used_ring_size
);
false
} else {
true
}
}
fn reset(&mut self) {
self.ready = false;
self.size = self.max_size;
self.desc_table = GuestAddress(DEFAULT_DESC_TABLE_ADDR);
self.avail_ring = GuestAddress(DEFAULT_AVAIL_RING_ADDR);
self.used_ring = GuestAddress(DEFAULT_USED_RING_ADDR);
self.next_avail = Wrapping(0);
self.next_used = Wrapping(0);
self.signalled_used = None;
self.event_idx_enabled = false;
}
fn lock(&mut self) -> <Self as QueueStateGuard>::G {
self
}
fn max_size(&self) -> u16 {
self.max_size
}
fn set_size(&mut self, size: u16) {
if size > self.max_size() || size == 0 || (size & (size - 1)) != 0 {
error!("virtio queue with invalid size: {}", size);
return;
}
self.size = size;
}
fn ready(&self) -> bool {
self.ready
}
fn set_ready(&mut self, ready: bool) {
self.ready = ready;
}
fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) {
let low = low.unwrap_or(self.desc_table.0 as u32) as u64;
let high = high.unwrap_or((self.desc_table.0 >> 32) as u32) as u64;
let desc_table = GuestAddress((high << 32) | low);
if desc_table.mask(0xf) != 0 {
error!("virtio queue descriptor table breaks alignment constraints");
return;
}
self.desc_table = desc_table;
}
fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
let low = low.unwrap_or(self.avail_ring.0 as u32) as u64;
let high = high.unwrap_or((self.avail_ring.0 >> 32) as u32) as u64;
let avail_ring = GuestAddress((high << 32) | low);
if avail_ring.mask(0x1) != 0 {
error!("virtio queue available ring breaks alignment constraints");
return;
}
self.avail_ring = avail_ring;
}
fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
let low = low.unwrap_or(self.used_ring.0 as u32) as u64;
let high = high.unwrap_or((self.used_ring.0 >> 32) as u32) as u64;
let used_ring = GuestAddress((high << 32) | low);
if used_ring.mask(0x3) != 0 {
error!("virtio queue used ring breaks alignment constraints");
return;
}
self.used_ring = used_ring;
}
fn set_event_idx(&mut self, enabled: bool) {
self.signalled_used = None;
self.event_idx_enabled = enabled;
}
fn avail_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
let addr = self
.avail_ring
.checked_add(2)
.ok_or(Error::AddressOverflow)?;
mem.load(addr, order)
.map(u16::from_le)
.map(Wrapping)
.map_err(Error::GuestMemory)
}
fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
let addr = self.used_ring.unchecked_add(2);
mem.load(addr, order)
.map(Wrapping)
.map_err(Error::GuestMemory)
}
fn add_used<M: GuestMemory>(
&mut self,
mem: &M,
head_index: u16,
len: u32,
) -> Result<(), Error> {
if head_index >= self.size {
error!(
"attempted to add out of bounds descriptor to used ring: {}",
head_index
);
return Err(Error::InvalidDescriptorIndex);
}
let next_used_index = u64::from(self.next_used.0 % self.size);
// This can not overflow an u64 since it is working with relatively small numbers compared
// to u64::MAX.
let offset = VIRTQ_USED_RING_HEADER_SIZE + next_used_index * VIRTQ_USED_ELEMENT_SIZE;
let addr = self
.used_ring
.checked_add(offset)
.ok_or(Error::AddressOverflow)?;
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
.map_err(Error::GuestMemory)?;
self.next_used += Wrapping(1);
mem.store(
u16::to_le(self.next_used.0),
self.used_ring
.checked_add(2)
.ok_or(Error::AddressOverflow)?,
Ordering::Release,
)
.map_err(Error::GuestMemory)
}
// TODO: Turn this into a doc comment/example.
// With the current implementation, a common way of consuming entries from the available ring
// while also leveraging notification suppression is to use a loop, for example:
//
// loop {
// // We have to explicitly disable notifications if `VIRTIO_F_EVENT_IDX` has not been
// // negotiated.
// self.disable_notification()?;
//
// for chain in self.iter()? {
// // Do something with each chain ...
// // Let's assume we process all available chains here.
// }
//
// // If `enable_notification` returns `true`, the driver has added more entries to the
// // available ring.
// if !self.enable_notification()? {
// break;
// }
// }
fn enable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> {
self.set_notification(mem, true)?;
// Ensures the following read is not reordered before any previous write operation.
fence(Ordering::SeqCst);
// We double check here to avoid the situation where the available ring has been updated
// just before we re-enabled notifications, and it's possible to miss one. We compare the
// current `avail_idx` value to `self.next_avail` because it's where we stopped processing
// entries. There are situations where we intentionally avoid processing everything in the
// available ring (which will cause this method to return `true`), but in that case we'll
// probably not re-enable notifications as we already know there are pending entries.
self.avail_idx(mem, Ordering::Relaxed)
.map(|idx| idx != self.next_avail)
}
fn disable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<(), Error> {
self.set_notification(mem, false)
}
fn needs_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> {
let used_idx = self.next_used;
// Complete all the writes in add_used() before reading the event.
fence(Ordering::SeqCst);
// The VRING_AVAIL_F_NO_INTERRUPT flag isn't supported yet.
if self.event_idx_enabled {
if let Some(old_idx) = self.signalled_used.replace(used_idx) {
let used_event = self.used_event(mem, Ordering::Relaxed)?;
// This check looks at `used_idx`, `used_event`, and `old_idx` as if they are on
// an axis that wraps around. If `used_idx - used_used - Wrapping(1)` is greater
// than or equal to the difference between `used_idx` and `old_idx`, then
// `old_idx` is closer to `used_idx` than `used_event` (and thus more recent), so
// we don't need to elicit another notification.
if (used_idx - used_event - Wrapping(1u16)) >= (used_idx - old_idx) {
return Ok(false);
}
}
}
Ok(true)
}
fn next_avail(&self) -> u16 {
self.next_avail.0
}
fn next_used(&self) -> u16 {
self.next_used.0
}
fn set_next_avail(&mut self, next_avail: u16) {
self.next_avail = Wrapping(next_avail);
}
fn set_next_used(&mut self, next_used: u16) {
self.next_used = Wrapping(next_used);
}
}

View File

@ -1,333 +0,0 @@
// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
use std::num::Wrapping;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, MutexGuard};
use vm_memory::GuestMemory;
use crate::{Error, QueueState, QueueStateGuard, QueueStateT};
/// Struct to maintain information and manipulate state of a virtio queue for multi-threaded
/// context.
///
/// # Example
///
/// ```rust
/// use virtio_queue::{Queue, QueueState, QueueStateSync, QueueStateT};
/// use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
///
/// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
/// let mut queue = QueueStateSync::new(1024);
///
/// // First, the driver sets up the queue; this set up is done via writes on the bus (PCI, MMIO).
/// queue.set_size(8);
/// queue.set_desc_table_address(Some(0x1000), None);
/// queue.set_avail_ring_address(Some(0x2000), None);
/// queue.set_used_ring_address(Some(0x3000), None);
/// queue.set_ready(true);
/// // The user should check if the queue is valid before starting to use it.
/// assert!(queue.is_valid(m.memory()));
///
/// // The memory object is not embedded in the `QueueStateSync`, so we have to pass it as a
/// // parameter to the methods that access the guest memory. Examples would be:
/// queue.add_used(m.memory(), 1, 0x100).unwrap();
/// queue.needs_notification(m.memory()).unwrap();
/// ```
#[derive(Clone, Debug)]
pub struct QueueStateSync {
state: Arc<Mutex<QueueState>>,
}
impl QueueStateSync {
fn lock_state(&self) -> MutexGuard<QueueState> {
// Do not expect poisoned lock.
self.state.lock().unwrap()
}
}
impl<'a> QueueStateGuard<'a> for QueueStateSync {
type G = MutexGuard<'a, QueueState>;
}
impl QueueStateT for QueueStateSync {
fn new(max_size: u16) -> Self {
QueueStateSync {
state: Arc::new(Mutex::new(QueueState::new(max_size))),
}
}
fn is_valid<M: GuestMemory>(&self, mem: &M) -> bool {
self.lock_state().is_valid(mem)
}
fn reset(&mut self) {
self.lock_state().reset();
}
fn lock(&mut self) -> <Self as QueueStateGuard>::G {
self.lock_state()
}
fn max_size(&self) -> u16 {
self.lock_state().max_size()
}
fn set_size(&mut self, size: u16) {
self.lock_state().set_size(size);
}
fn ready(&self) -> bool {
self.lock_state().ready
}
fn set_ready(&mut self, ready: bool) {
self.lock_state().set_ready(ready)
}
fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.lock_state().set_desc_table_address(low, high);
}
fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.lock_state().set_avail_ring_address(low, high);
}
fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.lock_state().set_used_ring_address(low, high);
}
fn set_event_idx(&mut self, enabled: bool) {
self.lock_state().set_event_idx(enabled);
}
fn avail_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
self.lock_state().avail_idx(mem, order)
}
fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
self.lock_state().used_idx(mem, order)
}
fn add_used<M: GuestMemory>(
&mut self,
mem: &M,
head_index: u16,
len: u32,
) -> Result<(), Error> {
self.lock_state().add_used(mem, head_index, len)
}
fn enable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> {
self.lock_state().enable_notification(mem)
}
fn disable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<(), Error> {
self.lock_state().disable_notification(mem)
}
fn needs_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> {
self.lock_state().needs_notification(mem)
}
fn next_avail(&self) -> u16 {
self.lock_state().next_avail()
}
fn next_used(&self) -> u16 {
self.lock_state().next_used()
}
fn set_next_avail(&mut self, next_avail: u16) {
self.lock_state().set_next_avail(next_avail);
}
fn set_next_used(&mut self, next_used: u16) {
self.lock_state().set_next_used(next_used);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::defs::{
DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR,
VIRTQ_USED_F_NO_NOTIFY,
};
use std::sync::Barrier;
use vm_memory::{Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
#[test]
fn test_queue_state_sync() {
let mut q = QueueStateSync::new(0x1000);
let mut q2 = q.clone();
let q3 = q.clone();
let barrier = Arc::new(Barrier::new(3));
let b2 = barrier.clone();
let b3 = barrier.clone();
let t1 = std::thread::spawn(move || {
{
let guard = q2.lock();
assert!(!guard.ready());
}
b2.wait();
b2.wait();
{
let guard = q2.lock();
assert!(guard.ready());
}
});
let t2 = std::thread::spawn(move || {
assert!(!q3.ready());
b3.wait();
b3.wait();
assert!(q3.ready());
});
barrier.wait();
q.set_ready(true);
barrier.wait();
t1.join().unwrap();
t2.join().unwrap();
}
#[test]
fn test_state_sync_add_used() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let mut q = QueueStateSync::new(0x100);
q.set_desc_table_address(Some(0x1000), None);
q.set_avail_ring_address(Some(0x2000), None);
q.set_used_ring_address(Some(0x3000), None);
q.set_event_idx(true);
q.set_ready(true);
assert!(q.is_valid(m.memory()));
assert_eq!(q.lock().size, 0x100);
assert_eq!(q.max_size(), 0x100);
q.set_size(0x80);
assert_eq!(q.max_size(), 0x100);
q.set_next_avail(5);
assert_eq!(q.next_avail(), 5);
assert_eq!(
q.avail_idx(m.memory(), Ordering::Acquire).unwrap(),
Wrapping(0)
);
assert_eq!(q.lock_state().next_used, Wrapping(0));
// index too large
assert!(q.add_used(m.memory(), 0x200, 0x1000).is_err());
assert_eq!(q.lock_state().next_used, Wrapping(0));
// should be ok
q.add_used(m.memory(), 1, 0x1000).unwrap();
assert_eq!(q.lock_state().next_used, Wrapping(1));
}
#[test]
fn test_sync_state_reset_queue() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let mut q = QueueStateSync::new(0x100);
q.set_desc_table_address(Some(0x1000), None);
q.set_avail_ring_address(Some(0x2000), None);
q.set_used_ring_address(Some(0x3000), None);
q.set_event_idx(true);
q.set_next_avail(2);
q.set_size(0x8);
q.set_ready(true);
assert!(q.is_valid(m.memory()));
q.add_used(m.memory(), 1, 0x100).unwrap();
q.needs_notification(m.memory()).unwrap();
assert_eq!(q.lock_state().size, 0x8);
assert!(q.lock_state().ready);
assert_ne!(
q.lock_state().desc_table,
GuestAddress(DEFAULT_DESC_TABLE_ADDR)
);
assert_ne!(
q.lock_state().avail_ring,
GuestAddress(DEFAULT_AVAIL_RING_ADDR)
);
assert_ne!(
q.lock_state().used_ring,
GuestAddress(DEFAULT_USED_RING_ADDR)
);
assert_ne!(q.lock_state().next_avail, Wrapping(0));
assert_ne!(q.lock_state().next_used, Wrapping(0));
assert_ne!(q.lock_state().signalled_used, None);
assert!(q.lock_state().event_idx_enabled);
q.reset();
assert_eq!(q.lock_state().size, 0x100);
assert!(!q.lock_state().ready);
assert_eq!(
q.lock_state().desc_table,
GuestAddress(DEFAULT_DESC_TABLE_ADDR)
);
assert_eq!(
q.lock_state().avail_ring,
GuestAddress(DEFAULT_AVAIL_RING_ADDR)
);
assert_eq!(
q.lock_state().used_ring,
GuestAddress(DEFAULT_USED_RING_ADDR)
);
assert_eq!(q.lock_state().next_avail, Wrapping(0));
assert_eq!(q.lock_state().next_used, Wrapping(0));
assert_eq!(q.lock_state().signalled_used, None);
assert!(!q.lock_state().event_idx_enabled);
}
#[test]
fn test_enable_disable_notification() {
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let mem = m.memory();
let mut q = QueueStateSync::new(0x100);
q.set_desc_table_address(Some(0x1000), None);
q.set_avail_ring_address(Some(0x2000), None);
q.set_used_ring_address(Some(0x3000), None);
q.set_ready(true);
assert!(q.is_valid(mem));
let used_addr = q.lock_state().used_ring;
assert!(!q.lock_state().event_idx_enabled);
q.enable_notification(mem).unwrap();
let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
assert_eq!(v, 0);
q.disable_notification(m.memory()).unwrap();
let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
assert_eq!(v, VIRTQ_USED_F_NO_NOTIFY);
q.enable_notification(mem).unwrap();
let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
assert_eq!(v, 0);
q.set_event_idx(true);
let avail_addr = q.lock_state().avail_ring;
m.write_obj::<u16>(u16::to_le(2), avail_addr.unchecked_add(2))
.unwrap();
assert!(q.enable_notification(mem).unwrap());
q.lock_state().next_avail = Wrapping(2);
assert!(!q.enable_notification(mem).unwrap());
m.write_obj::<u16>(u16::to_le(8), avail_addr.unchecked_add(2))
.unwrap();
assert!(q.enable_notification(mem).unwrap());
q.lock_state().next_avail = Wrapping(8);
assert!(!q.enable_notification(mem).unwrap());
}
}

View File

@ -10,5 +10,5 @@ default = []
[dependencies]
log = "0.4.14"
virtio-bindings = { version = "0.1.0", features = ["virtio-v5_0_0"] }
virtio-queue = { path = "../virtio-queue" }
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", branch = "main" }
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }

View File

@ -47,7 +47,7 @@ vfio-ioctls = { git = "https://github.com/rust-vmm/vfio-ioctls", branch = "main"
vfio_user = { path = "../vfio_user" }
vhdx = { path = "../vhdx" }
virtio-devices = { path = "../virtio-devices" }
virtio-queue = { path = "../virtio-queue" }
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", branch = "main" }
vm-allocator = { path = "../vm-allocator" }
vm-device = { path = "../vm-device" }
vm-memory = { version = "0.7.0", features = ["backend-mmap", "backend-atomic", "backend-bitmap"] }