Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion coverage_config_x86_64.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"coverage_score": 90.2,
"coverage_score": 88.8,
"exclude_path": "crates/virtio-queue/src/mock.rs",
"crate_features": "virtio-blk/backend-stdio"
}
18 changes: 12 additions & 6 deletions crates/devices/virtio-blk/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//! approach.

use std::fmt::{self, Display};
use std::ops::Deref;
use std::result;

use crate::defs::{
Expand All @@ -32,9 +33,7 @@ use crate::defs::{
};

use virtio_queue::{Descriptor, DescriptorChain};
use vm_memory::{
ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryError,
};
use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError};

/// Block request parsing errors.
#[derive(Debug)]
Expand Down Expand Up @@ -159,7 +158,10 @@ impl Request {
}

// Checks that a descriptor meets the minimal requirements for a valid status descriptor.
fn check_status_desc<M: GuestMemory>(mem: &M, desc: Descriptor) -> Result<()> {
fn check_status_desc<M>(mem: &M, desc: Descriptor) -> Result<()>
where
M: GuestMemory + ?Sized,
{
// The status MUST always be writable.
if !desc.is_write_only() {
return Err(Error::UnexpectedReadOnlyDescriptor);
Expand Down Expand Up @@ -202,7 +204,11 @@ impl Request {
/// # Arguments
/// * `desc_chain` - A mutable reference to the descriptor chain that should point to the
/// buffers of a virtio block request.
pub fn parse<M: GuestAddressSpace>(desc_chain: &mut DescriptorChain<M>) -> Result<Request> {
pub fn parse<M>(desc_chain: &mut DescriptorChain<M>) -> Result<Request>
where
M: Deref,
M::Target: GuestMemory,
{
let chain_head = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
// The head contains the request type which MUST be readable.
if chain_head.is_write_only() {
Expand Down Expand Up @@ -235,7 +241,7 @@ impl Request {
}
let status_desc = desc;

Request::check_status_desc::<<M>::M>(desc_chain.memory(), status_desc)?;
Request::check_status_desc(desc_chain.memory(), status_desc)?;

request.status_addr = status_desc.addr();
Ok(request)
Expand Down
199 changes: 199 additions & 0 deletions crates/virtio-queue/src/generic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright 2021 Red Hat, Inc.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

use std::num::Wrapping;
use std::ops::DerefMut;

use crate::{Error, Queue, QueueGuard, QueueState, QueueSync};
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, MutexGuard};
use vm_memory::GuestAddressSpace;

/// Lifetime-generic guard associated to a QueueStateT. In practice,
/// instead of having a `QueueStateT::Guard<'g>` generic associated type,
/// you have to write `<QueueStateT::Guard as QueueStateGuard<'g>>::Out`.
pub(crate) trait QueueStateGuard<'g> {
/// Type of the guard passed to the `with` method.
type Out: 'g + DerefMut<Target = QueueState>;
}

pub(crate) trait QueueT<M: GuestAddressSpace> {
/// Lifetime-generic guard. Usually this is just `Self`, and implementors
/// of `QueueT` also implement `QueueStateGuard`.
type Guard: for<'g> QueueStateGuard<'g>;

fn construct(mem: M, state: QueueState) -> Self;
fn with<
'a,
'g,
U,
F: FnOnce(QueueGuard<M::T, <Self::Guard as QueueStateGuard<'g>>::Out>) -> U,
>(
&'a mut self,
f: F,
) -> U
where
'a: 'g;

/// Check whether the queue configuration is valid.
fn is_valid(&mut self) -> bool {
self.with(|qstate| qstate.is_valid())
}

/// Reset the queue to the initial state.
fn reset(&mut self) {
self.with(|mut qstate| qstate.reset())
}

/// Get the maximum size of the virtio queue.
fn max_size(&mut self) -> u16 {
self.with(|qstate| qstate.max_size())
}

/// Configure the queue size for the virtio queue.
///
/// The `size` should power of two and less than or equal to value reported by `max_size()`,
/// otherwise it will panic.
fn set_size(&mut self, size: u16) {
self.with(|mut qstate| qstate.set_size(size))
}

/// Check whether the queue is ready to be processed.
fn ready(&mut self) -> bool {
self.with(|qstate| qstate.ready())
}

/// Configure the queue to ready for processing.
fn set_ready(&mut self, ready: bool) {
self.with(|mut qstate| qstate.set_ready(ready))
}

/// Set 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 valid.
fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.with(|mut qstate| qstate.set_desc_table_address(low, high))
}

/// Set 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 valid.
fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.with(|mut qstate| qstate.set_avail_ring_address(low, high))
}

/// Set 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 valid.
fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
self.with(|mut qstate| qstate.set_used_ring_address(low, high))
}

/// Enable/disable the VIRTIO_F_RING_EVENT_IDX feature for interrupt coalescing.
fn set_event_idx(&mut self, enabled: bool) {
self.with(|mut qstate| qstate.set_event_idx(enabled))
}

/// Read the `idx` field from the available ring.
fn avail_idx(&mut self, order: Ordering) -> Result<Wrapping<u16>, Error> {
self.with(|qstate| qstate.avail_idx(order))
}

/// Put a used descriptor head into the used ring.
fn add_used(&mut self, head_index: u16, len: u32) -> Result<(), Error> {
self.with(|mut qstate| qstate.add_used(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).
fn enable_notification(&mut self) -> Result<bool, Error> {
self.with(|mut qstate| qstate.enable_notification())
}

/// Disable notification events from the guest driver.
fn disable_notification(&mut self) -> Result<(), Error> {
self.with(|mut qstate| qstate.disable_notification())
}

/// 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(&mut self) -> Result<bool, Error> {
self.with(|mut qstate| qstate.needs_notification())
}

/// Return the index for the next descriptor in the available ring.
fn next_avail(&mut self) -> u16 {
self.with(|qstate| qstate.next_avail())
}

/// Set the index for the next descriptor in the available ring.
fn set_next_avail(&mut self, next_avail: u16) {
self.with(|mut qstate| qstate.set_next_avail(next_avail))
}
}

impl<'g, M: GuestAddressSpace> QueueStateGuard<'g> for Queue<M> {
type Out = &'g mut QueueState;
}

impl<M: GuestAddressSpace> QueueT<M> for Queue<M> {
type Guard = Self;

fn construct(mem: M, state: QueueState) -> Self {
Queue { mem, state }
}
fn with<
'a,
'g,
U,
F: FnOnce(QueueGuard<M::T, <Self::Guard as QueueStateGuard<'g>>::Out>) -> U,
>(
&'a mut self,
f: F,
) -> U
where
'a: 'g,
{
f(self.acquire())
}
}

impl<'g, M: GuestAddressSpace> QueueStateGuard<'g> for QueueSync<M> {
type Out = MutexGuard<'g, QueueState>;
}

impl<M: GuestAddressSpace> QueueT<M> for QueueSync<M> {
type Guard = Self;

fn construct(mem: M, state: QueueState) -> Self {
QueueSync {
mem,
state: Arc::new(Mutex::new(state)),
}
}
fn with<
'a,
'g,
U,
F: FnOnce(QueueGuard<M::T, <Self::Guard as QueueStateGuard<'g>>::Out>) -> U,
>(
&'a mut self,
f: F,
) -> U
where
'a: 'g,
{
f(self.lock())
}
}
Loading