Skip to content

Commit dadd9cc

Browse files
committed
devices/vsock: device unit tests
Added unit tests for: - the vsock virtio device implementation - the vsock epoll handler - the vsock packet handling interface Signed-off-by: Dan Horobeanu <[email protected]>
1 parent 249576b commit dadd9cc

File tree

6 files changed

+828
-1
lines changed

6 files changed

+828
-1
lines changed

devices/src/virtio/vsock/device.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,96 @@ where
224224
Ok(())
225225
}
226226
}
227+
228+
#[cfg(test)]
229+
mod tests {
230+
use crate::virtio::vsock::defs::uapi;
231+
232+
use super::super::tests::TestContext;
233+
use super::*;
234+
235+
#[test]
236+
fn test_virtio_device() {
237+
let mut ctx = TestContext::new();
238+
let device_features = AVAIL_FEATURES;
239+
let driver_features: u64 = AVAIL_FEATURES | 1 | (1 << 32);
240+
let device_pages = [
241+
(device_features & 0xffff_ffff) as u32,
242+
(device_features >> 32) as u32,
243+
];
244+
let driver_pages = [
245+
(driver_features & 0xffff_ffff) as u32,
246+
(driver_features >> 32) as u32,
247+
];
248+
assert_eq!(ctx.device.device_type(), uapi::VIRTIO_ID_VSOCK);
249+
assert_eq!(ctx.device.queue_max_sizes(), defs::QUEUE_SIZES);
250+
assert_eq!(ctx.device.features(0), device_pages[0]);
251+
assert_eq!(ctx.device.features(1), device_pages[1]);
252+
assert_eq!(ctx.device.features(2), 0);
253+
254+
ctx.device.ack_features(0, driver_pages[0]);
255+
ctx.device.ack_features(1, driver_pages[1]);
256+
ctx.device.ack_features(2, 0);
257+
ctx.device.ack_features(0, !driver_pages[0]);
258+
assert_eq!(ctx.device.acked_features, device_features & driver_features);
259+
260+
// Test reading 32-bit chunks.
261+
let mut data = [0u8; 8];
262+
ctx.device.read_config(0, &mut data[..4]);
263+
assert_eq!(
264+
u64::from(LittleEndian::read_u32(&data)),
265+
ctx.cid & 0xffff_ffff
266+
);
267+
ctx.device.read_config(4, &mut data[4..]);
268+
assert_eq!(
269+
u64::from(LittleEndian::read_u32(&data[4..])),
270+
(ctx.cid >> 32) & 0xffff_ffff
271+
);
272+
273+
// Test reading 64-bit.
274+
let mut data = [0u8; 8];
275+
ctx.device.read_config(0, &mut data);
276+
assert_eq!(LittleEndian::read_u64(&data), ctx.cid);
277+
278+
// Check that out-of-bounds reading doesn't mutate the destination buffer.
279+
let mut data = [0u8, 1, 2, 3, 4, 5, 6, 7];
280+
ctx.device.read_config(2, &mut data);
281+
assert_eq!(data, [0u8, 1, 2, 3, 4, 5, 6, 7]);
282+
283+
// Just covering lines here.
284+
ctx.device.write_config(0, &data[..4]);
285+
286+
// Test a bad activation.
287+
let bad_activate = ctx.device.activate(
288+
ctx.mem.clone(),
289+
EventFd::new().unwrap(),
290+
Arc::new(AtomicUsize::new(0)),
291+
Vec::new(),
292+
Vec::new(),
293+
);
294+
match bad_activate {
295+
Err(ActivateError::BadActivate) => (),
296+
other => panic!("{:?}", other),
297+
}
298+
299+
// Test a correct activation.
300+
ctx.device
301+
.activate(
302+
ctx.mem.clone(),
303+
EventFd::new().unwrap(),
304+
Arc::new(AtomicUsize::new(0)),
305+
vec![
306+
VirtQueue::new(256),
307+
VirtQueue::new(256),
308+
VirtQueue::new(256),
309+
],
310+
vec![
311+
EventFd::new().unwrap(),
312+
EventFd::new().unwrap(),
313+
EventFd::new().unwrap(),
314+
],
315+
)
316+
.unwrap();
317+
}
318+
319+
}

