Skip to content

Commit f264097

Browse files
committed
introduce QueueGuard
QueueGuard encapsulates Queue during a sequence of accesses under the same critical section. A QueueGuard always uses the same GuestMemory for all operations, and can be extended to be a single critical section in the case of multithreaded VMMs. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 67100b6 commit f264097

File tree

1 file changed

+119
-1
lines changed

1 file changed

+119
-1
lines changed

crates/virtio-queue/src/lib.rs

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ pub mod mock;
2222
use std::cmp::min;
2323
use std::convert::TryFrom;
2424
use std::fmt::{self, Debug, Display};
25+
use std::marker::PhantomData;
2526
use std::mem::size_of;
2627
use std::num::Wrapping;
27-
use std::ops::Deref;
28+
use std::ops::{Deref, DerefMut};
2829
use std::sync::atomic::{fence, Ordering};
2930

3031
use log::error;
@@ -785,6 +786,103 @@ impl QueueState {
785786
}
786787

787788
/// A convenient wrapper struct for a virtio queue, with associated GuestMemory object.
789+
pub struct QueueGuard<'a, M: 'a + Clone + Deref, S: 'a + DerefMut<Target = QueueState>>
790+
where
791+
M::Target: GuestMemory + Sized,
792+
{
793+
/// Guest memory object associated with the queue.
794+
pub mem: M,
795+
796+
/// Virtio queue state.
797+
pub state: S,
798+
799+
_marker: PhantomData<&'a S::Target>,
800+
}
801+
802+
impl<'a, M: 'a + Clone + Deref, S: 'a + DerefMut<Target = QueueState>> QueueGuard<'a, M, S>
803+
where
804+
M::Target: GuestMemory + Sized,
805+
{
806+
fn new(mem: M, state: S) -> QueueGuard<'a, M, S> {
807+
QueueGuard {
808+
mem,
809+
state,
810+
_marker: PhantomData,
811+
}
812+
}
813+
814+
/// Check whether the queue configuration is valid.
815+
pub fn is_valid(&self) -> bool {
816+
self.state.is_valid(self.mem.deref())
817+
}
818+
819+
/// Read the `idx` field from the available ring.
820+
pub fn avail_idx(&self, order: Ordering) -> Result<Wrapping<u16>, Error> {
821+
self.state.avail_idx(self.mem.deref(), order)
822+
}
823+
824+
/// Put a used descriptor head into the used ring.
825+
pub fn add_used(&mut self, head_index: u16, len: u32) -> Result<(), Error> {
826+
self.state.add_used(self.mem.deref(), head_index, len)
827+
}
828+
829+
/// Enable notification events from the guest driver.
830+
///
831+
/// Return true if one or more descriptors can be consumed from the available ring after
832+
/// notifications were enabled (and thus it's possible there will be no corresponding
833+
/// notification).
834+
pub fn enable_notification(&mut self) -> Result<bool, Error> {
835+
self.state.enable_notification(self.mem.deref())
836+
}
837+
838+
/// Disable notification events from the guest driver.
839+
pub fn disable_notification(&mut self) -> Result<(), Error> {
840+
self.state.disable_notification(self.mem.deref())
841+
}
842+
843+
/// Check whether a notification to the guest is needed.
844+
///
845+
/// Please note this method has side effects: once it returns `true`, it considers the
846+
/// driver will actually be notified, remember the associated index in the used ring, and
847+
/// won't return `true` again until the driver updates `used_event` and/or the notification
848+
/// conditions hold once more.
849+
pub fn needs_notification(&mut self) -> Result<bool, Error> {
850+
self.state.needs_notification(self.mem.deref())
851+
}
852+
853+
/// A consuming iterator over all available descriptor chain heads offered by the driver.
854+
pub fn iter(&mut self) -> Result<AvailIter<'_, M>, Error> {
855+
// FIXME: this is inefficient
856+
self.state.iter(self.mem.clone())
857+
}
858+
}
859+
860+
impl<'a, M: 'a + Clone + Deref, S: 'a + DerefMut<Target = QueueState>> Deref
861+
for QueueGuard<'a, M, S>
862+
where
863+
M::Target: GuestMemory + Sized,
864+
{
865+
type Target = QueueState;
866+
867+
/// Reset the queue to the initial state.
868+
fn deref(&self) -> &QueueState {
869+
&self.state
870+
}
871+
}
872+
873+
impl<'a, M: 'a + Clone + Deref, S: 'a + DerefMut<Target = QueueState>> DerefMut
874+
for QueueGuard<'a, M, S>
875+
where
876+
M::Target: GuestMemory + Sized,
877+
{
878+
/// Reset the queue to the initial state.
879+
fn deref_mut(&mut self) -> &mut QueueState {
880+
&mut self.state
881+
}
882+
}
883+
884+
/// A convenient wrapper struct for a virtio queue, with associated GuestAddressSpace
885+
/// object.
788886
#[derive(Clone, Debug)]
789887
pub struct Queue<M: GuestAddressSpace> {
790888
/// Guest memory object associated with the queue.
@@ -802,6 +900,12 @@ impl<M: GuestAddressSpace> Queue<M> {
802900
}
803901
}
804902

903+
/// Return a QueueGuard that allows to do multiple QueueState operations
904+
/// using the same GuestMemory.
905+
pub fn acquire(&mut self) -> QueueGuard<M::T, &mut QueueState> {
906+
QueueGuard::new(self.mem.memory(), &mut self.state)
907+
}
908+
805909
/// Check whether the queue configuration is valid.
806910
pub fn is_valid(&self) -> bool {
807911
self.state.is_valid(self.mem.memory().deref())
@@ -1275,6 +1379,20 @@ mod tests {
12751379
assert_eq!(x.len, 0x1000);
12761380
}
12771381

1382+
#[test]
1383+
fn test_queue_guard() {
1384+
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
1385+
let vq = MockSplitQueue::new(m, 16);
1386+
1387+
let mut q = vq.create_queue(m);
1388+
let mut qstate = q.acquire();
1389+
qstate.ready = true;
1390+
qstate.reset();
1391+
assert_eq!(qstate.ready, false);
1392+
let mut iter = qstate.iter().unwrap();
1393+
assert!(iter.next().is_none());
1394+
}
1395+
12781396
#[test]
12791397
fn test_reset_queue() {
12801398
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();

0 commit comments

Comments
 (0)