1 // 2 // Copyright 2019 Ettus Research, a National Instruments brand 3 // 4 // SPDX-License-Identifier: GPL-3.0-or-later 5 // 6 #pragma once 7 8 #include <uhdlib/transport/dpdk/common.hpp> 9 #include <uhdlib/transport/dpdk/service_queue.hpp> 10 #include <uhdlib/transport/io_service.hpp> 11 #include <rte_arp.h> 12 #include <rte_hash.h> 13 #include <rte_ip.h> 14 #include <rte_mbuf.h> 15 #include <rte_udp.h> 16 #include <vector> 17 18 namespace uhd { namespace transport { 19 20 class dpdk_send_io; 21 class dpdk_recv_io; 22 struct dpdk_io_if; 23 24 class dpdk_io_service : public virtual io_service, 25 public std::enable_shared_from_this<dpdk_io_service> 26 { 27 public: 28 using sptr = std::shared_ptr<dpdk_io_service>; 29 30 static sptr make( 31 unsigned int lcore_id, std::vector<dpdk::dpdk_port*> ports, size_t servq_depth); 32 33 ~dpdk_io_service(); 34 35 // Add entry to RX flow table 36 // This yields a link, which is then used for attaching to a buffer 37 // We yank from the link immediately following, then process at the transport level 38 // (so two tables here, one for transports, one for links) 39 void attach_recv_link(recv_link_if::sptr link); 40 41 // Create object to hold set of queues, to go in TX table 42 void attach_send_link(send_link_if::sptr link); 43 44 void detach_recv_link(recv_link_if::sptr link); 45 46 void detach_send_link(send_link_if::sptr link); 47 48 recv_io_if::sptr make_recv_client(recv_link_if::sptr data_link, 49 size_t num_recv_frames, 50 recv_callback_t cb, 51 send_link_if::sptr fc_link, 52 size_t num_send_frames, 53 recv_io_if::fc_callback_t fc_cb); 54 55 send_io_if::sptr make_send_client(send_link_if::sptr send_link, 56 size_t num_send_frames, 57 send_io_if::send_callback_t send_cb, 58 recv_link_if::sptr recv_link, 59 size_t num_recv_frames, 60 recv_callback_t recv_cb, 61 send_io_if::fc_callback_t fc_cb); 62 63 64 private: 65 friend class dpdk_recv_io; 66 friend class dpdk_send_io; 67 68 dpdk_io_service( 69 unsigned int lcore_id, std::vector<dpdk::dpdk_port*> ports, size_t servq_depth); 70 dpdk_io_service(const dpdk_io_service&) = delete; 71 72 /*! 73 * I/O worker function to be passed to the DPDK lcore 74 * 75 * The argument must be a pointer to *this* dpdk_io_service 76 * 77 * \param arg a pointer to this dpdk_io_service 78 * \return 0 for normal termination, else nonzero 79 */ 80 static int _io_worker(void* arg); 81 82 /*! 83 * Helper function for I/O thread to process requests on its service queue 84 */ 85 int _service_requests(); 86 87 /*! 88 * Helper function for I/O thread to service a WAIT_FLOW_OPEN request 89 * 90 * \param req The requester's wait_req object 91 */ 92 void _service_flow_open(dpdk::wait_req* req); 93 94 /*! 95 * Helper function for I/O thread to service a WAIT_FLOW_CLOSE request 96 * 97 * \param req The requester's wait_req object 98 */ 99 void _service_flow_close(dpdk::wait_req* req); 100 101 /*! 102 * Helper function for I/O thread to service a WAIT_XPORT_CONNECT request 103 * 104 * \param req The requester's wait_req object 105 */ 106 void _service_xport_connect(dpdk::wait_req* req); 107 108 /*! 109 * Helper function for I/O thread to service a WAIT_XPORT_DISCONNECT request 110 * 111 * \param req The requester's wait_req object 112 */ 113 void _service_xport_disconnect(dpdk::wait_req* req); 114 115 /*! 116 * Get Ethernet MAC address for the given IPv4 address, and wake the 117 * requester when finished. 118 * This may only be called by an I/O service, on behalf of a requester's 119 * WAIT_ARP request. 120 * 121 * \param req The requester's wait_req object 122 * \return 0 if address was written, -EAGAIN if request was queued for 123 * later completion, -ENOMEM if ran out of memory to complete 124 * request 125 */ 126 int _service_arp_request(dpdk::wait_req* req); 127 128 /*! 129 * Helper function for I/O thread to do a burst of packet retrieval and 130 * processing on an RX queue 131 * 132 * \param port the DPDK NIC port used for RX 133 * \param queue the DMA queue on the port to recv from 134 */ 135 int _rx_burst(dpdk::dpdk_port* port, dpdk::queue_id_t queue); 136 137 /*! 138 * Helper function for I/O thread to do a burst of packet transmission on a 139 * TX queue 140 * 141 * \param port the DPDK NIC port used for TX 142 * \return number of buffers transmitted 143 */ 144 int _tx_burst(dpdk::dpdk_port* port); 145 146 /*! 147 * Helper function for I/O thread to release a burst of buffers from an RX 148 * release queue 149 * 150 * \param port the DPDK NIC port used for RX 151 * \return number of buffers released 152 */ 153 int _rx_release(dpdk::dpdk_port* port); 154 155 /*! 156 * Helper function for I/O thread to do send an ARP request 157 * 158 * \param port the DPDK NIC port to send the ARP request through 159 * \param queue the DMA queue on the port to send to 160 * \param ip the IPv4 address for which the caller is seeking a MAC address 161 */ 162 int _send_arp_request( 163 dpdk::dpdk_port* port, dpdk::queue_id_t queue, dpdk::ipv4_addr ip); 164 165 /*! 166 * Helper function for I/O thread to process an ARP request/reply 167 * 168 * \param port the DPDK NIC port to send any ARP replies from 169 * \param queue the DMA queue on the port to send ARP replies to 170 * \param arp_frame a pointer to the ARP frame 171 */ 172 int _process_arp( 173 dpdk::dpdk_port* port, dpdk::queue_id_t queue_id, struct arp_hdr* arp_frame); 174 175 /*! 176 * Helper function for I/O thread to process an IPv4 packet 177 * 178 * \param port the DPDK NIC port to send any ARP replies from 179 * \param mbuf a pointer to the packet buffer container 180 * \param pkt a pointer to the IPv4 header of the packet 181 */ 182 int _process_ipv4(dpdk::dpdk_port* port, struct rte_mbuf* mbuf, struct ipv4_hdr* pkt); 183 184 /*! 185 * Helper function for I/O thread to process an IPv4 packet 186 * 187 * \param port the DPDK NIC port to send any ARP replies from 188 * \param mbuf a pointer to the packet buffer container 189 * \param pkt a pointer to the UDP header of the packet 190 * \param bcast whether this packet was destined for the port's broadcast 191 * IPv4 address 192 */ 193 int _process_udp( 194 dpdk::dpdk_port* port, struct rte_mbuf* mbuf, struct udp_hdr* pkt, bool bcast); 195 196 /*! 197 * Helper function to get a unique client ID 198 * 199 * \return a unique client ID 200 */ 201 uint16_t _get_unique_client_id(); 202 203 /*! 204 * Attempt to wake client 205 */ 206 void _wake_client(dpdk_io_if* dpdk_io); 207 208 //! The reference to the DPDK context 209 std::weak_ptr<dpdk::dpdk_ctx> _ctx; 210 //! The lcore running this dpdk_io_service's work routine 211 unsigned int _lcore_id; 212 //! The NIC ports served by this dpdk_io_service 213 std::vector<dpdk::dpdk_port*> _ports; 214 //! The set of TX queues associated with a given port 215 std::unordered_map<dpdk::port_id_t, std::list<dpdk_send_io*>> _tx_queues; 216 //! The list of recv_io for each port 217 std::unordered_map<dpdk::port_id_t, std::list<dpdk_recv_io*>> _recv_xport_map; 218 //! The RX table, which provides lists of dpdk_recv_io for an IPv4 tuple 219 struct rte_hash* _rx_table; 220 //! Service queue for clients to make requests 221 dpdk::service_queue _servq; 222 //! Retry list for waking clients 223 dpdk_io_if* _retry_head = NULL; 224 225 //! Mutex to protect below data structures 226 std::mutex _mutex; 227 //! The recv links attached to this I/O service (managed client side) 228 std::list<recv_link_if::sptr> _recv_links; 229 //! The send links attached to this I/O service (managed client side) 230 std::list<send_link_if::sptr> _send_links; 231 //! Set of IDs for new clients 232 std::set<uint16_t> _client_id_set; 233 //! Next ID to try 234 uint16_t _next_client_id; 235 236 static constexpr int MAX_PENDING_SERVICE_REQS = 32; 237 static constexpr int MAX_FLOWS = 128; 238 static constexpr int MAX_CLIENTS = 2048; 239 static constexpr int RX_BURST_SIZE = 16; 240 static constexpr int TX_BURST_SIZE = 16; 241 }; 242 243 }} // namespace uhd::transport 244