From 4e38eef817813c1bbd8a9cf972e4cf0134d24308 Mon Sep 17 00:00:00 2001 From: Alex Williams Date: Sun, 1 Dec 2019 21:58:13 -0800 Subject: dpdk: Add new DPDK stack to integrate with I/O services docs: Update DPDK docs with new parameters: Parameter names have had their hyphens changed to underscores, and the I/O CPU argument is now named after the lcores and reflects the naming used by DPDK. transport: Add new udp_dpdk_link, based atop the new APIs: This link is tightly coupled with the DPDK I/O service. The link class carries all the address information to communicate with the other host, and it can send packets directly through the DPDK NIC ports. However, for receiving packets, the I/O service must pull the packets from the DMA queue and attach them to the appropriate link object. The link object merely formats the frame_buff object underneath, which is embedded in the rte_mbuf container. For get_recv_buff, the link will pull buffers only from its internal queue (the one filled by the I/O service). transport: Add DPDK-specific I/O service: The I/O service is split into two parts, the user threads and the I/O worker threads. The user threads submit requests through various appropriate queues, and the I/O threads perform all the I/O on their behalf. This includes routing UDP packets to the correct receiver and getting the MAC address of a destination (by performing the ARP request and handling the ARP replies). The DPDK context stores I/O services. The context spawns all I/O services on init(), and I/O services can be fetched from the dpdk_ctx object by using a port ID. I/O service clients: The clients have two lockless ring buffers. One is to get a buffer from the I/O service; the other is to release a buffer back to the I/O service. Threads sleeping on buffer I/O are kept in a separate list from the service queue and are processed in the course of doing RX or TX. The list nodes are embedded in the dpdk_io_if, and the head of the list is on the dpdk_io_service. The I/O service will transfer the embedded wait_req to the list if it cannot acquire the mutex to complete the condition for waking. Co-authored-by: Martin Braun Co-authored-by: Ciro Nishiguchi Co-authored-by: Brent Stapleton --- host/lib/include/uhdlib/transport/dpdk/common.hpp | 202 +++++++++++++++------- 1 file changed, 144 insertions(+), 58 deletions(-) (limited to 'host/lib/include/uhdlib/transport/dpdk/common.hpp') diff --git a/host/lib/include/uhdlib/transport/dpdk/common.hpp b/host/lib/include/uhdlib/transport/dpdk/common.hpp index 1f4466a0f..ac3526e49 100644 --- a/host/lib/include/uhdlib/transport/dpdk/common.hpp +++ b/host/lib/include/uhdlib/transport/dpdk/common.hpp @@ -1,44 +1,130 @@ // -// Copyright 2019 Ettus Research, a National Instruments brand +// Copyright 2019 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: GPL-3.0-or-later // + #ifndef _INCLUDED_UHDLIB_TRANSPORT_DPDK_COMMON_HPP_ #define _INCLUDED_UHDLIB_TRANSPORT_DPDK_COMMON_HPP_ #include +#include #include -#include -#include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include #include -/* NOTE: There are changes to rte_eth_addr in 19.x */ +/* NOTE: There are changes to all the network standard fields in 19.x */ + + +namespace uhd { namespace transport { + +class dpdk_io_service; + +namespace dpdk { -namespace uhd { namespace transport { namespace dpdk { +struct arp_entry; using queue_id_t = uint16_t; using port_id_t = uint16_t; using ipv4_addr = uint32_t; +class dpdk_adapter_info : public adapter_info +{ +public: + dpdk_adapter_info(port_id_t port) : _port(port) {} + ~dpdk_adapter_info() {} + + std::string to_string() + { + return std::string("DPDK:") + std::to_string(_port); + } + + bool operator==(const dpdk_adapter_info& rhs) const + { + return (_port == rhs._port); + } + +private: + // Port ID + port_id_t _port; +}; + + +/*! + * Packet/Frame buffer class for DPDK + * + * This class is intended to be placed in the private area of the rte_mbuf, and + * its memory is part of the rte_mbuf, so its life is tied to the underlying + * buffer (or more precisely, the encapsulating one). + */ +class dpdk_frame_buff : public frame_buff +{ +public: + dpdk_frame_buff(struct rte_mbuf* mbuf) : _mbuf(mbuf) + { + _data = rte_pktmbuf_mtod(mbuf, void*); + _packet_size = 0; + } + + ~dpdk_frame_buff() = default; + + /*! + * Simple getter for the underlying rte_mbuf. + * The rte_mbuf may need further modification before sending packets, + * like adjusting the IP and UDP lengths. + */ + inline struct rte_mbuf* get_pktmbuf() + { + return _mbuf; + } + + /*! + * Move the data pointer by the indicated size, to some desired + * encapsulated frame. + * + * \param hdr_size Size (in bytes) of the headers to skip. Can be negative + * to pull the header back. + */ + inline void header_jump(ssize_t hdr_size) + { + _data = (void*)((uint8_t*)_data + hdr_size); + } + + //! Embedded list node's next ptr + dpdk_frame_buff* next = nullptr; + //! Embedded list node's prev ptr + dpdk_frame_buff* prev = nullptr; + +private: + struct rte_mbuf* _mbuf; +}; + + +/*! + * The size (in bytes) of the private area reserved within the rte_mbuf. + * This portion of the rte_mbuf is used for the embedded dpdk_frame_buff data + * structure. + */ +constexpr size_t DPDK_MBUF_PRIV_SIZE = + RTE_ALIGN(sizeof(struct dpdk_frame_buff), RTE_MBUF_PRIV_ALIGN); + /*! * Class representing a DPDK NIC port * * The dpdk_port object possesses all the data needed to send and receive - * packets between this port and a remote host. A logical link should specify - * which packets are destined for it and allocate a DMA queue with the - * dpdk_port::alloc_queue() function. A logical link should not, however, - * specify ARP packets for its set of received packets. That functionality is - * reserved for the special queue 0. + * packets between this port and a remote host. * * The logical link can then get the packet buffer pools associated with this * NIC port and use them to send and receive packets. @@ -55,7 +141,7 @@ public: * \param port The port ID * \param mtu The intended MTU for the port * \param num_queues Number of DMA queues to reserve for this port - * \param num_mbufs The number of packet buffers per queue + * \param num_desc The number of descriptors per DMA queue * \param rx_pktbuf_pool A pointer to the port's RX packet buffer pool * \param tx_pktbuf_pool A pointer to the port's TX packet buffer pool * \param ipv4_address The IPv4 network address (w/ netmask) @@ -64,7 +150,7 @@ public: static dpdk_port::uptr make(port_id_t port, size_t mtu, uint16_t num_queues, - size_t num_mbufs, + uint16_t num_desc, struct rte_mempool* rx_pktbuf_pool, struct rte_mempool* tx_pktbuf_pool, std::string ipv4_address); @@ -72,11 +158,13 @@ public: dpdk_port(port_id_t port, size_t mtu, uint16_t num_queues, - size_t num_mbufs, + uint16_t num_desc, struct rte_mempool* rx_pktbuf_pool, struct rte_mempool* tx_pktbuf_pool, std::string ipv4_address); + ~dpdk_port(); + /*! Getter for this port's ID * \return this port's ID */ @@ -85,6 +173,19 @@ public: return _port; } + inline dpdk_adapter_info get_adapter_info() const + { + return dpdk_adapter_info(_port); + } + + /*! Getter for this port's MAC address + * \return this port's MAC address + */ + inline ether_addr get_mac_addr() const + { + return _mac_addr; + } + /*! Getter for this port's MTU * \return this port's MTU */ @@ -141,68 +242,45 @@ public: * \param dst_ipv4_addr The destination IPv4 address (in network order) * \return whether the destination address matches this port's broadcast address */ - inline bool dst_is_broadcast(const uint32_t dst_ipv4_addr) const + inline bool dst_is_broadcast(const ipv4_addr dst_ipv4_addr) const { uint32_t network = _netmask | ((~_netmask) & dst_ipv4_addr); return (network == 0xffffffff); } - /*! Allocate a DMA queue (TX/RX pair) and use the specified flow pattern - * to route packets to the RX queue. - * - * \pattern recv_pattern The flow pattern to use for directing traffic to - * the allocated RX queue. - * \return The queue ID for the allocated queue - * \throw uhd::runtime_error when there are no free queues - */ - queue_id_t alloc_queue(struct rte_flow_pattern recv_pattern[]); - - /*! Free a previously allocated queue and tear down the associated flow rule - * \param queue The ID of the queue to free - * \throw std::out_of_range when the queue ID is not currently allocated - */ - void free_queue(queue_id_t queue); - /*! - * Process ARP request/reply + * Allocate a UDP port and return it in network order + * + * \param udp_port UDP port to attempt to allocate. Use 0 for no preference. + * \return 0 for failure, else the allocated UDP port in network order. */ - // int process_arp(struct rte_mempool *tx_pktbuf_pool, struct arp_hdr *arp_frame); + uint16_t alloc_udp_port(uint16_t udp_port); private: + friend uhd::transport::dpdk_io_service; + /*! * Construct and transmit an ARP reply (for the given ARP request) */ - int _arp_reply(struct rte_mempool* tx_pktbuf_pool, struct arp_hdr* arp_req); + int _arp_reply(queue_id_t queue_id, struct arp_hdr* arp_req); port_id_t _port; size_t _mtu; + size_t _num_queues; struct rte_mempool* _rx_pktbuf_pool; struct rte_mempool* _tx_pktbuf_pool; struct ether_addr _mac_addr; ipv4_addr _ipv4; ipv4_addr _netmask; - size_t _num_queues; - std::vector _free_queues; - std::unordered_map _flow_rules; - /* Need ARP table - * To implement ARP service, maybe create ARP xport - * Then need dpdk_udp_link and dpdk_raw_link - * - * ...Or just do it inline with dpdk_ctx - * - * And link can just save the result (do it during constructor) - * - * - * But what about the service that _responds_ to ARP requests?! - * - * Maybe have to connect a DPDK link in stages: - * First, create the ARP service and attach it to the dpdk_ctx - * dpdk_ctx must own the links...? - * Or! Always burn a DMA engine for ARP - * - * Maybe have a shared_ptr to an ARP service here? - */ + + // Structures protected by mutex std::mutex _mutex; + std::set _udp_ports; + uint16_t _next_udp_port = 0xffff; + + // Structures protected by spin lock + rte_spinlock_t _spinlock = RTE_SPINLOCK_INITIALIZER; + std::unordered_map _arp_table; }; @@ -267,17 +345,21 @@ public: int get_port_link_status(port_id_t portid) const; /*! - * Get port ID for routing packet destined for given address + * Get port for routing packet destined for given address * \param addr Destination address - * \return port ID from routing table + * \return pointer to the port from routing table */ - int get_route(const std::string& addr) const; + dpdk_port* get_route(const std::string& addr) const; /*! * \return whether init() has been called */ bool is_init_done(void) const; + /*! Return a reference to an IO service given a port ID + */ + std::shared_ptr get_io_service(const size_t port_id); + private: /*! Convert the args to DPDK's EAL args and Initialize the EAL * @@ -312,8 +394,12 @@ private: std::unordered_map _ports; std::vector _rx_pktbuf_pools; std::vector _tx_pktbuf_pools; + // Store all the I/O services, and also store the corresponding port ID + std::map, std::vector> + _io_srv_portid_map; }; -}}} // namespace uhd::transport::dpdk +} // namespace dpdk +}} // namespace uhd::transport #endif /* _INCLUDED_UHDLIB_TRANSPORT_DPDK_COMMON_HPP_ */ -- cgit v1.2.3