Line data Source code
1 : /**
2 : * @file EALSetup.hpp EAL setup functions for DPDK
3 : *
4 : * This is part of the DUNE DAQ , copyright 2020.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 : // #ifndef DPDKLIBS_INCLUDE_DPDKLIBS_EALSETUP_HPP_
9 : // #define DPDKLIBS_INCLUDE_DPDKLIBS_EALSETUP_HPP_
10 :
11 : #include "logging/Logging.hpp"
12 :
13 : #include <boost/program_options/parsers.hpp>
14 :
15 : #include "dpdklibs/EALSetup.hpp"
16 : #include "dpdklibs/Issues.hpp"
17 :
18 : #include <rte_eal.h>
19 : #include <rte_ethdev.h>
20 :
21 : namespace dunedaq {
22 : namespace dpdklibs {
23 : namespace ealutils {
24 :
25 : #define NUM_MBUFS 8191
26 : #define MBUF_CACHE_SIZE 250
27 :
28 : #define PG_JUMBO_FRAME_LEN (9600 + RTE_ETHER_CRC_LEN + RTE_ETHER_HDR_LEN)
29 : #ifndef RTE_JUMBO_ETHER_MTU
30 : #define RTE_JUMBO_ETHER_MTU (PG_JUMBO_FRAME_LEN - RTE_ETHER_HDR_LEN - RTE_ETHER_CRC_LEN) /*< Ethernet MTU. */
31 : #endif
32 :
33 : // static volatile uint8_t dpdk_quit_signal;
34 :
35 : static const struct rte_eth_conf iface_conf_default = {
36 : .rxmode = {
37 : .mtu = 9000,
38 : .max_lro_pkt_size = 9000,
39 : //.split_hdr_size = 0, // deprecated in dpdk@22.10
40 : .offloads = (RTE_ETH_RX_OFFLOAD_TIMESTAMP
41 : | RTE_ETH_RX_OFFLOAD_IPV4_CKSUM
42 : | RTE_ETH_RX_OFFLOAD_UDP_CKSUM),
43 : },
44 :
45 : .txmode = {
46 : .offloads = (RTE_ETH_TX_OFFLOAD_MULTI_SEGS),
47 : },
48 : };
49 :
50 : std::string
51 0 : get_mac_addr_str(const rte_ether_addr& addr)
52 : {
53 0 : std::stringstream macstr;
54 0 : macstr << std::hex << static_cast<int>(addr.addr_bytes[0]) << ":" << static_cast<int>(addr.addr_bytes[1]) << ":"
55 0 : << static_cast<int>(addr.addr_bytes[2]) << ":" << static_cast<int>(addr.addr_bytes[3]) << ":"
56 0 : << static_cast<int>(addr.addr_bytes[4]) << ":" << static_cast<int>(addr.addr_bytes[5]) << std::dec;
57 0 : return macstr.str();
58 0 : }
59 :
60 : // Modifies Ethernet device configuration to multi-queue RSS with offload
61 : void
62 0 : iface_conf_rss_mode(struct rte_eth_conf& iface_conf, bool mode, bool offload)
63 : {
64 0 : if (mode) {
65 0 : iface_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
66 0 : if (offload) {
67 0 : iface_conf.rxmode.offloads |= RTE_ETH_RX_OFFLOAD_RSS_HASH;
68 : }
69 : } else {
70 0 : iface_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_NONE;
71 : }
72 0 : }
73 :
74 : // Enables RX in promiscuous mode for the Ethernet device.
75 : int
76 0 : iface_promiscuous_mode(std::uint16_t iface, bool mode)
77 : {
78 0 : int retval = -1;
79 0 : retval = rte_eth_promiscuous_get(iface);
80 0 : TLOG() << "Before modification attempt, promiscuous mode is: " << retval;
81 0 : if (mode) {
82 0 : retval = rte_eth_promiscuous_enable(iface);
83 : } else {
84 0 : retval = rte_eth_promiscuous_disable(iface);
85 : }
86 0 : if (retval != 0) {
87 0 : TLOG() << "Couldn't modify promiscuous mode of iface[" << iface << "]! Error code: " << retval;
88 : }
89 0 : retval = rte_eth_promiscuous_get(iface);
90 0 : TLOG() << "New promiscuous mode of iface[" << iface << "] is: " << retval;
91 0 : return retval;
92 : }
93 :
94 : int
95 0 : iface_init(uint16_t iface,
96 : uint16_t rx_rings,
97 : uint16_t tx_rings,
98 : uint16_t rx_ring_size,
99 : uint16_t tx_ring_size,
100 : std::map<int, std::unique_ptr<rte_mempool>>& mbuf_pool,
101 : bool with_reset,
102 : bool with_mq_rss,
103 : bool check_link_status)
104 : {
105 0 : struct rte_eth_conf iface_conf = iface_conf_default;
106 0 : uint16_t nb_rxd = rx_ring_size;
107 0 : uint16_t nb_txd = tx_ring_size;
108 0 : int retval = -1;
109 0 : uint16_t q;
110 0 : struct rte_eth_dev_info dev_info;
111 0 : struct rte_eth_txconf txconf;
112 0 : struct rte_eth_link link;
113 :
114 : // Get interface validity
115 0 : if (!rte_eth_dev_is_valid_port(iface)) {
116 0 : TLOG() << "Specified interface " << iface << " is not valid in EAL!";
117 0 : throw InvalidEALPort(ERS_HERE, iface);
118 : }
119 :
120 : // Get interface info
121 0 : if ((retval = rte_eth_dev_info_get(iface, &dev_info)) != 0) {
122 0 : TLOG() << "Error during getting device (iface " << iface << ") retval: " << retval;
123 0 : throw FailedToRetrieveInterfaceInfo(ERS_HERE, iface, retval);
124 : }
125 :
126 0 : TLOG() << "Iface " << iface << " RX Ring info :"
127 0 : << " min " << dev_info.rx_desc_lim.nb_min << " max " << dev_info.rx_desc_lim.nb_max << " align "
128 0 : << dev_info.rx_desc_lim.nb_align;
129 :
130 : // Carry out a reset of the interface
131 0 : if (with_reset) {
132 0 : if ((retval = rte_eth_dev_reset(iface)) != 0) {
133 0 : throw FailedToResetInterface(ERS_HERE, iface, retval);
134 : }
135 : }
136 :
137 : // Should we configure MQ RSS and offload?
138 0 : if (with_mq_rss) {
139 0 : iface_conf_rss_mode(iface_conf, true, true); // with_rss, with_offload
140 : // RSS
141 0 : if ((iface_conf.rxmode.mq_mode & RTE_ETH_MQ_RX_RSS_FLAG) != 0) {
142 0 : TLOG() << "Ethdev port config prepared with RX RSS mq_mode!";
143 0 : if ((iface_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_RSS_HASH) != 0) {
144 0 : TLOG() << "Ethdev port config prepared with RX RSS mq_mode with offloading is requested!";
145 : }
146 : }
147 : }
148 :
149 0 : TLOG() << "Configuring Iface " << iface << " rx rings: " << rx_rings << ", tx rings " << tx_rings;
150 :
151 : // Configure the Ethernet interface
152 0 : if ((retval = rte_eth_dev_configure(iface, rx_rings, tx_rings, &iface_conf)) != 0) {
153 0 : throw FailedToConfigureInterface(ERS_HERE, iface, "Device Configuration", retval);
154 : }
155 :
156 : // Set MTU of interface
157 0 : rte_eth_dev_set_mtu(iface, RTE_JUMBO_ETHER_MTU);
158 0 : {
159 0 : uint16_t mtu;
160 0 : rte_eth_dev_get_mtu(iface, &mtu);
161 0 : TLOG() << "Interface: " << iface << " MTU: " << mtu;
162 : }
163 :
164 : // Set PTYPE parsing. RS FIXME: This function needs to be factorized, with overall better offloading control.
165 : // On most Intel and Mellanox drivers, packet_type will automatically be set if:
166 : // - the hardware supports RTE_ETH_RX_OFFLOAD_*PTYPE
167 : // - and ptype RX parsing is enabled in the PMD
168 : // Some PMDs require calling:
169 0 : rte_eth_dev_set_ptypes(iface, RTE_PTYPE_L2_MASK | RTE_PTYPE_L3_MASK | RTE_PTYPE_L4_MASK, NULL, 0);
170 :
171 : // // Adjust RX/TX ring sizes
172 : // retval = rte_eth_dev_adjust_nb_rx_tx_desc(iface, &nb_rxd, &nb_txd);
173 : // if (retval != 0)
174 : // return retval;
175 :
176 0 : if ((retval = rte_eth_dev_adjust_nb_rx_tx_desc(iface, &nb_rxd, &nb_txd)) != 0) {
177 0 : throw FailedToConfigureInterface(ERS_HERE, iface, "Adjust tx/rx descriptors", retval);
178 : }
179 :
180 : // Allocate and set up RX queues for interface.
181 0 : for (q = 0; q < rx_rings; q++) {
182 : // retval = rte_eth_rx_queue_setup(iface, q, nb_rxd, rte_eth_dev_socket_id(iface), NULL, mbuf_pool[q].get());
183 0 : if ((retval = rte_eth_rx_queue_setup(iface, q, nb_rxd, rte_eth_dev_socket_id(iface), NULL, mbuf_pool[q].get())) <
184 : 0) {
185 : // return retval;
186 0 : throw FailedToConfigureInterface(ERS_HERE, iface, "Rx queues setup", retval);
187 : }
188 : }
189 :
190 0 : txconf = dev_info.default_txconf;
191 0 : txconf.offloads = iface_conf.txmode.offloads;
192 :
193 : // These values influenced by Sec. 8.4.4 of https://doc.dpdk.org/guides-1.8/prog_guide/poll_mode_drv.html
194 0 : txconf.tx_rs_thresh = 32;
195 0 : txconf.tx_free_thresh = 32;
196 0 : txconf.tx_thresh.wthresh = 0;
197 :
198 : // Allocate and set up TX queues for interface.
199 0 : for (q = 0; q < tx_rings; q++) {
200 0 : if ((retval = rte_eth_tx_queue_setup(iface, q, nb_txd, rte_eth_dev_socket_id(iface), &txconf)) < 0) {
201 0 : throw FailedToConfigureInterface(ERS_HERE, iface, "Tx queues setup", retval);
202 : }
203 : }
204 :
205 : // Start the Ethernet interface.
206 0 : if ((retval = rte_eth_dev_start(iface)) < 0) {
207 0 : throw FailedToConfigureInterface(ERS_HERE, iface, "MAC address retrival", retval);
208 : }
209 :
210 0 : if ((retval = rte_eth_link_get(iface, &link)) != 0) {
211 0 : throw FailedToRetrieveLinkStatus(ERS_HERE, iface, retval);
212 : }
213 :
214 0 : TLOG() << "Link: speed=" << link.link_speed << " duplex=" << link.link_duplex << " autoneg=" << link.link_autoneg
215 0 : << " status=" << link.link_status;
216 :
217 0 : if (check_link_status && link.link_status == 0) {
218 0 : throw LinkOffline(ERS_HERE, iface);
219 : }
220 :
221 : // Display the interface MAC address.
222 0 : struct rte_ether_addr addr;
223 0 : if ((retval = rte_eth_macaddr_get(iface, &addr)) == 0) {
224 0 : TLOG() << "MAC address: " << get_mac_addr_str(addr);
225 : } else {
226 0 : throw FailedToConfigureInterface(ERS_HERE, iface, "MAC address retrival", retval);
227 : }
228 :
229 : // Get interface info
230 0 : if ((retval = rte_eth_dev_info_get(iface, &dev_info)) != 0) {
231 0 : TLOG() << "Error during getting device (iface " << iface << ") retval: " << retval;
232 0 : throw FailedToConfigureInterface(ERS_HERE, iface, "Device information retrival", retval);
233 : }
234 :
235 0 : TLOG() << "Iface[" << iface << "] Rx Ring info:"
236 0 : << " min=" << dev_info.rx_desc_lim.nb_min << " max=" << dev_info.rx_desc_lim.nb_max
237 0 : << " align=" << dev_info.rx_desc_lim.nb_align;
238 0 : TLOG() << "Iface[" << iface << "] Tx Ring info:"
239 0 : << " min=" << dev_info.rx_desc_lim.nb_min << " max=" << dev_info.rx_desc_lim.nb_max
240 0 : << " align=" << dev_info.rx_desc_lim.nb_align;
241 :
242 0 : for (size_t j = 0; j < dev_info.nb_rx_queues; j++) {
243 :
244 0 : struct rte_eth_rxq_info queue_info;
245 0 : int count;
246 :
247 0 : retval = rte_eth_rx_queue_info_get(iface, j, &queue_info);
248 0 : if (retval != 0)
249 0 : continue;
250 :
251 0 : count = rte_eth_rx_queue_count(iface, j);
252 0 : TLOG() << "rx[" << j << "] descriptors=" << count << "/" << queue_info.nb_desc
253 0 : << " scattered=" << (queue_info.scattered_rx ? "yes" : "no")
254 0 : << " conf.drop_en=" << (queue_info.conf.rx_drop_en ? "yes" : "no")
255 0 : << " conf.rx_deferred_start=" << (queue_info.conf.rx_deferred_start ? "yes" : "no")
256 0 : << " rx_buf_size=" << queue_info.rx_buf_size;
257 : }
258 :
259 0 : return 0;
260 : }
261 :
262 : std::unique_ptr<rte_mempool>
263 0 : get_mempool(const std::string& pool_name, int num_mbufs, int mbuf_cache_size, int data_room_size, int socket_id)
264 : {
265 0 : TLOG() << "get_mempool with: NUM_MBUFS = " << num_mbufs << " | MBUF_CACHE_SIZE = " << mbuf_cache_size
266 0 : << " | data_room_size = " << data_room_size << " | SOCKET_ID = " << socket_id;
267 :
268 0 : struct rte_mempool* mbuf_pool;
269 0 : mbuf_pool = rte_pktmbuf_pool_create(pool_name.c_str(), num_mbufs, mbuf_cache_size, 0, data_room_size, socket_id);
270 :
271 0 : if (mbuf_pool == NULL) {
272 : // ers fatal
273 0 : rte_exit(EXIT_FAILURE, "ERROR: Cannot create rte_mempool!\n");
274 : }
275 0 : return std::unique_ptr<rte_mempool>(mbuf_pool);
276 : }
277 :
278 : std::vector<const char*>
279 0 : construct_eal_argv(const std::vector<std::string>& std_argv)
280 : {
281 0 : std::vector<const char*> vec_argv;
282 0 : for (int i = 0; i < std_argv.size(); i++) {
283 0 : vec_argv.insert(vec_argv.end(), std_argv[i].data());
284 : }
285 0 : return vec_argv;
286 0 : }
287 :
288 : void
289 0 : init_eal(int argc, const char* argv[])
290 : {
291 :
292 0 : std::stringstream ss;
293 0 : for (size_t i(0); i < argc; ++i) {
294 0 : ss << argv[i] << " ";
295 : }
296 0 : TLOG() << "EAL init arguments: " << ss.str();
297 :
298 : // Init EAL
299 0 : int ret = rte_eal_init(argc, (char**)argv);
300 0 : if (ret < 0) {
301 0 : rte_exit(EXIT_FAILURE, "ERROR: EAL initialization failed.\n");
302 : }
303 0 : TLOG() << "EAL initialized with provided parameters.";
304 0 : }
305 :
306 : void
307 0 : init_eal(const std::vector<std::string>& args)
308 : {
309 :
310 0 : std::vector<const char*> eal_argv = ealutils::construct_eal_argv(args);
311 0 : const char** constructed_eal_argv = eal_argv.data();
312 0 : int constructed_eal_argc = args.size();
313 0 : ealutils::init_eal(constructed_eal_argc, constructed_eal_argv);
314 0 : }
315 :
316 : int
317 0 : get_available_ifaces()
318 : {
319 : // Check that there is an even number of interfaces to send/receive on
320 0 : unsigned nb_ifaces;
321 0 : nb_ifaces = rte_eth_dev_count_avail();
322 0 : TLOG() << "Available interfaces: " << nb_ifaces;
323 0 : return nb_ifaces;
324 : }
325 :
326 : int
327 0 : wait_for_lcores()
328 : {
329 0 : int lcore_id;
330 0 : int ret = 0;
331 0 : RTE_LCORE_FOREACH_WORKER(lcore_id)
332 : {
333 : // TLOG() << "Waiting for lcore[" << lcore_id << "] to finish packet processing.";
334 0 : ret = rte_eal_wait_lcore(lcore_id);
335 : }
336 0 : return ret;
337 : }
338 :
339 : void
340 0 : finish_eal()
341 : {
342 0 : rte_eal_cleanup();
343 0 : }
344 :
345 : } // namespace ealutils
346 : } // namespace dpdklibs
347 : } // namespace dunedaq
348 : // #endif // DPDKLIBS_INCLUDE_DPDKLIBS_EALSETUP_HPP_
|