Skip to content

Commit 37a6745

Browse files
committed
mana: TX SGE0 and WQE for LSO should meet GDMA requirements
GDMA requires that SGE0 for the LSO TX WQE should: - Only contain the header - Should not exceed 256 bytes - There should be > 1 SGL in the TX LSO WQE Also added test cases to cover the above requirements
1 parent 1fa089e commit 37a6745

File tree

3 files changed

+736
-317
lines changed

3 files changed

+736
-317
lines changed

vm/devices/net/mana_driver/src/queues.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ impl Wq {
436436
pub const fn entry_size(oob_len: usize, sge_count: usize) -> u32 {
437437
let len = size_of::<WqeHeader>() + oob_len + size_of::<Sge>() * sge_count;
438438
let len = (len + WQE_ALIGNMENT - 1) & !(WQE_ALIGNMENT - 1);
439+
assert!(len <= 512, "WQE size too large");
439440
len as u32
440441
}
441442

vm/devices/net/net_mana/src/lib.rs

Lines changed: 27 additions & 317 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#![forbid(unsafe_code)]
55
#![expect(missing_docs)]
66

7+
mod test;
8+
79
use anyhow::Context as _;
810
use async_trait::async_trait;
911
use futures::FutureExt;
@@ -1174,6 +1176,7 @@ impl<T: DeviceBacking> ManaQueue<T> {
11741176
} else {
11751177
let mut gd_client_unit_data = 0;
11761178
let mut header_len = head.len;
1179+
// For LSO, GDMA requires that SGE0 should only contain the header.
11771180
let (header_segment_count, partial_bytes) = if meta.offload_tcp_segmentation {
11781181
header_len = (meta.l2_len as u16 + meta.l3_len + meta.l4_len as u16) as u32;
11791182
if header_len > PAGE_SIZE32 {
@@ -1304,7 +1307,14 @@ impl<T: DeviceBacking> ManaQueue<T> {
13041307
// longest sequence that can be coalesced, instead of the first sequence.
13051308
let coalesce_possible = cur_seg.size + tail.len < PAGE_SIZE32;
13061309
if segment_count > hardware_segment_limit {
1310+
// With LSO, GDMA requires that the first segment should only contain
1311+
// the header and should not exceed 256 bytes. Otherwise, it treats
1312+
// the WQE as "corrupt", disables the queue and return GDMA error.
1313+
// To meet the hardware requirements, do not coalesce SGE0 when LSO is enabled.
1314+
let coalesce_sge_possible =
1315+
!meta.offload_tcp_segmentation || tail_idx > header_segment_count;
13071316
if !last_segment_bounced
1317+
&& coalesce_sge_possible
13081318
&& coalesce_possible
13091319
&& bounce_buffer.allocate(cur_seg.size + tail.len).is_ok()
13101320
{
@@ -1319,7 +1329,7 @@ impl<T: DeviceBacking> ManaQueue<T> {
13191329
cur_seg.address = gpa;
13201330
last_segment_bounced = true;
13211331
}
1322-
if last_segment_bounced {
1332+
if last_segment_bounced && coalesce_sge_possible {
13231333
if let Some(mut copy) = bounce_buffer.try_extend(tail.len) {
13241334
// Combine current segment with previous one using bounce buffer.
13251335
self.guest_memory
@@ -1357,6 +1367,22 @@ impl<T: DeviceBacking> ManaQueue<T> {
13571367
&sgl[..segment_count]
13581368
};
13591369

1370+
assert!(
1371+
!meta.offload_tcp_segmentation || sgl[0].size == header_len as u32,
1372+
"SGL[0] size {} should match header length {} for LSO packets",
1373+
sgl[0].size,
1374+
header_len
1375+
);
1376+
// Drop the LSO packet, if it only has a header segment.
1377+
if meta.offload_tcp_segmentation && sgl.len() == 1 {
1378+
tracelimit::error_ratelimited!(
1379+
sgl_len = sgl.len(),
1380+
"LSO packets should have more than one SGL entry"
1381+
);
1382+
1383+
return Ok(None);
1384+
}
1385+
13601386
let wqe_len = if short_format {
13611387
self.tx_wq
13621388
.push(
@@ -1544,319 +1570,3 @@ impl Inspect for ContiguousBufferManager {
15441570
.counter("failed_allocations", self.failed_allocations);
15451571
}
15461572
}
1547-
1548-
#[cfg(test)]
1549-
mod tests {
1550-
use crate::GuestDmaMode;
1551-
use crate::ManaEndpoint;
1552-
use crate::QueueStats;
1553-
use chipset_device::mmio::ExternallyManagedMmioIntercepts;
1554-
use gdma::VportConfig;
1555-
use gdma_defs::bnic::ManaQueryDeviceCfgResp;
1556-
use mana_driver::mana::ManaDevice;
1557-
use net_backend::Endpoint;
1558-
use net_backend::QueueConfig;
1559-
use net_backend::RxId;
1560-
use net_backend::TxId;
1561-
use net_backend::TxSegment;
1562-
use net_backend::loopback::LoopbackEndpoint;
1563-
use pal_async::DefaultDriver;
1564-
use pal_async::async_test;
1565-
use pci_core::msi::MsiInterruptSet;
1566-
use std::future::poll_fn;
1567-
use test_with_tracing::test;
1568-
use user_driver_emulated_mock::DeviceTestMemory;
1569-
use user_driver_emulated_mock::EmulatedDevice;
1570-
use vmcore::vm_task::SingleDriverBackend;
1571-
use vmcore::vm_task::VmTaskDriverSource;
1572-
1573-
/// Constructs a mana emulator backed by the loopback endpoint, then hooks a
1574-
/// mana driver up to it, puts the net_mana endpoint on top of that, and
1575-
/// ensures that packets can be sent and received.
1576-
#[async_test]
1577-
async fn test_endpoint_direct_dma(driver: DefaultDriver) {
1578-
send_test_packet(driver, GuestDmaMode::DirectDma, 1138, 1).await;
1579-
}
1580-
1581-
#[async_test]
1582-
async fn test_endpoint_bounce_buffer(driver: DefaultDriver) {
1583-
send_test_packet(driver, GuestDmaMode::BounceBuffer, 1138, 1).await;
1584-
}
1585-
1586-
#[async_test]
1587-
async fn test_segment_coalescing(driver: DefaultDriver) {
1588-
// 34 segments of 60 bytes each == 2040
1589-
send_test_packet(driver, GuestDmaMode::DirectDma, 2040, 34).await;
1590-
}
1591-
1592-
#[async_test]
1593-
async fn test_segment_coalescing_many(driver: DefaultDriver) {
1594-
// 128 segments of 16 bytes each == 2048
1595-
send_test_packet(driver, GuestDmaMode::DirectDma, 2048, 128).await;
1596-
}
1597-
1598-
async fn send_test_packet(
1599-
driver: DefaultDriver,
1600-
dma_mode: GuestDmaMode,
1601-
packet_len: usize,
1602-
num_segments: usize,
1603-
) {
1604-
let tx_id = 1;
1605-
let tx_metadata = net_backend::TxMetadata {
1606-
id: TxId(tx_id),
1607-
segment_count: num_segments,
1608-
len: packet_len,
1609-
..Default::default()
1610-
};
1611-
let expected_num_received_packets = 1;
1612-
let (data_to_send, tx_segments) =
1613-
build_tx_segments(packet_len, num_segments, tx_metadata.clone());
1614-
1615-
test_endpoint(
1616-
driver,
1617-
dma_mode,
1618-
packet_len,
1619-
tx_segments,
1620-
data_to_send,
1621-
expected_num_received_packets,
1622-
)
1623-
.await;
1624-
}
1625-
1626-
fn build_tx_segments(
1627-
packet_len: usize,
1628-
num_segments: usize,
1629-
tx_metadata: net_backend::TxMetadata,
1630-
) -> (Vec<u8>, Vec<TxSegment>) {
1631-
let data_to_send = (0..packet_len).map(|v| v as u8).collect::<Vec<u8>>();
1632-
1633-
let mut tx_segments = Vec::new();
1634-
let segment_len = packet_len / num_segments;
1635-
assert_eq!(packet_len % num_segments, 0);
1636-
assert_eq!(data_to_send.len(), packet_len);
1637-
1638-
tx_segments.push(TxSegment {
1639-
ty: net_backend::TxSegmentType::Head(tx_metadata.clone()),
1640-
gpa: 0,
1641-
len: segment_len as u32,
1642-
});
1643-
1644-
for j in 0..(num_segments - 1) {
1645-
let gpa = (j + 1) * segment_len;
1646-
tx_segments.push(TxSegment {
1647-
ty: net_backend::TxSegmentType::Tail,
1648-
gpa: gpa as u64,
1649-
len: segment_len as u32,
1650-
});
1651-
}
1652-
1653-
assert_eq!(tx_segments.len(), num_segments);
1654-
(data_to_send, tx_segments)
1655-
}
1656-
1657-
async fn test_endpoint(
1658-
driver: DefaultDriver,
1659-
dma_mode: GuestDmaMode,
1660-
packet_len: usize,
1661-
tx_segments: Vec<TxSegment>,
1662-
data_to_send: Vec<u8>,
1663-
expected_num_received_packets: usize,
1664-
) -> QueueStats {
1665-
let tx_id = 1;
1666-
let pages = 256; // 1MB
1667-
let allow_dma = dma_mode == GuestDmaMode::DirectDma;
1668-
let mem: DeviceTestMemory = DeviceTestMemory::new(pages * 2, allow_dma, "test_endpoint");
1669-
let payload_mem = mem.payload_mem();
1670-
1671-
let mut msi_set = MsiInterruptSet::new();
1672-
let device = gdma::GdmaDevice::new(
1673-
&VmTaskDriverSource::new(SingleDriverBackend::new(driver.clone())),
1674-
mem.guest_memory(),
1675-
&mut msi_set,
1676-
vec![VportConfig {
1677-
mac_address: [1, 2, 3, 4, 5, 6].into(),
1678-
endpoint: Box::new(LoopbackEndpoint::new()),
1679-
}],
1680-
&mut ExternallyManagedMmioIntercepts,
1681-
);
1682-
let device = EmulatedDevice::new(device, msi_set, mem.dma_client());
1683-
let dev_config = ManaQueryDeviceCfgResp {
1684-
pf_cap_flags1: 0.into(),
1685-
pf_cap_flags2: 0,
1686-
pf_cap_flags3: 0,
1687-
pf_cap_flags4: 0,
1688-
max_num_vports: 1,
1689-
reserved: 0,
1690-
max_num_eqs: 64,
1691-
};
1692-
let thing = ManaDevice::new(&driver, device, 1, 1).await.unwrap();
1693-
let vport = thing.new_vport(0, None, &dev_config).await.unwrap();
1694-
let mut endpoint = ManaEndpoint::new(driver.clone(), vport, dma_mode).await;
1695-
let mut queues = Vec::new();
1696-
let pool = net_backend::tests::Bufs::new(payload_mem.clone());
1697-
endpoint
1698-
.get_queues(
1699-
vec![QueueConfig {
1700-
pool: Box::new(pool),
1701-
initial_rx: &(1..128).map(RxId).collect::<Vec<_>>(),
1702-
driver: Box::new(driver.clone()),
1703-
}],
1704-
None,
1705-
&mut queues,
1706-
)
1707-
.await
1708-
.unwrap();
1709-
1710-
payload_mem.write_at(0, &data_to_send).unwrap();
1711-
1712-
queues[0].tx_avail(tx_segments.as_slice()).unwrap();
1713-
1714-
// Poll for completion
1715-
let mut rx_packets = [RxId(0); 2];
1716-
let mut rx_packets_n = 0;
1717-
let mut tx_done = [TxId(0); 2];
1718-
let mut tx_done_n = 0;
1719-
while rx_packets_n == 0 {
1720-
poll_fn(|cx| queues[0].poll_ready(cx)).await;
1721-
rx_packets_n += queues[0].rx_poll(&mut rx_packets[rx_packets_n..]).unwrap();
1722-
// GDMA Errors generate a TryReturn error, ignored here.
1723-
tx_done_n += queues[0].tx_poll(&mut tx_done[tx_done_n..]).unwrap_or(0);
1724-
if expected_num_received_packets == 0 {
1725-
break;
1726-
}
1727-
}
1728-
assert_eq!(rx_packets_n, expected_num_received_packets);
1729-
1730-
if expected_num_received_packets == 0 {
1731-
// If no packets were received, exit.
1732-
let stats = get_queue_stats(queues[0].queue_stats());
1733-
drop(queues);
1734-
endpoint.stop().await;
1735-
return stats;
1736-
}
1737-
1738-
// Check tx
1739-
assert_eq!(tx_done_n, 1);
1740-
assert_eq!(tx_done[0].0, tx_id);
1741-
1742-
// Check rx
1743-
assert_eq!(rx_packets[0].0, 1);
1744-
let rx_id = rx_packets[0];
1745-
1746-
let mut received_data = vec![0; packet_len];
1747-
payload_mem
1748-
.read_at(2048 * rx_id.0 as u64, &mut received_data)
1749-
.unwrap();
1750-
assert_eq!(received_data.len(), packet_len);
1751-
assert_eq!(&received_data[..], data_to_send, "{:?}", rx_id);
1752-
1753-
let stats = get_queue_stats(queues[0].queue_stats());
1754-
drop(queues);
1755-
endpoint.stop().await;
1756-
stats
1757-
}
1758-
1759-
#[async_test]
1760-
async fn test_vport_with_query_filter_state(driver: DefaultDriver) {
1761-
let pages = 512; // 2MB
1762-
let mem = DeviceTestMemory::new(pages, false, "test_vport_with_query_filter_state");
1763-
let mut msi_set = MsiInterruptSet::new();
1764-
let device = gdma::GdmaDevice::new(
1765-
&VmTaskDriverSource::new(SingleDriverBackend::new(driver.clone())),
1766-
mem.guest_memory(),
1767-
&mut msi_set,
1768-
vec![VportConfig {
1769-
mac_address: [1, 2, 3, 4, 5, 6].into(),
1770-
endpoint: Box::new(LoopbackEndpoint::new()),
1771-
}],
1772-
&mut ExternallyManagedMmioIntercepts,
1773-
);
1774-
let dma_client = mem.dma_client();
1775-
let device = EmulatedDevice::new(device, msi_set, dma_client);
1776-
let cap_flags1 = gdma_defs::bnic::BasicNicDriverFlags::new().with_query_filter_state(1);
1777-
let dev_config = ManaQueryDeviceCfgResp {
1778-
pf_cap_flags1: cap_flags1,
1779-
pf_cap_flags2: 0,
1780-
pf_cap_flags3: 0,
1781-
pf_cap_flags4: 0,
1782-
max_num_vports: 1,
1783-
reserved: 0,
1784-
max_num_eqs: 64,
1785-
};
1786-
let thing = ManaDevice::new(&driver, device, 1, 1).await.unwrap();
1787-
let _ = thing.new_vport(0, None, &dev_config).await.unwrap();
1788-
}
1789-
1790-
#[async_test]
1791-
async fn test_valid_packet(driver: DefaultDriver) {
1792-
let tx_id = 1;
1793-
let expected_num_received_packets = 1;
1794-
let num_segments = 1;
1795-
let packet_len = 1138;
1796-
let metadata = net_backend::TxMetadata {
1797-
id: TxId(tx_id),
1798-
segment_count: num_segments,
1799-
len: packet_len,
1800-
..Default::default()
1801-
};
1802-
1803-
let (data_to_send, tx_segments) = build_tx_segments(packet_len, num_segments, metadata);
1804-
1805-
let stats = test_endpoint(
1806-
driver,
1807-
GuestDmaMode::DirectDma,
1808-
packet_len,
1809-
tx_segments,
1810-
data_to_send,
1811-
expected_num_received_packets,
1812-
)
1813-
.await;
1814-
1815-
assert_eq!(stats.tx_packets.get(), 1, "tx_packets increase");
1816-
assert_eq!(stats.rx_packets.get(), 1, "rx_packets increase");
1817-
assert_eq!(stats.tx_errors.get(), 0, "tx_errors remain the same");
1818-
assert_eq!(stats.rx_errors.get(), 0, "rx_errors remain the same");
1819-
}
1820-
1821-
#[async_test]
1822-
async fn test_tx_error_handling(driver: DefaultDriver) {
1823-
let tx_id = 1;
1824-
let expected_num_received_packets = 0;
1825-
let num_segments = 1;
1826-
let packet_len = 1138;
1827-
// LSO Enabled, but sending insufficient number of segments.
1828-
let metadata = net_backend::TxMetadata {
1829-
id: TxId(tx_id),
1830-
segment_count: num_segments,
1831-
len: packet_len,
1832-
offload_tcp_segmentation: true,
1833-
..Default::default()
1834-
};
1835-
1836-
let (data_to_send, tx_segments) = build_tx_segments(packet_len, num_segments, metadata);
1837-
1838-
let stats = test_endpoint(
1839-
driver,
1840-
GuestDmaMode::DirectDma,
1841-
packet_len,
1842-
tx_segments,
1843-
data_to_send,
1844-
expected_num_received_packets,
1845-
)
1846-
.await;
1847-
1848-
assert_eq!(stats.tx_errors.get(), 1, "tx_errors increase");
1849-
assert_eq!(stats.tx_packets.get(), 0, "tx_packets stay the same");
1850-
}
1851-
1852-
fn get_queue_stats(queue_stats: Option<&dyn net_backend::BackendQueueStats>) -> QueueStats {
1853-
let queue_stats = queue_stats.unwrap();
1854-
QueueStats {
1855-
rx_errors: queue_stats.rx_errors(),
1856-
tx_errors: queue_stats.tx_errors(),
1857-
rx_packets: queue_stats.rx_packets(),
1858-
tx_packets: queue_stats.tx_packets(),
1859-
..Default::default()
1860-
}
1861-
}
1862-
}

0 commit comments

Comments
 (0)