1 //
2 // Copyright 2019 Ettus Research, a National Instruments brand
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 //
6 
7 #pragma once
8 
9 #include <uhd/config.hpp>
10 #include <uhd/transport/buffer_pool.hpp>
11 #include <uhd/types/device_addr.hpp>
12 #include <uhdlib/transport/dpdk/common.hpp>
13 #include <uhdlib/transport/link_if.hpp>
14 #include <uhdlib/transport/links.hpp>
15 #include <rte_udp.h>
16 #include <cassert>
17 #include <string>
18 #include <vector>
19 
20 namespace uhd { namespace transport {
21 
22 /*!
23  * A zero copy transport interface to the dpdk DMA library.
24  */
25 class udp_dpdk_link : public virtual recv_link_if, public virtual send_link_if
26 {
27 public:
28     using sptr = std::shared_ptr<udp_dpdk_link>;
29 
30     udp_dpdk_link(const dpdk::port_id_t port_id,
31         const std::string& remote_addr,
32         const std::string& remote_port,
33         const std::string& local_port,
34         const link_params_t& params);
35 
36     ~udp_dpdk_link();
37 
38     /*!
39      * Make a new dpdk link. Get port ID from routing table.
40      *
41      * \param remote_addr Remote IP address
42      * \param remote_port Remote UDP port
43      * \param params Values for frame sizes, num frames, and buffer sizes
44      * \return a shared_ptr to a new udp dpdk link
45      */
46     static sptr make(const std::string& remote_addr,
47         const std::string& remote_port,
48         const link_params_t& params);
49 
50     /*!
51      * Make a new dpdk link. User specifies DPDK port ID directly.
52      *
53      * \param port_id DPDK port ID to use for communication
54      * \param remote_addr Remote IP address
55      * \param remote_port Remote UDP port
56      * \param local_port Local UDP port
57      * \param params Values for frame sizes, num frames, and buffer sizes
58      * \return a shared_ptr to a new udp dpdk link
59      */
60     static sptr make(const dpdk::port_id_t port_id,
61         const std::string& remote_addr,
62         const std::string& remote_port,
63         const std::string& local_port,
64         const link_params_t& params);
65 
66     /*!
67      * Get the associated dpdk_port
68      *
69      * \return a pointer to the dpdk_port used by this link
70      */
get_port()71     inline dpdk::dpdk_port* get_port()
72     {
73         return _port;
74     }
75 
76     /*!
77      * Get the DMA queue associated with this link
78      *
79      * \return the queue ID for this link's DMA queue
80      */
get_queue_id()81     inline dpdk::queue_id_t get_queue_id()
82     {
83         return _queue;
84     }
85 
86     /*!
87      * Get the local UDP port used by this link
88      *
89      * \return the local UDP port, in network order
90      */
get_local_port()91     inline uint16_t get_local_port()
92     {
93         return _local_port;
94     }
95 
96     /*!
97      * Get the remote UDP port used by this link
98      *
99      * \return the remote UDP port, in network order
100      */
get_remote_port()101     inline uint16_t get_remote_port()
102     {
103         return _remote_port;
104     }
105 
106     /*!
107      * Get the remote IPv4 address used by this link
108      *
109      * \return the remote IPv4 address, in network order
110      */
get_remote_ipv4()111     inline uint32_t get_remote_ipv4()
112     {
113         return _remote_ipv4;
114     }
115 
116     /*!
117      * Set the remote host's MAC address
118      * This MAC address must be filled in for the remote IPv4 address before
119      * the link can reach its destination.
120      *
121      * \param mac the remote host's MAC address
122      */
set_remote_mac(struct ether_addr & mac)123     inline void set_remote_mac(struct ether_addr& mac)
124     {
125         ether_addr_copy(&mac, &_remote_mac);
126     }
127 
128     /*!
129      * Get the remote host's MAC address
130      *
131      * \param mac Where to write the MAC address
132      */
get_remote_mac(struct ether_addr & dst)133     inline void get_remote_mac(struct ether_addr& dst)
134     {
135         ether_addr_copy(&_remote_mac, &dst);
136     }
137 
138     /*!
139      * Get the number of frame buffers that can be queued by this link.
140      */
get_num_send_frames() const141     size_t get_num_send_frames() const
142     {
143         return _num_send_frames;
144     }
145 
146     /*!
147      * Get the maximum capacity of a frame buffer.
148      */
get_send_frame_size() const149     size_t get_send_frame_size() const
150     {
151         return _send_frame_size;
152     }
153 
154     /*!
155      * Get the physical adapter ID used for this link
156      */
get_send_adapter_id() const157     inline adapter_id_t get_send_adapter_id() const
158     {
159         return _adapter_id;
160     }
161 
162     /*!
163      * Get the number of frame buffers that can be queued by this link.
164      */
get_num_recv_frames() const165     size_t get_num_recv_frames() const
166     {
167         return _num_recv_frames;
168     }
169 
170     /*!
171      * Get the maximum capacity of a frame buffer.
172      */
get_recv_frame_size() const173     size_t get_recv_frame_size() const
174     {
175         return _recv_frame_size;
176     }
177 
178     /*!
179      * Get the physical adapter ID used for this link
180      */
get_recv_adapter_id() const181     inline adapter_id_t get_recv_adapter_id() const
182     {
183         return _adapter_id;
184     }
185 
186     /*!
187      * Enqueue a received mbuf, which can be pulled via get_recv_buff()
188      */
189     void enqueue_recv_mbuf(struct rte_mbuf* mbuf);
190 
191     /*!
192      * Receive a packet and return a frame buffer containing the packet data.
193      * The timeout argument is ignored.
194      *
195      * Received buffers are pulled from the frame buffer list. No buffers can
196      * be retrieved unless the corresponding rte_mbufs were placed in the list
197      * via the enqueue_recv_mbuf() method.
198      *
199      * \return a frame buffer, or null uptr if timeout occurs
200      */
201     frame_buff::uptr get_recv_buff(int32_t /*timeout_ms*/);
202 
203     /*!
204      * Release a frame buffer, allowing the link driver to reuse it.
205      *
206      * \param buffer frame buffer to release for reuse by the link
207      */
208     void release_recv_buff(frame_buff::uptr buff);
209 
210     /*!
211      * Get an empty frame buffer in which to write packet contents.
212      *
213      * \param timeout_ms a positive timeout value specifies the maximum number
214                          of ms to wait, a negative value specifies to block
215                          until successful, and a value of 0 specifies no wait.
216      * \return a frame buffer, or null uptr if timeout occurs
217      */
218     frame_buff::uptr get_send_buff(int32_t /*timeout_ms*/);
219 
220     /*!
221      * Send a packet with the contents of the frame buffer and release the
222      * buffer, allowing the link driver to reuse it. If the size of the frame
223      * buffer is 0, the buffer is released with no packet being sent.
224      *
225      * Note that this function will only fill in the L2 header and send the
226      * mbuf. The L3 and L4 headers, in addition to the lengths in the rte_mbuf
227      * fields, must be set in the I/O service.
228      *
229      * \param buffer frame buffer containing packet data
230      *
231      * Throws an exception if an I/O error occurs while sending
232      */
233     void release_send_buff(frame_buff::uptr buff);
234 
235 private:
236     //! A reference to the DPDK context
237     dpdk::dpdk_ctx::sptr _ctx;
238     //! The DPDK NIC port used by this link
239     dpdk::dpdk_port* _port;
240     //! Local UDP port, in network order
241     uint16_t _local_port;
242     //! Remote UDP port, in network order
243     uint16_t _remote_port;
244     //! Remote IPv4 address, in network order
245     uint32_t _remote_ipv4;
246     //! Remote host's MAC address
247     struct ether_addr _remote_mac;
248     //! Number of recv frames is not validated
249     size_t _num_recv_frames;
250     //! Maximum bytes of UDP payload data in recv frame
251     size_t _recv_frame_size;
252     //! Number of send frames is not validated
253     size_t _num_send_frames;
254     //! Maximum bytes of UDP payload data in send frame
255     size_t _send_frame_size;
256     //! Registered adapter ID for this link's DPDK NIC port
257     adapter_id_t _adapter_id;
258     //! The RX frame buff list head
259     dpdk::dpdk_frame_buff* _recv_buff_head = nullptr;
260     // TODO: Implement ability to use multiple queues
261     dpdk::queue_id_t _queue = 0;
262 };
263 
264 }} // namespace uhd::transport
265