44#![ forbid( unsafe_code) ]
55#![ expect( missing_docs) ]
66
7+ mod test;
8+
79use anyhow:: Context as _;
810use async_trait:: async_trait;
911use 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