devices/src/virtio/vsock/epoll_handler.rs

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,244 @@ where
220220
Ok(())
221221
}
222222
}
223+
224+
#[cfg(test)]
225+
mod tests {
226+
use super::super::tests::TestContext;
227+
use super::super::*;
228+
use super::*;
229+
use crate::virtio::vsock::defs::{BACKEND_EVENT, EVQ_EVENT, RXQ_EVENT, TXQ_EVENT};
230+
231+
#[test]
232+
fn test_irq() {
233+
// Test case: successful IRQ signaling.
234+
{
235+
let test_ctx = TestContext::new();
236+
let ctx = test_ctx.create_epoll_handler_context();
237+
238+
ctx.handler.signal_used_queue().unwrap();
239+
assert_eq!(
240+
ctx.handler.interrupt_status.load(Ordering::SeqCst),
241+
VIRTIO_MMIO_INT_VRING as usize
242+
);
243+
assert_eq!(ctx.handler.interrupt_evt.read().unwrap(), 1);
244+
}
245+
246+
// Test case: error (a real stretch) - the event counter is full.
247+
//
248+
{
249+
let test_ctx = TestContext::new();
250+
let ctx = test_ctx.create_epoll_handler_context();
251+
252+
ctx.handler.interrupt_evt.write(std::u64::MAX - 1).unwrap();
253+
match ctx.handler.signal_used_queue() {
254+
Err(DeviceError::FailedSignalingUsedQueue(_)) => (),
255+
other => panic!("{:?}", other),
256+
}
257+
}
258+
}
259+
260+
#[test]
261+
fn test_txq_event() {
262+
// Test case:
263+
// - the driver has something to send (there's data in the TX queue); and
264+
// - the backend has no pending RX data.
265+
{
266+
let test_ctx = TestContext::new();
267+
let mut ctx = test_ctx.create_epoll_handler_context();
268+
269+
ctx.handler.backend.set_pending_rx(false);
270+
ctx.signal_txq_event();
271+
272+
// The available TX descriptor should have been used.
273+
assert_eq!(ctx.guest_txvq.used.idx.get(), 1);
274+
// The available RX descriptor should be untouched.
275+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 0);
276+
}
277+
278+
// Test case:
279+
// - the driver has something to send (there's data in the TX queue); and
280+
// - the backend also has some pending RX data.
281+
{
282+
let test_ctx = TestContext::new();
283+
let mut ctx = test_ctx.create_epoll_handler_context();
284+
285+
ctx.handler.backend.set_pending_rx(true);
286+
ctx.signal_txq_event();
287+
288+
// Both available RX and TX descriptors should have been used.
289+
assert_eq!(ctx.guest_txvq.used.idx.get(), 1);
290+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 1);
291+
}
292+
293+
// Test case:
294+
// - the driver has something to send (there's data in the TX queue); and
295+
// - the backend errors out and cannot process the TX queue.
296+
{
297+
let test_ctx = TestContext::new();
298+
let mut ctx = test_ctx.create_epoll_handler_context();
299+
300+
ctx.handler.backend.set_pending_rx(false);
301+
ctx.handler.backend.set_tx_err(Some(VsockError::NoData));
302+
ctx.signal_txq_event();
303+
304+
// Both RX and TX queues should be untouched.
305+
assert_eq!(ctx.guest_txvq.used.idx.get(), 0);
306+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 0);
307+
}
308+
309+
// Test case:
310+
// - the driver supplied a malformed TX buffer.
311+
{
312+
let test_ctx = TestContext::new();
313+
let mut ctx = test_ctx.create_epoll_handler_context();
314+
315+
// Invalidate the packet header descriptor, by setting its length to 0.
316+
ctx.guest_txvq.dtable[0].len.set(0);
317+
ctx.signal_txq_event();
318+
319+
// The available descriptor should have been consumed, but no packet should have
320+
// reached the backend.
321+
assert_eq!(ctx.guest_txvq.used.idx.get(), 1);
322+
assert_eq!(ctx.handler.backend.tx_ok_cnt, 0);
323+
}
324+
325+
// Test case: spurious TXQ_EVENT.
326+
{
327+
let test_ctx = TestContext::new();
328+
let mut ctx = test_ctx.create_epoll_handler_context();
329+
330+
match ctx.handler.handle_event(TXQ_EVENT, epoll::Events::EPOLLIN) {
331+
Err(DeviceError::FailedReadingQueue { .. }) => (),
332+
other => panic!("{:?}", other),
333+
}
334+
}
335+
}
336+
337+
#[test]
338+
fn test_rxq_event() {
339+
// Test case:
340+
// - there is pending RX data in the backend; and
341+
// - the driver makes RX buffers available; and
342+
// - the backend successfully places its RX data into the queue.
343+
{
344+
let test_ctx = TestContext::new();
345+
let mut ctx = test_ctx.create_epoll_handler_context();
346+
347+
ctx.handler.backend.set_pending_rx(true);
348+
ctx.handler.backend.set_rx_err(Some(VsockError::NoData));
349+
ctx.signal_rxq_event();
350+
351+
// The available RX buffer should've been left untouched.
352+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 0);
353+
}
354+
355+
// Test case:
356+
// - there is pending RX data in the backend; and
357+
// - the driver makes RX buffers available; and
358+
// - the backend errors out, when attempting to receive data.
359+
{
360+
let test_ctx = TestContext::new();
361+
let mut ctx = test_ctx.create_epoll_handler_context();
362+
363+
ctx.handler.backend.set_pending_rx(true);
364+
ctx.signal_rxq_event();
365+
366+
// The available RX buffer should have been used.
367+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 1);
368+
}
369+
370+
// Test case: the driver provided a malformed RX descriptor chain.
371+
{
372+
let test_ctx = TestContext::new();
373+
let mut ctx = test_ctx.create_epoll_handler_context();
374+
375+
// Invalidate the packet header descriptor, by setting its length to 0.
376+
ctx.guest_rxvq.dtable[0].len.set(0);
377+
378+
// The chain should've been processed, without employing the backend.
379+
assert_eq!(ctx.handler.process_rx(), true);
380+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 1);
381+
assert_eq!(ctx.handler.backend.rx_ok_cnt, 0);
382+
}
383+
384+
// Test case: spurious RXQ_EVENT.
385+
{
386+
let test_ctx = TestContext::new();
387+
let mut ctx = test_ctx.create_epoll_handler_context();
388+
ctx.handler.backend.set_pending_rx(false);
389+
match ctx.handler.handle_event(RXQ_EVENT, epoll::Events::EPOLLIN) {
390+
Err(DeviceError::FailedReadingQueue { .. }) => (),
391+
other => panic!("{:?}", other),
392+
}
393+
}
394+
}
395+
396+
#[test]
397+
fn test_evq_event() {
398+
// Test case: spurious EVQ_EVENT.
399+
{
400+
let test_ctx = TestContext::new();
401+
let mut ctx = test_ctx.create_epoll_handler_context();
402+
ctx.handler.backend.set_pending_rx(false);
403+
match ctx.handler.handle_event(EVQ_EVENT, epoll::Events::EPOLLIN) {
404+
Err(DeviceError::FailedReadingQueue { .. }) => (),
405+
other => panic!("{:?}", other),
406+
}
407+
}
408+
}
409+
410+
#[test]
411+
fn test_backend_event() {
412+
// Test case:
413+
// - a backend event is received; and
414+
// - the backend has pending RX data.
415+
{
416+
let test_ctx = TestContext::new();
417+
let mut ctx = test_ctx.create_epoll_handler_context();
418+
419+
ctx.handler.backend.set_pending_rx(true);
420+
ctx.handler
421+
.handle_event(BACKEND_EVENT, epoll::Events::EPOLLIN)
422+
.unwrap();
423+
424+
// The backend should've received this event.
425+
assert_eq!(ctx.handler.backend.evset, Some(epoll::Events::EPOLLIN));
426+
// TX queue processing should've been triggered.
427+
assert_eq!(ctx.guest_txvq.used.idx.get(), 1);
428+
// RX queue processing should've been triggered.
429+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 1);
430+
}
431+
432+
// Test case:
433+
// - a backend event is received; and
434+
// - the backend doesn't have any pending RX data.
435+
{
436+
let test_ctx = TestContext::new();
437+
let mut ctx = test_ctx.create_epoll_handler_context();
438+
439+
ctx.handler.backend.set_pending_rx(false);
440+
ctx.handler
441+
.handle_event(BACKEND_EVENT, epoll::Events::EPOLLIN)
442+
.unwrap();
443+
444+
// The backend should've received this event.
445+
assert_eq!(ctx.handler.backend.evset, Some(epoll::Events::EPOLLIN));
446+
// TX queue processing should've been triggered.
447+
assert_eq!(ctx.guest_txvq.used.idx.get(), 1);
448+
// The RX queue should've been left untouched.
449+
assert_eq!(ctx.guest_rxvq.used.idx.get(), 0);
450+
}
451+
}
452+
453+
#[test]
454+
fn test_unknown_event() {
455+
let test_ctx = TestContext::new();
456+
let mut ctx = test_ctx.create_epoll_handler_context();
457+
458+
match ctx.handler.handle_event(0xff, epoll::Events::EPOLLIN) {
459+
Err(DeviceError::UnknownEvent { .. }) => (),
460+
other => panic!("{:?}", other),
461+
}
462+
}
463+
}

0 commit comments

Comments
 (0)