Skip to content

Commit 7f94f6a

Browse files
committed
chore: add unit tests for virtio-net with VIRTIO_NET_F_MRG_RXBUF
Add new unit tests for virtio-net with enabled VIRTIO_NET_F_MRG_RXBUF feature. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 6b80c11 commit 7f94f6a

File tree

2 files changed

+175
-3
lines changed

2 files changed

+175
-3
lines changed

src/vmm/src/devices/virtio/net/device.rs

Lines changed: 165 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,8 +1048,8 @@ pub mod tests {
10481048
};
10491049
use crate::devices::virtio::net::test_utils::test::TestHelper;
10501050
use crate::devices::virtio::net::test_utils::{
1051-
default_net, if_index, inject_tap_tx_frame, set_mac, NetEvent, NetQueue, ReadTapMock,
1052-
TapTrafficSimulator, WriteTapMock,
1051+
default_net, if_index, inject_tap_tx_frame, mock_frame_set_num_buffers, set_mac, NetEvent,
1052+
NetQueue, ReadTapMock, TapTrafficSimulator, WriteTapMock,
10531053
};
10541054
use crate::devices::virtio::net::NET_QUEUE_SIZES;
10551055
use crate::devices::virtio::queue::VIRTQ_DESC_F_WRITE;
@@ -1290,7 +1290,40 @@ pub mod tests {
12901290
}
12911291

