// 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::queue::{AvailIter, Queue}; pub use self::queue_sync::QueueSync; pub use self::state::QueueState; pub mod defs; #[cfg(any(test, feature = "test-utils"))] pub mod mock; mod chain; mod descriptor; mod queue; mod queue_sync; mod state; /// 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, /// Invalid max_size. InvalidMaxSize, /// Invalid Queue Size. InvalidSize, /// Invalid alignment of descriptor table address. InvalidDescTableAlign, /// Invalid alignment of available ring address. InvalidAvailRingAlign, /// Invalid alignment of used ring address. InvalidUsedRingAlign, /// Invalid available ring index. InvalidAvailRingIndex, /// The queue is not ready for operation. QueueNotReady, } 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"), InvalidMaxSize => write!(f, "invalid queue maximum size"), InvalidSize => write!(f, "invalid queue size"), InvalidDescTableAlign => write!( f, "virtio queue descriptor table breaks alignment constraints" ), InvalidAvailRingAlign => write!( f, "virtio queue available ring breaks alignment constraints" ), InvalidUsedRingAlign => { write!(f, "virtio queue used ring breaks alignment constraints") } InvalidAvailRingIndex => write!( f, "invalid available ring index (more descriptors to process than queue size)" ), QueueNotReady => write!(f, "trying to process requests on a queue that's not ready"), } } } impl std::error::Error for Error {} /// Trait for objects returned by `QueueT::lock()`. pub trait QueueGuard<'a> { /// Type for guard returned by `Self::lock()`. type G: DerefMut; } /// Trait to access and manipulate a virtio queue. /// /// To optimize for performance, different implementations of the `QueueT` 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 `QueueT` trait with a lifetime as well. pub trait QueueT: for<'a> QueueGuard<'a> { /// Construct an empty virtio queue state object with the given `max_size`. /// /// Returns an error if `max_size` is invalid. fn new(max_size: u16) -> Result where Self: Sized; /// Check whether the queue configuration is valid. fn is_valid(&self, mem: &M) -> bool; /// Reset the queue to the initial state. fn reset(&mut self); /// Get an exclusive reference to the underlying `Queue` object. /// /// Logically this method will acquire the underlying lock protecting the `Queue` Object. /// The lock will be released when the returned object gets dropped. fn lock(&mut self) -> ::G; /// Get the maximum size of the virtio queue. fn max_size(&self) -> u16; /// Get the actual size configured by the guest. fn 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, high: Option); /// 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, high: Option); /// 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, high: Option); /// 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. /// /// # Panics /// /// Panics if order is Release or AcqRel. fn avail_idx(&self, mem: &M, order: Ordering) -> Result, Error> where M: GuestMemory + ?Sized; /// Read the `idx` field from the used ring. /// /// # Panics /// /// Panics if order is Release or AcqRel. fn used_idx(&self, mem: &M, order: Ordering) -> Result, Error>; /// Put a used descriptor head into the used ring. fn add_used(&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(&mut self, mem: &M) -> Result; /// Disable notification events from the guest driver. fn disable_notification(&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(&mut self, mem: &M) -> Result; /// Return the index of the next entry in the available ring. fn next_avail(&self) -> u16; /// Set the index of the next entry in the available ring. fn set_next_avail(&mut self, next_avail: u16); /// Return the index for the next descriptor in the used ring. fn next_used(&self) -> u16; /// Set the index for the next descriptor in the used ring. fn set_next_used(&mut self, next_used: u16); /// Return the address of the descriptor table. fn desc_table(&self) -> u64; /// Return the address of the available ring. fn avail_ring(&self) -> u64; /// Return the address of the used ring. fn used_ring(&self) -> u64; /// Checks whether `VIRTIO_F_RING_EVENT_IDX` is negotiated. /// /// This getter is only returning the correct value after the device passes the `FEATURES_OK` /// status. fn event_idx_enabled(&self) -> bool; /// Pop and return the next available descriptor chain, or `None` when there are no more /// descriptor chains available. /// /// This enables the consumption of available descriptor chains in a "one at a time" /// manner, without having to hold a borrow after the method returns. fn pop_descriptor_chain(&mut self, mem: M) -> Option> where M: Clone + Deref, M::Target: GuestMemory; } /// Trait to access and manipulate a Virtio queue that's known to be exclusively accessed /// by a single execution thread. pub trait QueueOwnedT: QueueT { /// 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. fn iter(&mut self, mem: M) -> Result, Error> where M: Deref, M::Target: GuestMemory; /// Undo the last advancement of the next available index field by decrementing its /// value by one. fn go_to_previous_position(&mut self); }