Skip to content

Commit e3ed61a

Browse files
committed
mock: add trait to build QueueState and QueueGuard
This will allow to reuse the same tests for QueueState and QueueStateSync. The trait is _not_ part of the public API, which consists exclusively of acquire() and lock(). This is because with() is mostly useless for the multithreaded case, where you have a &self but not a &mut self. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent f264097 commit e3ed61a

File tree

3 files changed

+198
-15
lines changed

3 files changed

+198
-15
lines changed

crates/virtio-queue/src/generic.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright 2021 Red Hat, Inc.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
4+
5+
use std::num::Wrapping;
6+
use std::ops::DerefMut;
7+
8+
use crate::{Error, Queue, QueueGuard, QueueState};
9+
use std::sync::atomic::Ordering;
10+
use vm_memory::GuestAddressSpace;
11+
12+
/// Lifetime-generic guard associated to a QueueStateT. In practice,
13+
/// instead of having a `QueueStateT::Guard<'g>` generic associated type,
14+
/// you have to write `<QueueStateT::Guard as QueueStateGuard<'g>>::Out`.
15+
pub(crate) trait QueueStateGuard<'g> {
16+
/// Type of the guard passed to the `with` method.
17+
type Out: 'g + DerefMut<Target = QueueState>;
18+
}
19+
20+
pub(crate) trait QueueT<M: GuestAddressSpace> {
21+
/// Lifetime-generic guard. Usually this is just `Self`, and implementors
22+
/// of `QueueT` also implement `QueueStateGuard`.
23+
type Guard: for<'g> QueueStateGuard<'g>;
24+
25+
fn construct(mem: M, state: QueueState) -> Self;
26+
fn with<
27+
'a,
28+
'g,
29+
U,
30+
F: FnOnce(QueueGuard<M::T, <Self::Guard as QueueStateGuard<'g>>::Out>) -> U,
31+
>(
32+
&'a mut self,
33+
f: F,
34+
) -> U
35+
where
36+
'a: 'g;
37+
38+
/// Check whether the queue configuration is valid.
39+
fn is_valid(&mut self) -> bool {
40+
self.with(|qstate| qstate.is_valid())
41+
}
42+
43+
/// Reset the queue to the initial state.
44+
fn reset(&mut self) {
45+
self.with(|mut qstate| qstate.reset())
46+
}
47+
48+
/// Get the maximum size of the virtio queue.
49+
fn max_size(&mut self) -> u16 {
50+
self.with(|qstate| qstate.max_size())
51+
}
52+
53+
/// Return the actual size of the queue.
54+
///
55+
/// The virtio driver may configure queue size smaller than the value reported by `max_size()`.
56+
fn actual_size(&mut self) -> u16 {
57+
self.with(|qstate| qstate.actual_size())
58+
}
59+
60+
/// Configure the queue size for the virtio queue.
61+
///
62+
/// The `size` should power of two and less than or equal to value reported by `max_size()`,
63+
/// otherwise it will panic.
64+
fn set_size(&mut self, size: u16) {
65+
self.with(|mut qstate| qstate.set_size(size))
66+
}
67+
68+
/// Check whether the queue is ready to be processed.
69+
fn ready(&mut self) -> bool {
70+
self.with(|qstate| qstate.ready())
71+
}
72+
73+
/// Configure the queue to ready for processing.
74+
fn set_ready(&mut self, ready: bool) {
75+
self.with(|mut qstate| qstate.set_ready(ready))
76+
}
77+
78+
/// Set descriptor table address for the queue.
79+
///
80+
/// The descriptor table address is 64-bit, the corresponding part will be updated if 'low'
81+
/// and/or `high` is valid.
82+
fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) {
83+
self.with(|mut qstate| qstate.set_desc_table_address(low, high))
84+
}
85+
86+
/// Set available ring address for the queue.
87+
///
88+
/// The available ring address is 64-bit, the corresponding part will be updated if 'low'
89+
/// and/or `high` is valid.
90+
fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
91+
self.with(|mut qstate| qstate.set_avail_ring_address(low, high))
92+
}
93+
94+
/// Set used ring address for the queue.
95+
///
96+
/// The used ring address is 64-bit, the corresponding part will be updated if 'low'
97+
/// and/or `high` is valid.
98+
fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
99+
self.with(|mut qstate| qstate.set_used_ring_address(low, high))
100+
}
101+
102+
/// Enable/disable the VIRTIO_F_RING_EVENT_IDX feature for interrupt coalescing.
103+
fn set_event_idx(&mut self, enabled: bool) {
104+
self.with(|mut qstate| qstate.set_event_idx(enabled))
105+
}
106+
107+
/// Read the `idx` field from the available ring.
108+
fn avail_idx(&mut self, order: Ordering) -> Result<Wrapping<u16>, Error> {
109+
self.with(|qstate| qstate.avail_idx(order))
110+
}
111+
112+
/// Put a used descriptor head into the used ring.
113+
fn add_used(&mut self, head_index: u16, len: u32) -> Result<(), Error> {
114+
self.with(|mut qstate| qstate.add_used(head_index, len))
115+
}
116+
117+
/// Enable notification events from the guest driver.
118+
///
119+
/// Return true if one or more descriptors can be consumed from the available ring after
120+
/// notifications were enabled (and thus it's possible there will be no corresponding
121+
/// notification).
122+
fn enable_notification(&mut self) -> Result<bool, Error> {
123+
self.with(|mut qstate| qstate.enable_notification())
124+
}
125+
126+
/// Disable notification events from the guest driver.
127+
fn disable_notification(&mut self) -> Result<(), Error> {
128+
self.with(|mut qstate| qstate.disable_notification())
129+
}
130+
131+
/// Check whether a notification to the guest is needed.
132+
///
133+
/// Please note this method has side effects: once it returns `true`, it considers the
134+
/// driver will actually be notified, remember the associated index in the used ring, and
135+
/// won't return `true` again until the driver updates `used_event` and/or the notification
136+
/// conditions hold once more.
137+
fn needs_notification(&mut self) -> Result<bool, Error> {
138+
self.with(|mut qstate| qstate.needs_notification())
139+
}
140+
141+
/// Return the index for the next descriptor in the available ring.
142+
fn next_avail(&mut self) -> u16 {
143+
self.with(|qstate| qstate.next_avail())
144+
}
145+
146+
/// Set the index for the next descriptor in the available ring.
147+
fn set_next_avail(&mut self, next_avail: u16) {
148+
self.with(|mut qstate| qstate.set_next_avail(next_avail))
149+
}
150+
}
151+
152+
impl<'g, M: GuestAddressSpace> QueueStateGuard<'g> for Queue<M> {
153+
type Out = &'g mut QueueState;
154+
}
155+
156+
impl<M: GuestAddressSpace> QueueT<M> for Queue<M> {
157+
type Guard = Self;
158+
159+
fn construct(mem: M, state: QueueState) -> Self {
160+
Queue { mem, state }
161+
}
162+
fn with<
163+
'a,
164+
'g,
165+
U,
166+
F: FnOnce(QueueGuard<M::T, <Self::Guard as QueueStateGuard<'g>>::Out>) -> U,
167+
>(
168+
&'a mut self,
169+
f: F,
170+
) -> U
171+
where
172+
'a: 'g,
173+
{
174+
f(self.acquire())
175+
}
176+
}

crates/virtio-queue/src/lib.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub mod defs;
1919
#[cfg(any(test, feature = "test-utils"))]
2020
pub mod mock;
2121

22+
pub(crate) mod generic;
23+
2224
use std::cmp::min;
2325
use std::convert::TryFrom;
2426
use std::fmt::{self, Debug, Display};
@@ -1187,7 +1189,7 @@ mod tests {
11871189
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
11881190
let vq = MockSplitQueue::new(m, 16);
11891191

1190-
let mut q = vq.create_queue(m);
1192+
let mut q: Queue<_> = vq.as_queue(m);
11911193

11921194
// q is currently valid
11931195
assert!(q.is_valid());
@@ -1299,7 +1301,7 @@ mod tests {
12991301
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
13001302
let vq = MockSplitQueue::new(m, 16);
13011303

1302-
let mut q = vq.create_queue(m);
1304+
let mut q: Queue<_> = vq.as_queue(m);
13031305

13041306
// q is currently valid
13051307
assert!(q.is_valid());
@@ -1361,7 +1363,7 @@ mod tests {
13611363
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
13621364
let vq = MockSplitQueue::new(m, 16);
13631365

1364-
let mut q = vq.create_queue(m);
1366+
let mut q: Queue<_> = vq.as_queue(m);
13651367

13661368
assert_eq!(vq.used().idx().load(), 0);
13671369

@@ -1398,7 +1400,7 @@ mod tests {
13981400
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
13991401
let vq = MockSplitQueue::new(m, 16);
14001402

1401-
let mut q = vq.create_queue(m);
1403+
let mut q: Queue<_> = vq.as_queue(m);
14021404
q.state.size = 8;
14031405
q.state.ready = true;
14041406
q.state.reset();
@@ -1412,7 +1414,7 @@ mod tests {
14121414
let qsize = 16;
14131415
let vq = MockSplitQueue::new(m, qsize);
14141416

1415-
let mut q = vq.create_queue(m);
1417+
let mut q: Queue<_> = vq.as_queue(m);
14161418
let avail_addr = vq.avail_addr();
14171419

14181420
// It should always return true when EVENT_IDX isn't enabled.
@@ -1457,7 +1459,7 @@ mod tests {
14571459
let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
14581460
let vq = MockSplitQueue::new(m, 16);
14591461

1460-
let mut q = vq.create_queue(m);
1462+
let mut q: Queue<_> = vq.as_queue(m);
14611463
let used_addr = vq.used_addr();
14621464

14631465
assert_eq!(q.state.event_idx_enabled, false);

crates/virtio-queue/src/mock.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use vm_memory::{
1212
};
1313

1414
use crate::defs::{VIRTQ_DESC_F_INDIRECT, VIRTQ_DESC_F_NEXT};
15-
use crate::{Descriptor, Queue};
15+
use crate::generic::QueueT;
16+
use crate::{Descriptor, Queue, QueueState};
1617

1718
/// Wrapper struct used for accesing a particular address of a GuestMemory area.
1819
pub struct Ref<'a, M, T> {
@@ -364,16 +365,20 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> {
364365
self.update_avail_idx(head_idx);
365366
}
366367

368+
/// Return a QueueT implementation for this queue.
369+
pub(crate) fn as_queue<A: GuestAddressSpace, T: QueueT<A>>(&self, a: A) -> T {
370+
let mut q = QueueState::new(self.len);
371+
q.size = self.len;
372+
q.ready = true;
373+
q.desc_table = self.desc_table_addr;
374+
q.avail_ring = self.avail_addr;
375+
q.used_ring = self.used_addr;
376+
QueueT::construct(a, q)
377+
}
378+
367379
/// Creates a new `Queue`, using the underlying memory regions represented
368380
/// by the `MockSplitQueue`.
369381
pub fn create_queue<A: GuestAddressSpace>(&self, a: A) -> Queue<A> {
370-
let mut q = Queue::<A>::new(a, self.len);
371-
372-
q.state.size = self.len;
373-
q.state.ready = true;
374-
q.state.desc_table = self.desc_table_addr;
375-
q.state.avail_ring = self.avail_addr;
376-
q.state.used_ring = self.used_addr;
377-
q
382+
self.as_queue(a)
378383
}
379384
}

0 commit comments

Comments
 (0)