12921292
#[test]
1293-
fn test_rx_partial_write() {
1293+
fn test_rx_mrg_buf_multiple_short_writable_descriptors() {
1294+
let mut th = TestHelper::get_default();
1295+
th.activate_net();
1296+
th.net().tap.mocks.set_read_tap(ReadTapMock::TapFrame);
1297+
1298+
// VIRTIO_NET_F_MRG_RXBUF is not enabled by default
1299+
th.net().acked_features = 1 << VIRTIO_NET_F_MRG_RXBUF;
1300+
1301+
th.add_desc_chain(NetQueue::Rx, 0, &[(0, 500, VIRTQ_DESC_F_WRITE)]);
1302+
th.add_desc_chain(NetQueue::Rx, 500, &[(1, 500, VIRTQ_DESC_F_WRITE)]);
1303+
1304+
// There will be 2 heads used.
1305+
let mut frame = inject_tap_tx_frame(&th.net(), 1000);
1306+
mock_frame_set_num_buffers(&mut frame, 2);
1307+
1308+
check_metric_after_block!(
1309+
th.net().metrics.rx_packets_count,
1310+
1,
1311+
th.event_manager.run_with_timeout(100).unwrap()
1312+
);
1313+
1314+
assert_eq!(th.rxq.used.idx.get(), 2);
1315+
assert!(th.net().irq_trigger.has_pending_irq(IrqType::Vring));
1316+
assert!(!th.net().rx_deferred_frame);
1317+
1318+
th.rxq.check_used_elem(0, 0, 500);
1319+
th.rxq.check_used_elem(1, 1, 500);
1320+
1321+
th.rxq.dtable[0].check_data(&frame[..500]);
1322+
th.rxq.dtable[1].check_data(&frame[500..]);
1323+
}
1324+
1325+
#[test]
1326+
fn test_rx_invalid_desc_chain() {
12941327
let mut th = TestHelper::get_default();
12951328
th.activate_net();
12961329

@@ -1312,6 +1345,79 @@ pub mod tests {
13121345
th.check_rx_queue_resume(&frame);
13131346
}
13141347

1348+
#[test]
1349+
fn test_rx_mrg_buf_invalid_desc_chain() {
1350+
let mut th = TestHelper::get_default();
1351+
th.activate_net();
1352+
1353+
// VIRTIO_NET_F_MRG_RXBUF is not enabled by default
1354+
th.net().acked_features = 1 << VIRTIO_NET_F_MRG_RXBUF;
1355+
1356+
// The descriptor chain is created so that the last descriptor doesn't fit in the
1357+
// guest memory.
1358+
let offset = th.mem.last_addr().raw_value() - th.data_addr() - 300;
1359+
th.add_desc_chain(
1360+
NetQueue::Rx,
1361+
offset,
1362+
&[
1363+
(0, 100, VIRTQ_DESC_F_WRITE),
1364+
(1, 50, VIRTQ_DESC_F_WRITE),
1365+
(2, 4096, VIRTQ_DESC_F_WRITE),
1366+
],
1367+
);
1368+
let frame = th.check_rx_deferred_frame(1000);
1369+
th.rxq.check_used_elem(0, 0, 0);
1370+
1371+
th.check_rx_queue_resume(&frame);
1372+
}
1373+
1374+
#[test]
1375+
fn test_rx_mrg_buf_partial_write() {
1376+
let mut th = TestHelper::get_default();
1377+
th.activate_net();
1378+
th.net().tap.mocks.set_read_tap(ReadTapMock::TapFrame);
1379+
1380+
// VIRTIO_NET_F_MRG_RXBUF is not enabled by default
1381+
th.net().acked_features = 1 << VIRTIO_NET_F_MRG_RXBUF;
1382+
1383+
// Add descriptor that is not big enough to store the
1384+
// whole packet.
1385+
th.add_desc_chain(NetQueue::Rx, 0, &[(0, 500, VIRTQ_DESC_F_WRITE)]);
1386+
1387+
// There will be 2 heads used.
1388+
let mut frame = inject_tap_tx_frame(&th.net(), 1000);
1389+
mock_frame_set_num_buffers(&mut frame, 2);
1390+
1391+
// For now only 1 descriptor chain is used,
1392+
// but the packet is not fully written yet.
1393+
check_metric_after_block!(
1394+
th.net().metrics.rx_packets_count,
1395+
0,
1396+
th.event_manager.run_with_timeout(100).unwrap()
1397+
);
1398+
th.rxq.check_used_elem(0, 0, 500);
1399+
1400+
// The write was converted to partial write
1401+
assert!(th.net().rx_partial_write.is_some());
1402+
1403+
// Continuing writing.
1404+
th.add_desc_chain(NetQueue::Rx, 500, &[(1, 500, VIRTQ_DESC_F_WRITE)]);
1405+
check_metric_after_block!(
1406+
th.net().metrics.rx_packets_count,
1407+
1,
1408+
th.event_manager.run_with_timeout(100).unwrap()
1409+
);
1410+
assert!(th.net().rx_partial_write.is_none());
1411+
assert_eq!(th.rxq.used.idx.get(), 2);
1412+
assert!(th.net().irq_trigger.has_pending_irq(IrqType::Vring));
1413+
1414+
th.rxq.check_used_elem(0, 0, 500);
1415+
th.rxq.check_used_elem(1, 1, 500);
1416+
1417+
th.rxq.dtable[0].check_data(&frame[..500]);
1418+
th.rxq.dtable[1].check_data(&frame[500..]);
1419+
}
1420+
13151421
#[test]
13161422
fn test_rx_retry() {
13171423
let mut th = TestHelper::get_default();
@@ -1363,6 +1469,62 @@ pub mod tests {
13631469
th.rxq.dtable[5].check_data(&frame);
13641470
}
13651471

1472+
#[test]
1473+
fn test_rx_mrg_buf_retry() {
1474+
let mut th = TestHelper::get_default();
1475+
th.activate_net();
1476+
th.net().tap.mocks.set_read_tap(ReadTapMock::TapFrame);
1477+
1478+
// VIRTIO_NET_F_MRG_RXBUF is not enabled by default
1479+
th.net().acked_features = 1 << VIRTIO_NET_F_MRG_RXBUF;
1480+
1481+
// Add invalid descriptor chain - read only descriptor.
1482+
th.add_desc_chain(
1483+
NetQueue::Rx,
1484+
0,
1485+
&[
1486+
(0, 100, VIRTQ_DESC_F_WRITE),
1487+
(1, 100, 0),
1488+
(2, 1000, VIRTQ_DESC_F_WRITE),
1489+
],
1490+
);
1491+
// Add valid descriptor chain but too short. This one will be used
1492+
// and the write will be converted to partial write.
1493+
th.add_desc_chain(NetQueue::Rx, 1200, &[(3, 100, VIRTQ_DESC_F_WRITE)]);
1494+
// Add invalid descriptor chain - invalid memory offset.
1495+
// The partial write stated with previous descriptor should halt here.
1496+
th.add_desc_chain(
1497+
NetQueue::Rx,
1498+
th.mem.last_addr().raw_value(),
1499+
&[(4, 1000, VIRTQ_DESC_F_WRITE)],
1500+
);
1501+
1502+
// Add valid descriptor chain.
1503+
th.add_desc_chain(NetQueue::Rx, 1300, &[(5, 1000, VIRTQ_DESC_F_WRITE)]);
1504+
1505+
// Inject frame to tap and run epoll.
1506+
let frame = inject_tap_tx_frame(&th.net(), 1000);
1507+
check_metric_after_block!(
1508+
th.net().metrics.rx_packets_count,
1509+
1,
1510+
th.event_manager.run_with_timeout(100).unwrap()
1511+
);
1512+
1513+
// Check that the used queue has advanced.
1514+
assert_eq!(th.rxq.used.idx.get(), 4);
1515+
assert!(&th.net().irq_trigger.has_pending_irq(IrqType::Vring));
1516+
// Check that the invalid descriptor chains have been discarded
1517+
th.rxq.check_used_elem(0, 0, 0);
1518+
th.rxq.check_used_elem(1, 3, 0);
1519+
th.rxq.check_used_elem(2, 4, 0);
1520+
// Check that the frame wasn't deferred.
1521+
assert!(!th.net().rx_deferred_frame);
1522+
// Check that the frame has been written successfully to the valid Rx descriptor chain.
1523+
th.rxq
1524+
.check_used_elem(3, 5, frame.len().try_into().unwrap());
1525+
th.rxq.dtable[5].check_data(&frame);
1526+
}
1527+
13661528
#[test]
13671529
fn test_rx_complex_desc_chain() {
13681530
let mut th = TestHelper::get_default();

src/vmm/src/devices/virtio/net/test_utils.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ pub fn mock_frame(len: usize) -> Vec<u8> {
9393
mock_frame
9494
}
9595

96+
pub fn mock_frame_set_num_buffers(frame: &mut [u8], num_buffers: u16) {
97+
assert!(std::mem::size_of::<virtio_net_hdr_v1>() <= frame.len());
98+
// SAFETY:
99+
// Frame is bigger than the header.
100+
unsafe {
101+
let hdr = &mut *frame.as_mut_ptr().cast::<virtio_net_hdr_v1>();
102+
hdr.num_buffers = num_buffers;
103+
}
104+
}
105+
96106
#[derive(Debug)]
97107
pub enum ReadTapMock {
98108
Failure,

0 commit comments

Comments
 (0)