Skip to content

Commit 05923db

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 62cba22 commit 05923db

File tree

2 files changed

+175
-3
lines changed

2 files changed

+175
-3
lines changed

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

+165-3
Original file line numberDiff line numberDiff line change
@@ -1046,8 +1046,8 @@ pub mod tests {
10461046
};
10471047
use crate::devices::virtio::net::test_utils::test::TestHelper;
10481048
use crate::devices::virtio::net::test_utils::{
1049-
default_net, if_index, inject_tap_tx_frame, set_mac, NetEvent, NetQueue, ReadTapMock,
1050-
TapTrafficSimulator, WriteTapMock,
1049+
default_net, if_index, inject_tap_tx_frame, mock_frame_set_num_buffers, set_mac, NetEvent,
1050+
NetQueue, ReadTapMock, TapTrafficSimulator, WriteTapMock,
10511051
};
10521052
use crate::devices::virtio::net::NET_QUEUE_SIZES;
10531053
use crate::devices::virtio::queue::VIRTQ_DESC_F_WRITE;
@@ -1288,7 +1288,40 @@ pub mod tests {
12881288
}
12891289

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

@@ -1310,6 +1343,79 @@ pub mod tests {
13101343
th.check_rx_queue_resume(&frame);
13111344
}
13121345

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

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

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

+10
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)