From c70eaf363e8c42d7fc1629eea2a7202dd5239d64 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 04:49:50 +0000 Subject: [PATCH 1/2] Initial plan From 8605161b5d1e81e9f11c96b8c705c7d93f727120 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Feb 2026 05:13:37 +0000 Subject: [PATCH 2/2] Changes before error encountered Co-authored-by: benaadams <1142958+benaadams@users.noreply.github.com> --- src/Magma.DPDK/Interop/Dpdk.cs | 178 +++++++++++++++++++++++ src/Magma.DPDK/Interop/DpdkStructs.cs | 119 +++++++++++++++ src/Magma.DPDK/Interop/MbufExtensions.cs | 83 +++++++++++ src/Magma.DPDK/Magma.DPDK.csproj | 20 +++ 4 files changed, 400 insertions(+) create mode 100644 src/Magma.DPDK/Interop/Dpdk.cs create mode 100644 src/Magma.DPDK/Interop/DpdkStructs.cs create mode 100644 src/Magma.DPDK/Interop/MbufExtensions.cs create mode 100644 src/Magma.DPDK/Magma.DPDK.csproj diff --git a/src/Magma.DPDK/Interop/Dpdk.cs b/src/Magma.DPDK/Interop/Dpdk.cs new file mode 100644 index 0000000..6348156 --- /dev/null +++ b/src/Magma.DPDK/Interop/Dpdk.cs @@ -0,0 +1,178 @@ +using System.Runtime.InteropServices; + +namespace Magma.DPDK.Interop; + +/// +/// P/Invoke declarations for DPDK (Data Plane Development Kit) functions. +/// These bindings provide access to DPDK's low-level packet I/O operations. +/// +internal static unsafe class Dpdk +{ + private const string LibraryName = "librte_ethdev.so"; + + /// + /// Initialize the EAL (Environment Abstraction Layer). + /// Must be called before any other DPDK function. + /// + /// Argument count + /// Argument vector + /// Number of parsed arguments on success, negative on error + [DllImport("librte_eal.so", CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eal_init(int argc, byte** argv); + + /// + /// Get the number of Ethernet devices available. + /// + /// Number of available Ethernet devices + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern ushort rte_eth_dev_count_avail(); + + /// + /// Configure an Ethernet device. + /// + /// Port identifier + /// Number of RX queues + /// Number of TX queues + /// Device configuration + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_dev_configure(ushort port_id, ushort nb_rx_queue, ushort nb_tx_queue, ref rte_eth_conf eth_conf); + + /// + /// Allocate and set up a receive queue for an Ethernet device. + /// + /// Port identifier + /// RX queue index (must be in range [0, nb_rx_queue - 1]) + /// Number of RX descriptors + /// NUMA socket ID for memory allocation + /// RX queue configuration (can be null for defaults) + /// Memory pool from which to allocate rte_mbuf network memory buffers + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_rx_queue_setup(ushort port_id, ushort rx_queue_id, ushort nb_rx_desc, uint socket_id, rte_eth_rxconf* rx_conf, void* mb_pool); + + /// + /// Allocate and set up a transmit queue for an Ethernet device. + /// + /// Port identifier + /// TX queue index (must be in range [0, nb_tx_queue - 1]) + /// Number of TX descriptors + /// NUMA socket ID for memory allocation + /// TX queue configuration (can be null for defaults) + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_tx_queue_setup(ushort port_id, ushort tx_queue_id, ushort nb_tx_desc, uint socket_id, rte_eth_txconf* tx_conf); + + /// + /// Start an Ethernet device. + /// The device start step is the last one before beginning to receive and transmit packets. + /// + /// Port identifier + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_dev_start(ushort port_id); + + /// + /// Stop an Ethernet device. The device can be restarted with rte_eth_dev_start(). + /// + /// Port identifier + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_dev_stop(ushort port_id); + + /// + /// Close a stopped Ethernet device and release resources. + /// + /// Port identifier + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_dev_close(ushort port_id); + + /// + /// Enable receipt in promiscuous mode for an Ethernet device. + /// + /// Port identifier + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_promiscuous_enable(ushort port_id); + + /// + /// Disable receipt in promiscuous mode for an Ethernet device. + /// + /// Port identifier + /// 0 on success, negative on error + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern int rte_eth_promiscuous_disable(ushort port_id); + + /// + /// Retrieve a burst of input packets from an RX queue. + /// This is the main function for receiving packets in poll mode. + /// + /// Port identifier + /// RX queue index (must be in range [0, nb_rx_queue - 1]) + /// Array of pointers to rte_mbuf structures for received packets + /// Maximum number of packets to retrieve + /// Number of packets actually retrieved (0 to nb_pkts) + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern ushort rte_eth_rx_burst(ushort port_id, ushort queue_id, rte_mbuf** rx_pkts, ushort nb_pkts); + + /// + /// Send a burst of output packets on a TX queue. + /// This is the main function for transmitting packets in poll mode. + /// + /// Port identifier + /// TX queue index (must be in range [0, nb_tx_queue - 1]) + /// Array of pointers to rte_mbuf structures for packets to send + /// Number of packets to transmit + /// Number of packets actually sent + [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] + public static extern ushort rte_eth_tx_burst(ushort port_id, ushort queue_id, rte_mbuf** tx_pkts, ushort nb_pkts); + + /// + /// Create a new mbuf pool. + /// + /// Name of the mbuf pool + /// Number of elements in the pool + /// Size of the per-core object cache + /// Size of application private data + /// Size of data buffer in each mbuf + /// NUMA socket ID + /// Pointer to the new mbuf pool, or null on error + [DllImport("librte_mbuf.so", CallingConvention = CallingConvention.Cdecl)] + public static extern void* rte_pktmbuf_pool_create(byte* name, uint n, uint cache_size, ushort priv_size, ushort data_room_size, int socket_id); + + /// + /// Free a packet mbuf back to its pool. + /// + /// Pointer to the mbuf to free + [DllImport("librte_mbuf.so", CallingConvention = CallingConvention.Cdecl)] + public static extern void rte_pktmbuf_free(rte_mbuf* m); + + /// + /// Allocate a new mbuf from a pool. + /// + /// Pointer to the mbuf pool + /// Pointer to the new mbuf, or null on error + [DllImport("librte_mbuf.so", CallingConvention = CallingConvention.Cdecl)] + public static extern rte_mbuf* rte_pktmbuf_alloc(void* mp); + + /// + /// Get the NUMA socket ID for a given lcore. + /// + /// Logical core ID + /// NUMA socket ID + [DllImport("librte_eal.so", CallingConvention = CallingConvention.Cdecl)] + public static extern uint rte_lcore_to_socket_id(uint lcore_id); + + /// + /// Get the ID of the current lcore. + /// + /// Current lcore ID + [DllImport("librte_eal.so", CallingConvention = CallingConvention.Cdecl)] + public static extern uint rte_lcore_id(); + + /// + /// Special socket ID value indicating "any socket" for NUMA-unaware allocation. + /// + public const int SOCKET_ID_ANY = -1; +} diff --git a/src/Magma.DPDK/Interop/DpdkStructs.cs b/src/Magma.DPDK/Interop/DpdkStructs.cs new file mode 100644 index 0000000..1ced3f1 --- /dev/null +++ b/src/Magma.DPDK/Interop/DpdkStructs.cs @@ -0,0 +1,119 @@ +using System.Runtime.InteropServices; + +namespace Magma.DPDK.Interop; + +/// +/// DPDK mbuf (memory buffer) structure for packet data. +/// This is a simplified version containing the essential fields needed for basic RX/TX operations. +/// +[StructLayout(LayoutKind.Sequential)] +internal unsafe struct rte_mbuf +{ + public void* buf_addr; + public ulong buf_iova; + + // Rearm data marker + public ushort data_off; + public ushort refcnt; + public ushort nb_segs; + public ushort port; + + public ulong ol_flags; + + // Packet type and RSS hash + public uint packet_type; + public uint pkt_len; + public ushort data_len; + public ushort vlan_tci; + + public ulong hash_rss; + + public ushort vlan_tci_outer; + public ushort buf_len; + + // Pool pointer + public void* pool; + + // Second cache line - skip for now + // We'll access packet data via buf_addr + data_off +} + +/// +/// DPDK Ethernet device configuration structure. +/// +[StructLayout(LayoutKind.Sequential)] +internal struct rte_eth_conf +{ + public uint link_speeds; + public rte_eth_rxmode rxmode; + public rte_eth_txmode txmode; + public uint lpbk_mode; + public rte_eth_rxconf rxconf; + public rte_eth_txconf txconf; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct rte_eth_rxmode +{ + public ulong offloads; + public uint mtu; + public ushort max_lro_pkt_size; + public ushort split_hdr_size; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct rte_eth_txmode +{ + public ulong offloads; + public ushort pvid; + public byte hw_vlan_reject_tagged; + public byte hw_vlan_reject_untagged; + public byte hw_vlan_insert_pvid; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct rte_eth_rxconf +{ + public ushort rx_thresh_pthresh; + public ushort rx_thresh_hthresh; + public ushort rx_thresh_wthresh; + public byte rx_drop_en; + public byte rx_deferred_start; + public ulong offloads; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct rte_eth_txconf +{ + public ushort tx_thresh_pthresh; + public ushort tx_thresh_hthresh; + public ushort tx_thresh_wthresh; + public ushort tx_rs_thresh; + public ushort tx_free_thresh; + public byte tx_deferred_start; + public ulong offloads; +} + +/// +/// Link speeds +/// +internal static class RteEthLinkSpeed +{ + public const uint RTE_ETH_LINK_SPEED_AUTONEG = 0; + public const uint RTE_ETH_LINK_SPEED_FIXED = 0x80000000; + public const uint RTE_ETH_LINK_SPEED_10M_HD = 0x00000001; + public const uint RTE_ETH_LINK_SPEED_10M = 0x00000002; + public const uint RTE_ETH_LINK_SPEED_100M_HD = 0x00000004; + public const uint RTE_ETH_LINK_SPEED_100M = 0x00000008; + public const uint RTE_ETH_LINK_SPEED_1G = 0x00000010; + public const uint RTE_ETH_LINK_SPEED_2_5G = 0x00000020; + public const uint RTE_ETH_LINK_SPEED_5G = 0x00000040; + public const uint RTE_ETH_LINK_SPEED_10G = 0x00000080; + public const uint RTE_ETH_LINK_SPEED_20G = 0x00000100; + public const uint RTE_ETH_LINK_SPEED_25G = 0x00000200; + public const uint RTE_ETH_LINK_SPEED_40G = 0x00000400; + public const uint RTE_ETH_LINK_SPEED_50G = 0x00000800; + public const uint RTE_ETH_LINK_SPEED_56G = 0x00001000; + public const uint RTE_ETH_LINK_SPEED_100G = 0x00002000; + public const uint RTE_ETH_LINK_SPEED_200G = 0x00004000; +} diff --git a/src/Magma.DPDK/Interop/MbufExtensions.cs b/src/Magma.DPDK/Interop/MbufExtensions.cs new file mode 100644 index 0000000..9c2b46f --- /dev/null +++ b/src/Magma.DPDK/Interop/MbufExtensions.cs @@ -0,0 +1,83 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Magma.DPDK.Interop; + +/// +/// Extension methods for working with DPDK mbufs and providing zero-copy access via Span<T>. +/// +internal static unsafe class MbufExtensions +{ + /// + /// Get a Span<byte> view of the packet data in an mbuf (zero-copy). + /// + /// Pointer to the mbuf + /// Span representing the packet data + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetPacketData(this rte_mbuf* mbuf) + { + if (mbuf == null) + return Span.Empty; + + var dataPtr = (byte*)mbuf->buf_addr + mbuf->data_off; + return new Span(dataPtr, mbuf->data_len); + } + + /// + /// Get a pointer to the start of packet data in an mbuf. + /// + /// Pointer to the mbuf + /// Pointer to packet data + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte* GetDataPointer(this rte_mbuf* mbuf) + { + if (mbuf == null) + return null; + + return (byte*)mbuf->buf_addr + mbuf->data_off; + } + + /// + /// Set the packet length for an mbuf. + /// + /// Pointer to the mbuf + /// Packet length in bytes + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetPacketLength(this rte_mbuf* mbuf, ushort length) + { + mbuf->data_len = length; + mbuf->pkt_len = length; + } + + /// + /// Copy data into an mbuf's packet buffer. + /// + /// Pointer to the mbuf + /// Source data to copy + /// True if successful, false if buffer too small + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CopyPacketData(this rte_mbuf* mbuf, ReadOnlySpan source) + { + if (mbuf == null || source.Length > mbuf->buf_len - mbuf->data_off) + return false; + + var dest = mbuf->GetPacketData(); + source.CopyTo(dest); + mbuf->SetPacketLength((ushort)source.Length); + return true; + } + + /// + /// Get the available buffer space in an mbuf for packet data. + /// + /// Pointer to the mbuf + /// Available space in bytes + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetAvailableSpace(this rte_mbuf* mbuf) + { + if (mbuf == null) + return 0; + + return mbuf->buf_len - mbuf->data_off; + } +} diff --git a/src/Magma.DPDK/Magma.DPDK.csproj b/src/Magma.DPDK/Magma.DPDK.csproj new file mode 100644 index 0000000..cca619b --- /dev/null +++ b/src/Magma.DPDK/Magma.DPDK.csproj @@ -0,0 +1,20 @@ + + + + net10.0 + true + latest + DPDK (Data Plane Development Kit) integration for high-performance packet I/O with kernel bypass + + + + + + + + + + + + +