Skip to content

Commit 1116b24

Browse files
committed
tests: switch to QueueT
Make all accesses to the underlying QueueState go through the with() function, which will take care of locking when testing QueueSync. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 670de1b commit 1116b24

File tree

2 files changed

+152
-84
lines changed

2 files changed

+152
-84
lines changed

crates/virtio-queue/src/generic.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
use std::num::Wrapping;
66
use std::ops::DerefMut;
77

8-
use crate::{Error, Queue, QueueGuard, QueueState};
8+
use crate::{Error, Queue, QueueGuard, QueueState, QueueSync};
99
use std::sync::atomic::Ordering;
10+
use std::sync::{Arc, Mutex, MutexGuard};
1011
use vm_memory::GuestAddressSpace;
1112

1213
/// Lifetime-generic guard associated to a QueueStateT. In practice,
@@ -174,3 +175,32 @@ impl<M: GuestAddressSpace> QueueT<M> for Queue<M> {
174175
f(self.acquire())
175176
}
176177
}
178+
179+
impl<'g, M: GuestAddressSpace> QueueStateGuard<'g> for QueueSync<M> {
180+
type Out = MutexGuard<'g, QueueState>;
181+
}
182+
183+
impl<M: GuestAddressSpace> QueueT<M> for QueueSync<M> {
184+
type Guard = Self;
185+
186+
fn construct(mem: M, state: QueueState) -> Self {
187+
QueueSync {
188+
mem,
189+
state: Arc::new(Mutex::new(state)),
190+
}
191+
}
192+
fn with<
193+
'a,
194+
'g,
195+
U,
196+
F: FnOnce(QueueGuard<M::T, <Self::Guard as QueueStateGuard<'g>>::Out>) -> U,
197+
>(
198+
&'a mut self,
199+
f: F,
200+
) -> U
201+
where
202+
'a: 'g,
203+
{
204+
f(self.lock())
205+
}
206+
}

crates/virtio-queue/src/lib.rs

Lines changed: 121 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use std::mem::size_of;
2929
use std::num::Wrapping;
3030
use std::ops::{Deref, DerefMut};
3131
use std::sync::atomic::{fence, Ordering};
32+
use std::sync::{Arc, Mutex, MutexGuard};
3233

3334
use log::error;
3435
use vm_memory::{
@@ -1028,9 +1029,37 @@ impl<M: GuestAddressSpace> Queue<M> {
10281029
}
10291030
}
10301031

1032+
/// A convenient wrapper struct for a thread-safe virtio queue, with associated GuestMemory object.
1033+
#[derive(Clone, Debug)]
1034+
pub struct QueueSync<M: GuestAddressSpace> {
1035+
/// Guest memory object associated with the queue.
1036+
pub mem: M,
1037+
/// Virtio queue state.
1038+
pub state: Arc<Mutex<QueueState>>,
1039+
}
1040+
1041+
impl<M: GuestAddressSpace> QueueSync<M> {
1042+
/// Construct an empty virtio queue with the given `max_size`.
1043+
pub fn new(mem: M, max_size: u16) -> Self {
1044+
QueueSync {
1045+
mem,
1046+
state: Arc::new(Mutex::new(QueueState::new(max_size))),
1047+
}
1048+
}
1049+
1050+
/// Get an exclusive reference to the underlying `QueueState` object.
1051+
///
1052+
/// Logically this method will acquire the underlying lock protecting the `QueueState`
1053+
/// object. The lock will be released when the returned object gets dropped.
1054+
pub fn lock(&self) -> QueueGuard<M::T, MutexGuard<QueueState>> {
1055+
QueueGuard::new(self.mem.memory(), self.state.lock().unwrap())
1056+
}
1057+
}
1058+
10311059
#[cfg(test)]
10321060
mod tests {
10331061
use super::*;
1062+
use generic::QueueT;
10341063
use memoffset::offset_of;
10351064
use mock::{DescriptorTable, MockSplitQueue};
10361065

@@ -1193,72 +1222,74 @@ mod tests {
11931222
assert!(q.is_valid());
11941223

11951224
// shouldn't be valid when not marked as ready
1196-
q.state.ready = false;
1225+
q.with(|mut qstate| qstate.ready = false);
11971226
assert!(!q.is_valid());
1198-
q.state.ready = true;
1227+
q.with(|mut qstate| qstate.ready = true);
1228+
1229+
let max_size = q.with(|qstate| qstate.max_size);
11991230

12001231
// or when size > max_size
1201-
q.state.size = q.state.max_size << 1;
1232+
q.with(|mut qstate| qstate.size = max_size << 1);
12021233
assert!(!q.is_valid());
1203-
q.state.size = q.state.max_size;
1234+
q.with(|mut qstate| qstate.size = max_size);
12041235

12051236
// or when size is 0
1206-
q.state.size = 0;
1237+
q.with(|mut qstate| qstate.size = 0);
12071238
assert!(!q.is_valid());
1208-
q.state.size = q.state.max_size;
1239+
q.with(|mut qstate| qstate.size = max_size);
12091240

12101241
// or when size is not a power of 2
1211-
q.state.size = 11;
1242+
q.with(|mut qstate| qstate.size = 11);
12121243
assert!(!q.is_valid());
1213-
q.state.size = q.state.max_size;
1244+
q.with(|mut qstate| qstate.size = max_size);
12141245

12151246
// or if the various addresses are off
12161247

1217-
q.state.desc_table = GuestAddress(0xffff_ffff);
1248+
q.with(|mut qstate| qstate.desc_table = GuestAddress(0xffff_ffff));
12181249
assert!(!q.is_valid());
1219-
q.state.desc_table = GuestAddress(0x1001);
1250+
q.with(|mut qstate| qstate.desc_table = GuestAddress(0x1001));
12201251
assert!(!q.is_valid());
1221-
q.state.desc_table = vq.desc_table_addr();
1252+
q.with(|mut qstate| qstate.desc_table = vq.desc_table_addr());
12221253

1223-
q.state.avail_ring = GuestAddress(0xffff_ffff);
1254+
q.with(|mut qstate| qstate.avail_ring = GuestAddress(0xffff_ffff));
12241255
assert!(!q.is_valid());
1225-
q.state.avail_ring = GuestAddress(0x1001);
1256+
q.with(|mut qstate| qstate.avail_ring = GuestAddress(0x1001));
12261257
assert!(!q.is_valid());
1227-
q.state.avail_ring = vq.avail_addr();
1258+
q.with(|mut qstate| qstate.avail_ring = vq.avail_addr());
12281259

1229-
q.state.used_ring = GuestAddress(0xffff_ffff);
1260+
q.with(|mut qstate| qstate.used_ring = GuestAddress(0xffff_ffff));
12301261
assert!(!q.is_valid());
1231-
q.state.used_ring = GuestAddress(0x1001);
1262+
q.with(|mut qstate| qstate.used_ring = GuestAddress(0x1001));
12321263
assert!(!q.is_valid());
1233-
q.state.used_ring = vq.used_addr();
1264+
q.with(|mut qstate| qstate.used_ring = vq.used_addr());
12341265

1235-
{
1266+
q.with(|mut qstate| {
12361267
// an invalid queue should return an iterator with no next
1237-
q.state.ready = false;
1238-
let mut i = q.iter().unwrap();
1268+
qstate.ready = false;
1269+
let mut i = qstate.iter().unwrap();
12391270
assert!(i.next().is_none());
1240-
}
1271+
});
12411272

1242-
q.state.ready = true;
1273+
q.with(|mut qstate| qstate.ready = true);
12431274

12441275
// now let's create two simple descriptor chains
12451276
// the chains are (0, 1) and (2, 3, 4)
1246-
{
1247-
for j in 0..5u16 {
1248-
let flags = match j {
1249-
1 | 4 => 0,
1250-
_ => VIRTQ_DESC_F_NEXT,
1251-
};
1252-
1253-
let desc = Descriptor::new((0x1000 * (j + 1)) as u64, 0x1000, flags, j + 1);
1254-
vq.desc_table().store(j, desc);
1255-
}
1277+
for j in 0..5u16 {
1278+
let flags = match j {
1279+
1 | 4 => 0,
1280+
_ => VIRTQ_DESC_F_NEXT,
1281+
};
12561282

1257-
vq.avail().ring().ref_at(0).store(0);
1258-
vq.avail().ring().ref_at(1).store(2);
1259-
vq.avail().idx().store(2);
1283+
let desc = Descriptor::new((0x1000 * (j + 1)) as u64, 0x1000, flags, j + 1);
1284+
vq.desc_table().store(j, desc);
1285+
}
1286+
1287+
vq.avail().ring().ref_at(0).store(0);
1288+
vq.avail().ring().ref_at(1).store(2);
1289+
vq.avail().idx().store(2);
12601290

1261-
let mut i = q.iter().unwrap();
1291+
q.with(|mut qstate| {
1292+
let mut i = qstate.iter().unwrap();
12621293

12631294
{
12641295
let mut c = i.next().unwrap();
@@ -1285,13 +1316,13 @@ mod tests {
12851316
{
12861317
assert!(i.next().is_none());
12871318
i.go_to_previous_position();
1288-
let mut c = q.iter().unwrap().next().unwrap();
1319+
let mut c = qstate.iter().unwrap().next().unwrap();
12891320
c.next().unwrap();
12901321
c.next().unwrap();
12911322
c.next().unwrap();
12921323
assert!(c.next().is_none());
12931324
}
1294-
}
1325+
})
12951326
}
12961327

12971328
#[test]
@@ -1321,39 +1352,41 @@ mod tests {
13211352
vq.avail().ring().ref_at(2).store(5);
13221353
vq.avail().idx().store(3);
13231354

1324-
let mut i = q.iter().unwrap();
1355+
q.with(|mut qstate| {
1356+
let mut i = qstate.iter().unwrap();
13251357

1326-
{
1327-
let c = i.next().unwrap();
1328-
assert_eq!(c.head_index(), 0);
1329-
1330-
let mut iter = c;
1331-
assert!(iter.next().is_some());
1332-
assert!(iter.next().is_some());
1333-
assert!(iter.next().is_none());
1334-
assert!(iter.next().is_none());
1335-
}
1358+
{
1359+
let c = i.next().unwrap();
1360+
assert_eq!(c.head_index(), 0);
13361361

1337-
{
1338-
let c = i.next().unwrap();
1339-
assert_eq!(c.head_index(), 2);
1340-
1341-
let mut iter = c.writable();
1342-
assert!(iter.next().is_some());
1343-
assert!(iter.next().is_some());
1344-
assert!(iter.next().is_none());
1345-
assert!(iter.next().is_none());
1346-
}
1362+
let mut iter = c;
1363+
assert!(iter.next().is_some());
1364+
assert!(iter.next().is_some());
1365+
assert!(iter.next().is_none());
1366+
assert!(iter.next().is_none());
1367+
}
13471368

1348-
{
1349-
let c = i.next().unwrap();
1350-
assert_eq!(c.head_index(), 5);
1369+
{
1370+
let c = i.next().unwrap();
1371+
assert_eq!(c.head_index(), 2);
13511372

1352-
let mut iter = c.readable();
1353-
assert!(iter.next().is_some());
1354-
assert!(iter.next().is_none());
1355-
assert!(iter.next().is_none());
1356-
}
1373+
let mut iter = c.writable();
1374+
assert!(iter.next().is_some());
1375+
assert!(iter.next().is_some());
1376+
assert!(iter.next().is_none());
1377+
assert!(iter.next().is_none());
1378+
}
1379+
1380+
{
1381+
let c = i.next().unwrap();
1382+
assert_eq!(c.head_index(), 5);
1383+
1384+
let mut iter = c.readable();
1385+
assert!(iter.next().is_some());
1386+
assert!(iter.next().is_none());
1387+
assert!(iter.next().is_none());
1388+
}
1389+
})
13571390
}
13581391

13591392
#[test]
@@ -1371,7 +1404,7 @@ mod tests {
13711404

13721405
// should be ok
13731406
q.add_used(1, 0x1000).unwrap();
1374-
assert_eq!(q.state.next_used, Wrapping(1));
1407+
q.with(|qstate| assert_eq!(qstate.next_used, Wrapping(1)));
13751408
assert_eq!(vq.used().idx().load(), 1);
13761409

13771410
let x = vq.used().ring().ref_at(0).load();
@@ -1399,11 +1432,13 @@ mod tests {
13991432
let vq = MockSplitQueue::new(m, 16);
14001433

14011434
let mut q: Queue<_> = vq.as_queue(m);
1402-
q.state.size = 8;
1403-
q.state.ready = true;
1404-
q.state.reset();
1405-
assert_eq!(q.state.size, 16);
1406-
assert_eq!(q.state.ready, false);
1435+
q.with(|mut qstate| {
1436+
qstate.size = 8;
1437+
qstate.ready = true;
1438+
qstate.reset();
1439+
assert_eq!(qstate.size, 16);
1440+
assert_eq!(qstate.ready, false);
1441+
})
14071442
}
14081443

14091444
#[test]
@@ -1417,21 +1452,24 @@ mod tests {
14171452

14181453
// It should always return true when EVENT_IDX isn't enabled.
14191454
for i in 0..qsize {
1420-
q.state.next_used = Wrapping(i);
1455+
q.with(|mut qstate| qstate.next_used = Wrapping(i));
14211456
assert_eq!(q.needs_notification().unwrap(), true);
14221457
}
14231458

14241459
m.write_obj::<u16>(4, avail_addr.unchecked_add(4 + qsize as u64 * 2))
14251460
.unwrap();
1426-
q.state.set_event_idx(true);
1461+
q.with(|mut qstate| qstate.set_event_idx(true));
14271462

14281463
// Incrementing up to this value causes an `u16` to wrap back to 0.
14291464
let wrap = u32::from(u16::MAX) + 1;
14301465

14311466
for i in 0..wrap + 12 {
1432-
q.state.next_used = Wrapping(i as u16);
1433-
// Let's test wrapping around the maximum index value as well.
1434-
let expected = i == 5 || i == (5 + wrap) || q.state.signalled_used.is_none();
1467+
let expected = q.with(|mut qstate| {
1468+
qstate.next_used = Wrapping(i as u16);
1469+
// Let's test wrapping around the maximum index value as well.
1470+
i == 5 || i == (5 + wrap) || qstate.signalled_used.is_none()
1471+
});
1472+
14351473
assert_eq!(q.needs_notification().unwrap(), expected);
14361474
}
14371475

@@ -1445,9 +1483,9 @@ mod tests {
14451483
.unwrap();
14461484

14471485
assert_eq!(q.needs_notification().unwrap(), false);
1448-
q.state.next_used = Wrapping(15);
1486+
q.with(|mut qstate| qstate.next_used = Wrapping(15));
14491487
assert_eq!(q.needs_notification().unwrap(), false);
1450-
q.state.next_used = Wrapping(0);
1488+
q.with(|mut qstate| qstate.next_used = Wrapping(0));
14511489
assert_eq!(q.needs_notification().unwrap(), true);
14521490
assert_eq!(q.needs_notification().unwrap(), false);
14531491
}
@@ -1460,7 +1498,7 @@ mod tests {
14601498
let mut q: Queue<_> = vq.as_queue(m);
14611499
let used_addr = vq.used_addr();
14621500

1463-
assert_eq!(q.state.event_idx_enabled, false);
1501+
q.with(|qstate| assert_eq!(qstate.event_idx_enabled, false));
14641502

14651503
q.enable_notification().unwrap();
14661504
let v = m.read_obj::<u16>(used_addr).unwrap();
@@ -1479,13 +1517,13 @@ mod tests {
14791517
m.write_obj::<u16>(2, avail_addr.unchecked_add(2)).unwrap();
14801518

14811519
assert_eq!(q.enable_notification().unwrap(), true);
1482-
q.state.next_avail = Wrapping(2);
1520+
q.with(|mut qstate| qstate.next_avail = Wrapping(2));
14831521
assert_eq!(q.enable_notification().unwrap(), false);
14841522

14851523
m.write_obj::<u16>(8, avail_addr.unchecked_add(2)).unwrap();
14861524

14871525
assert_eq!(q.enable_notification().unwrap(), true);
1488-
q.state.next_avail = Wrapping(8);
1526+
q.with(|mut qstate| qstate.next_avail = Wrapping(8));
14891527
assert_eq!(q.enable_notification().unwrap(), false);
14901528
}
14911529
}

0 commit comments

Comments
 (0)