Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the usability of queue interfaces #148

Merged
merged 3 commits into from
Mar 30, 2022
Merged
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 crates/virtio-queue/src/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub struct AvailIter<'b, M> {
impl<'b, M> AvailIter<'b, M>
where
M: Deref,
M::Target: GuestMemory + Sized,
M::Target: GuestMemory,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a description about this in the commit message as well? It will help with writing the changelog.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think it's better to relax the trait bounds for the other methods as well first via a separate PR, and add that to the changelog. Created #152 to track this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change required in this commit? If not, I would postpone it when fixing #152, if yes, I would still leave some sort of explanation about it in the commit message or at least give a pointer to #152.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an explanation, and mentioned the issue as well in the commit message.

{
/// Create a new instance of `AvailInter`.
///
Expand Down
31 changes: 30 additions & 1 deletion crates/virtio-queue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ pub trait QueueStateT: for<'a> QueueStateGuard<'a> {
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>;
fn avail_idx<M>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>
where
M: GuestMemory + ?Sized;

/// Read the `idx` field from the used ring.
fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>;
Expand Down Expand Up @@ -193,4 +195,31 @@ pub trait QueueStateT: for<'a> QueueStateGuard<'a> {

/// Set the index for the next descriptor in the used ring.
fn set_next_used(&mut self, next_used: u16);

/// Pop and return the next available descriptor chain, or `None` when there are no more
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add some description about how this is suppose to be used (i.e. in respect to the already existing iterator; i think you mention this is an alternative way of consuming the iterator)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an extra phrase to the comment. Didn't mention the iterator, as it doesn't even exist strictly in the context of the QueueStateT trait.

/// 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<M>(&mut self, mem: M) -> Option<DescriptorChain<M>>
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 QueueStateOwnedT: QueueStateT {
/// 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<M>(&mut self, mem: M) -> Result<AvailIter<'_, M>, 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);
jiangliu marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 3 additions & 1 deletion crates/virtio-queue/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use std::sync::atomic::Ordering;

use vm_memory::GuestAddressSpace;

use crate::{AvailIter, Error, QueueGuard, QueueState, QueueStateGuard, QueueStateT};
use crate::{
AvailIter, Error, QueueGuard, QueueState, QueueStateGuard, QueueStateOwnedT, QueueStateT,
};

/// A convenient wrapper struct for a virtio queue, with associated `GuestMemory` object.
///
Expand Down
33 changes: 32 additions & 1 deletion crates/virtio-queue/src/queue_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::sync::atomic::Ordering;

use vm_memory::GuestMemory;

use crate::{AvailIter, Error, QueueState, QueueStateT};
use crate::{AvailIter, DescriptorChain, Error, QueueState, QueueStateOwnedT, QueueStateT};

/// A guard object to exclusively access an `Queue` object.
///
Expand Down Expand Up @@ -192,10 +192,21 @@ where
self.state.set_next_used(next_used);
}

/// Pop and return the next available descriptor chain, or `None` when there are no more
/// descriptor chains available.
pub fn pop_descriptor_chain(&mut self) -> Option<DescriptorChain<M>> {
self.state.pop_descriptor_chain(self.mem.clone())
}

/// 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())
}

/// Decrement the value of the next available index by one position.
pub fn go_to_previous_position(&mut self) {
self.state.go_to_previous_position();
}
}

#[cfg(test)]
Expand Down Expand Up @@ -259,7 +270,27 @@ mod tests {
break;
}
}

{
g.go_to_previous_position();
let mut chain = g.pop_descriptor_chain().unwrap();
// The descriptor index of the head descriptor from the last available chain
// defined above is equal to 5. The chain has two descriptors, the second with
// the index equal to 6.
assert_eq!(chain.head_index(), 5);

let desc = chain.next().unwrap();
assert!(desc.has_next());
assert_eq!(desc.next(), 6);

let desc = chain.next().unwrap();
assert!(!desc.has_next());

assert!(chain.next().is_none());
}

// 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_eq!(g.next_used(), 3);
Expand Down
53 changes: 38 additions & 15 deletions crates/virtio-queue/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ use crate::defs::{
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};
use crate::{
error, AvailIter, Descriptor, DescriptorChain, Error, QueueStateGuard, QueueStateOwnedT,
QueueStateT, VirtqUsedElem,
};

/// Struct to maintain information and manipulate state of a virtio queue.
///
Expand Down Expand Up @@ -64,19 +67,6 @@ pub struct QueueState {
}

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>(
Expand Down Expand Up @@ -311,7 +301,10 @@ impl QueueStateT for QueueState {
self.event_idx_enabled = enabled;
}

fn avail_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
fn avail_idx<M>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>
where
M: GuestMemory + ?Sized,
{
let addr = self
.avail_ring
.checked_add(2)
Expand Down Expand Up @@ -450,4 +443,34 @@ impl QueueStateT for QueueState {
fn set_next_used(&mut self, next_used: u16) {
self.next_used = Wrapping(next_used);
}

fn pop_descriptor_chain<M>(&mut self, mem: M) -> Option<DescriptorChain<M>>
where
M: Clone + Deref,
M::Target: GuestMemory,
{
// Default, iter-based impl. Will be subsequently improved.
match self.iter(mem) {
Ok(mut iter) => iter.next(),
Err(e) => {
error!("Iterator error {}", e);
None
}
}
}
}

impl QueueStateOwnedT for QueueState {
fn iter<M>(&mut self, mem: M) -> Result<AvailIter<'_, M>, Error>
where
M: Deref,
M::Target: GuestMemory,
{
self.avail_idx(mem.deref(), Ordering::Acquire)
.map(move |idx| AvailIter::new(mem, idx, self))
}

fn go_to_previous_position(&mut self) {
self.next_avail -= Wrapping(1);
}
}
16 changes: 14 additions & 2 deletions crates/virtio-queue/src/state_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

use std::num::Wrapping;
use std::ops::Deref;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, MutexGuard};

use vm_memory::GuestMemory;

use crate::{Error, QueueState, QueueStateGuard, QueueStateT};
use crate::{DescriptorChain, Error, QueueState, QueueStateGuard, QueueStateT};

/// Struct to maintain information and manipulate state of a virtio queue for multi-threaded
/// context.
Expand Down Expand Up @@ -107,7 +108,10 @@ impl QueueStateT for QueueStateSync {
self.lock_state().set_event_idx(enabled);
}

fn avail_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
fn avail_idx<M>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>
where
M: GuestMemory + ?Sized,
{
self.lock_state().avail_idx(mem, order)
}

Expand Down Expand Up @@ -151,6 +155,14 @@ impl QueueStateT for QueueStateSync {
fn set_next_used(&mut self, next_used: u16) {
self.lock_state().set_next_used(next_used);
}

fn pop_descriptor_chain<M>(&mut self, mem: M) -> Option<DescriptorChain<M>>
where
M: Clone + Deref,
M::Target: GuestMemory,
{
self.lock_state().pop_descriptor_chain(mem)
}
}

#[cfg(test)]
Expand Down