NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
dpdk_utils.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021-2024 NVIDIA CORPORATION AND AFFILIATES. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are permitted
5  * provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright notice, this list of
7  * conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright notice, this list of
9  * conditions and the following disclaimer in the documentation and/or other materials
10  * provided with the distribution.
11  * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
12  * to endorse or promote products derived from this software without specific prior written
13  * permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21  * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25 
26 #include <rte_ethdev.h>
27 #include <rte_ether.h>
28 #include <rte_malloc.h>
29 
30 #include <doca_log.h>
31 #include <doca_mmap.h>
32 #include <doca_buf_inventory.h>
33 
34 #include "dpdk_utils.h"
35 
37 
38 #define RSS_KEY_LEN 40
39 
40 #ifndef IPv6_BYTES
41 #define IPv6_BYTES_FMT \
42  "%02x%02x:%02x%02x:%02x%02x:%02x%02x:" \
43  "%02x%02x:%02x%02x:%02x%02x:%02x%02x"
44 #define IPv6_BYTES(addr) \
45  addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], \
46  addr[12], addr[13], addr[14], addr[15]
47 #endif
48 
50  struct doca_dev *device; /* DOCA device used to register memory */
51  struct doca_mmap **mmap_arr; /* DOCA mmap array that has mapped the packet buffers */
52  uint32_t nb_mmaps; /* Number of elements in mmap_arr */
53 };
54 
55 /*
56  * Bind port to all the peer ports
57  *
58  * @port_id [in]: port ID
59  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
60  */
61 static doca_error_t bind_hairpin_queues(uint16_t port_id)
62 {
63  /* Configure the Rx and Tx hairpin queues for the selected port */
64  int result = 0, peer_port, peer_ports_len;
65  uint16_t peer_ports[RTE_MAX_ETHPORTS];
66 
67  /* bind current Tx to all peer Rx */
68  peer_ports_len = rte_eth_hairpin_get_peer_ports(port_id, peer_ports, RTE_MAX_ETHPORTS, 1);
69  if (peer_ports_len < 0) {
70  DOCA_LOG_ERR("Failed to get hairpin peer Rx ports of port %d, (%d)", port_id, peer_ports_len);
71  return DOCA_ERROR_DRIVER;
72  }
73  for (peer_port = 0; peer_port < peer_ports_len; peer_port++) {
74  result = rte_eth_hairpin_bind(port_id, peer_ports[peer_port]);
75  if (result < 0) {
76  DOCA_LOG_ERR("Failed to bind hairpin queues (%d)", result);
77  return DOCA_ERROR_DRIVER;
78  }
79  }
80  /* bind all peer Tx to current Rx */
81  peer_ports_len = rte_eth_hairpin_get_peer_ports(port_id, peer_ports, RTE_MAX_ETHPORTS, 0);
82  if (peer_ports_len < 0) {
83  DOCA_LOG_ERR("Failed to get hairpin peer Tx ports of port %d, (%d)", port_id, peer_ports_len);
84  return DOCA_ERROR_DRIVER;
85  }
86 
87  for (peer_port = 0; peer_port < peer_ports_len; peer_port++) {
88  result = rte_eth_hairpin_bind(peer_ports[peer_port], port_id);
89  if (result < 0) {
90  DOCA_LOG_ERR("Failed to bind hairpin queues (%d)", result);
91  return DOCA_ERROR_DRIVER;
92  }
93  }
94  return DOCA_SUCCESS;
95 }
96 
97 /*
98  * Unbind port from all its peer ports
99  *
100  * @port_id [in]: port ID
101  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
102  */
103 static doca_error_t unbind_hairpin_queues(uint16_t port_id)
104 {
105  /* Configure the Rx and Tx hairpin queues for the selected port */
106  int result = 0, peer_port, peer_ports_len;
107  uint16_t peer_ports[RTE_MAX_ETHPORTS];
108 
109  /* unbind current Tx from all peer Rx */
110  peer_ports_len = rte_eth_hairpin_get_peer_ports(port_id, peer_ports, RTE_MAX_ETHPORTS, 1);
111  if (peer_ports_len < 0) {
112  DOCA_LOG_ERR("Failed to get hairpin peer Tx ports of port %d, (%d)", port_id, peer_ports_len);
113  return DOCA_ERROR_DRIVER;
114  }
115 
116  for (peer_port = 0; peer_port < peer_ports_len; peer_port++) {
117  result = rte_eth_hairpin_unbind(port_id, peer_ports[peer_port]);
118  if (result < 0) {
119  DOCA_LOG_ERR("Failed to bind hairpin queues (%d)", result);
120  return DOCA_ERROR_DRIVER;
121  }
122  }
123  /* unbind all peer Tx from current Rx */
124  peer_ports_len = rte_eth_hairpin_get_peer_ports(port_id, peer_ports, RTE_MAX_ETHPORTS, 0);
125  if (peer_ports_len < 0) {
126  DOCA_LOG_ERR("Failed to get hairpin peer Tx ports of port %d, (%d)", port_id, peer_ports_len);
127  return DOCA_ERROR_DRIVER;
128  }
129  for (peer_port = 0; peer_port < peer_ports_len; peer_port++) {
130  result = rte_eth_hairpin_unbind(peer_ports[peer_port], port_id);
131  if (result < 0) {
132  DOCA_LOG_ERR("Failed to bind hairpin queues (%d)", result);
133  return DOCA_ERROR_DRIVER;
134  }
135  }
136  return DOCA_SUCCESS;
137 }
138 
139 /*
140  * Set up all hairpin queues
141  *
142  * @port_id [in]: port ID
143  * @peer_port_id [in]: peer port ID
144  * @reserved_hairpin_q_list [in]: list of hairpin queues index
145  * @hairpin_queue_len [in]: length of reserved_hairpin_q_list
146  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
147  */
148 static doca_error_t setup_hairpin_queues(uint16_t port_id,
149  uint16_t peer_port_id,
150  uint16_t *reserved_hairpin_q_list,
151  int hairpin_queue_len)
152 {
153  /* Port:
154  * 0. RX queue
155  * 1. RX hairpin queue rte_eth_rx_hairpin_queue_setup
156  * 2. TX hairpin queue rte_eth_tx_hairpin_queue_setup
157  */
158 
159  int result = 0, hairpin_q;
160  uint16_t nb_tx_rx_desc = 2048;
161  uint32_t manual = 1;
162  uint32_t tx_exp = 1;
163  struct rte_eth_hairpin_conf hairpin_conf = {
164  .peer_count = 1,
165  .manual_bind = !!manual,
166  .tx_explicit = !!tx_exp,
167  .peers[0] = {peer_port_id, 0},
168  };
169 
170  for (hairpin_q = 0; hairpin_q < hairpin_queue_len; hairpin_q++) {
171  // TX
172  hairpin_conf.peers[0].queue = reserved_hairpin_q_list[hairpin_q];
173  result = rte_eth_tx_hairpin_queue_setup(port_id,
174  reserved_hairpin_q_list[hairpin_q],
175  nb_tx_rx_desc,
176  &hairpin_conf);
177  if (result < 0) {
178  DOCA_LOG_ERR("Failed to setup hairpin queues (%d)", result);
179  return DOCA_ERROR_DRIVER;
180  }
181 
182  // RX
183  hairpin_conf.peers[0].queue = reserved_hairpin_q_list[hairpin_q];
184  result = rte_eth_rx_hairpin_queue_setup(port_id,
185  reserved_hairpin_q_list[hairpin_q],
186  nb_tx_rx_desc,
187  &hairpin_conf);
188  if (result < 0) {
189  DOCA_LOG_ERR("Failed to setup hairpin queues (%d)", result);
190  return DOCA_ERROR_DRIVER;
191  }
192  }
193  return DOCA_SUCCESS;
194 }
195 
196 /*
197  * Unbind hairpin queues from all ports
198  *
199  * @nb_ports [in]: number of ports
200  */
201 static void disable_hairpin_queues(uint16_t nb_ports)
202 {
204  uint16_t port_id;
205 
206  for (port_id = 0; port_id < nb_ports; port_id++) {
207  if (!rte_eth_dev_is_valid_port(port_id))
208  continue;
209  result = unbind_hairpin_queues(port_id);
210  if (result != DOCA_SUCCESS)
211  DOCA_LOG_ERR("Disabling hairpin queues failed: err=%d, port=%u", result, port_id);
212  }
213 }
214 
215 /*
216  * Bind hairpin queues to all ports
217  *
218  * @nb_ports [in]: number of ports
219  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
220  */
222 {
223  uint16_t port_id;
224  uint16_t n = 0;
226 
227  for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
228  if (!rte_eth_dev_is_valid_port(port_id))
229  /* the device ID might not be contiguous */
230  continue;
231  result = bind_hairpin_queues(port_id);
232  if (result != DOCA_SUCCESS) {
233  DOCA_LOG_ERR("Hairpin bind failed on port=%u", port_id);
234  disable_hairpin_queues(port_id);
235  return result;
236  }
237  if (++n >= nb_ports)
238  break;
239  }
240  return DOCA_SUCCESS;
241 }
242 
243 /*
244  * Creates a new mempool in memory to hold the mbufs
245  *
246  * @total_nb_mbufs [in]: the number of elements in the mbuf pool
247  * @mbuf_size [in]: the size of each mbuf, including headroom
248  * @mbuf_pool [out]: the allocated pool
249  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
250  */
251 static doca_error_t allocate_mempool(const uint32_t total_nb_mbufs,
252  const uint32_t mbuf_size,
253  struct rte_mempool **mbuf_pool)
254 {
255  *mbuf_pool =
256  rte_pktmbuf_pool_create("MBUF_POOL", total_nb_mbufs, MBUF_CACHE_SIZE, 0, mbuf_size, rte_socket_id());
257  if (*mbuf_pool == NULL) {
258  DOCA_LOG_ERR("Cannot allocate mbuf pool");
259  return DOCA_ERROR_DRIVER;
260  }
261  return DOCA_SUCCESS;
262 }
263 
264 /*
265  * Initialize all the port resources
266  *
267  * @mbuf_pool [in]: packet mbuf pool
268  * @port [in]: the port ID
269  * @app_config [in]: application DPDK configuration values
270  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
271  */
272 static doca_error_t port_init(struct rte_mempool *mbuf_pool, uint8_t port, struct application_dpdk_config *app_config)
273 {
275  int ret = 0;
276  int symmetric_hash_key_length = RSS_KEY_LEN;
277  const uint16_t nb_hairpin_queues = app_config->port_config.nb_hairpin_q;
278  const uint16_t rx_rings = app_config->port_config.nb_queues;
279  const uint16_t tx_rings = app_config->port_config.nb_queues;
280  const uint16_t rss_support = !!(app_config->port_config.rss_support && (app_config->port_config.nb_queues > 1));
281  bool isolated = !!app_config->port_config.isolated_mode;
282  uint16_t q, queue_index;
283  struct rte_ether_addr addr;
284  struct rte_eth_dev_info dev_info;
285  struct rte_flow_error error;
286  uint8_t symmetric_hash_key[RSS_KEY_LEN] = {
287  0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
288  0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
289  0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
290  };
291  const struct rte_eth_conf port_conf_default = {
292  .lpbk_mode = app_config->port_config.lpbk_support,
293  .rx_adv_conf =
294  {
295  .rss_conf =
296  {
297  .rss_key_len = symmetric_hash_key_length,
298  .rss_key = symmetric_hash_key,
299  .rss_hf = (RTE_ETH_RSS_IP | RTE_ETH_RSS_UDP | RTE_ETH_RSS_TCP),
300  },
301  },
302  };
303  struct rte_eth_conf port_conf = port_conf_default;
304 
305  ret = rte_eth_dev_info_get(port, &dev_info);
306  if (ret < 0) {
307  DOCA_LOG_ERR("Failed getting device (port %u) info, error=%s", port, strerror(-ret));
308  return DOCA_ERROR_DRIVER;
309  }
310  if (*dev_info.dev_flags & RTE_ETH_DEV_REPRESENTOR && app_config->port_config.switch_mode) {
311  DOCA_LOG_INFO("Skip represent port %d init in switch mode", port);
312  return DOCA_SUCCESS;
313  }
314 
315  port_conf.rxmode.mq_mode = rss_support ? RTE_ETH_MQ_RX_RSS : RTE_ETH_MQ_RX_NONE;
316  port_conf.txmode.offloads = app_config->port_config.tx_offloads;
317 
318  /* Configure the Ethernet device */
319  ret = rte_eth_dev_configure(port, rx_rings + nb_hairpin_queues, tx_rings + nb_hairpin_queues, &port_conf);
320  if (ret < 0) {
321  DOCA_LOG_ERR("Failed to configure the ethernet device - (%d)", ret);
322  return DOCA_ERROR_DRIVER;
323  }
324  if (port_conf_default.rx_adv_conf.rss_conf.rss_hf != port_conf.rx_adv_conf.rss_conf.rss_hf) {
325  DOCA_LOG_DBG("Port %u modified RSS hash function based on hardware support, requested:%#" PRIx64
326  " configured:%#" PRIx64 "",
327  port,
328  port_conf_default.rx_adv_conf.rss_conf.rss_hf,
329  port_conf.rx_adv_conf.rss_conf.rss_hf);
330  }
331 
332  /* Enable RX in promiscuous mode for the Ethernet device */
333  ret = rte_eth_promiscuous_enable(port);
334  if (ret < 0) {
335  DOCA_LOG_ERR("Failed to Enable RX in promiscuous mode - (%d)", ret);
336  return DOCA_ERROR_DRIVER;
337  }
338 
339  /* Allocate and set up RX queues according to number of cores per Ethernet port */
340  for (q = 0; q < rx_rings; q++) {
341  ret = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, rte_eth_dev_socket_id(port), NULL, mbuf_pool);
342  if (ret < 0) {
343  DOCA_LOG_ERR("Failed to set up RX queues - (%d)", ret);
344  return DOCA_ERROR_DRIVER;
345  }
346  }
347 
348  /* Allocate and set up TX queues according to number of cores per Ethernet port */
349  for (q = 0; q < tx_rings; q++) {
350  ret = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, rte_eth_dev_socket_id(port), NULL);
351  if (ret < 0) {
352  DOCA_LOG_ERR("Failed to set up TX queues - (%d)", ret);
353  return DOCA_ERROR_DRIVER;
354  }
355  }
356 
357  /* Enabled hairpin queue before port start */
358  if (nb_hairpin_queues) {
359  uint16_t rss_queue_list[nb_hairpin_queues];
360 
361  if (app_config->port_config.self_hairpin && rte_eth_dev_is_valid_port(port ^ 1)) {
362  /* Hairpin to both self and peer */
363  assert((nb_hairpin_queues % 2) == 0);
364  for (queue_index = 0; queue_index < nb_hairpin_queues / 2; queue_index++)
365  rss_queue_list[queue_index] = app_config->port_config.nb_queues + queue_index * 2;
366  result = setup_hairpin_queues(port, port, rss_queue_list, nb_hairpin_queues / 2);
367  if (result != DOCA_SUCCESS) {
368  DOCA_LOG_ERR("Cannot hairpin self port %" PRIu8 ", ret: %s",
369  port,
371  return result;
372  }
373  for (queue_index = 0; queue_index < nb_hairpin_queues / 2; queue_index++)
374  rss_queue_list[queue_index] = app_config->port_config.nb_queues + queue_index * 2 + 1;
375  result = setup_hairpin_queues(port, port ^ 1, rss_queue_list, nb_hairpin_queues / 2);
376  if (result != DOCA_SUCCESS) {
377  DOCA_LOG_ERR("Cannot hairpin peer port %" PRIu8 ", ret: %s",
378  port ^ 1,
380  return result;
381  }
382  } else {
383  /* Hairpin to self or peer */
384  for (queue_index = 0; queue_index < nb_hairpin_queues; queue_index++)
385  rss_queue_list[queue_index] = app_config->port_config.nb_queues + queue_index;
386  if (rte_eth_dev_is_valid_port(port ^ 1))
387  result = setup_hairpin_queues(port, port ^ 1, rss_queue_list, nb_hairpin_queues);
388  else
389  result = setup_hairpin_queues(port, port, rss_queue_list, nb_hairpin_queues);
390  if (result != DOCA_SUCCESS) {
391  DOCA_LOG_ERR("Cannot hairpin port %" PRIu8 ", ret=%d", port, result);
392  return result;
393  }
394  }
395  }
396 
397  /* Set isolated mode (true or false) before port start */
398  ret = rte_flow_isolate(port, isolated, &error);
399  if (ret < 0) {
400  DOCA_LOG_ERR("Port %u could not be set isolated mode to %s (%s)",
401  port,
402  isolated ? "true" : "false",
403  error.message);
404  return DOCA_ERROR_DRIVER;
405  }
406  if (isolated)
407  DOCA_LOG_INFO("Ingress traffic on port %u is in isolated mode", port);
408 
409  /* Start the Ethernet port */
410  ret = rte_eth_dev_start(port);
411  if (ret < 0) {
412  DOCA_LOG_ERR("Cannot start port %" PRIu8 ", ret=%d", port, ret);
413  return DOCA_ERROR_DRIVER;
414  }
415 
416  /* Display the port MAC address */
417  rte_eth_macaddr_get(port, &addr);
418  DOCA_LOG_DBG("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "",
419  (unsigned int)port,
420  addr.addr_bytes[0],
421  addr.addr_bytes[1],
422  addr.addr_bytes[2],
423  addr.addr_bytes[3],
424  addr.addr_bytes[4],
425  addr.addr_bytes[5]);
426 
427  /*
428  * Check that the port is on the same NUMA node as the polling thread
429  * for best performance.
430  */
431  if (rte_eth_dev_socket_id(port) > 0 && rte_eth_dev_socket_id(port) != (int)rte_socket_id()) {
432  DOCA_LOG_WARN("Port %u is on remote NUMA node to polling thread", port);
433  DOCA_LOG_WARN("\tPerformance will not be optimal");
434  }
435  return DOCA_SUCCESS;
436 }
437 
438 /*
439  * Destroy all DPDK ports
440  *
441  * @app_dpdk_config [in]: application DPDK configuration values
442  * @nb_ports [in]: number of ports to destroy
443  */
444 static void dpdk_ports_fini(struct application_dpdk_config *app_dpdk_config, uint16_t nb_ports)
445 {
446  int result;
447  int port_id;
448 
449  for (port_id = nb_ports; port_id >= 0; port_id--) {
450  if (!rte_eth_dev_is_valid_port(port_id))
451  continue;
452  result = rte_eth_dev_stop(port_id);
453  if (result != 0)
454  DOCA_LOG_ERR("rte_eth_dev_stop(): err=%d, port=%u", result, port_id);
455 
456  result = rte_eth_dev_close(port_id);
457  if (result != 0)
458  DOCA_LOG_ERR("rte_eth_dev_close(): err=%d, port=%u", result, port_id);
459  }
460 
461  /* Free the memory pool used by the ports for rte_pktmbufs */
462  if (app_dpdk_config->mbuf_pool != NULL)
463  rte_mempool_free(app_dpdk_config->mbuf_pool);
464 }
465 
466 /*
467  * Initialize all DPDK ports
468  *
469  * @app_config [in]: application DPDK configuration values
470  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
471  */
473 {
475  int ret;
476  uint16_t port_id;
477  uint16_t n;
478  const uint16_t nb_ports = app_config->port_config.nb_ports;
479  const uint32_t total_nb_mbufs = app_config->port_config.nb_queues * nb_ports * NUM_MBUFS;
480  const uint32_t mbuf_size = app_config->port_config.mbuf_size ? app_config->port_config.mbuf_size :
481  RTE_MBUF_DEFAULT_BUF_SIZE;
482 
483  /* Initialize mbufs mempool */
484  result = allocate_mempool(total_nb_mbufs, mbuf_size, &app_config->mbuf_pool);
485  if (result != DOCA_SUCCESS)
486  return result;
487 
488  /*
489  * Enable metadata to be delivered to application in the packets mbuf, the metadata is user configurable,
490  * with DOCA Flow offering a metadata scheme
491  */
492  if (app_config->port_config.enable_mbuf_metadata) {
493  ret = rte_flow_dynf_metadata_register();
494  if (ret < 0) {
495  DOCA_LOG_ERR("Metadata register failed, ret=%d", ret);
496  return DOCA_ERROR_DRIVER;
497  }
498  }
499 
500  for (port_id = 0, n = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
501  if (!rte_eth_dev_is_valid_port(port_id))
502  continue;
503  result = port_init(app_config->mbuf_pool, port_id, app_config);
504  if (result != DOCA_SUCCESS) {
505  DOCA_LOG_ERR("Cannot init port %" PRIu8, port_id);
506  dpdk_ports_fini(app_config, port_id);
507  return result;
508  }
509  if (++n >= nb_ports)
510  break;
511  }
512  return DOCA_SUCCESS;
513 }
514 
516 {
518  int ret = 0;
519 
520  /* Check that DPDK enabled the required ports to send/receive on */
521  ret = rte_eth_dev_count_avail();
522  if (app_dpdk_config->port_config.nb_ports > 0 && ret < app_dpdk_config->port_config.nb_ports) {
523  DOCA_LOG_ERR("Application will only function with %u ports, num_of_ports=%d",
524  app_dpdk_config->port_config.nb_ports,
525  ret);
526  return DOCA_ERROR_DRIVER;
527  }
528 
529  /* Check for available logical cores */
530  ret = rte_lcore_count();
531  if (app_dpdk_config->port_config.nb_queues > 0 && ret < app_dpdk_config->port_config.nb_queues) {
532  DOCA_LOG_ERR("At least %u cores are needed for the application to run, available_cores=%d",
533  app_dpdk_config->port_config.nb_queues,
534  ret);
535  return DOCA_ERROR_DRIVER;
536  }
537  app_dpdk_config->port_config.nb_queues = ret;
538 
539  if (app_dpdk_config->reserve_main_thread)
540  app_dpdk_config->port_config.nb_queues -= 1;
541 
542  if (app_dpdk_config->port_config.nb_ports > 0) {
543  result = dpdk_ports_init(app_dpdk_config);
544  if (result != DOCA_SUCCESS) {
545  DOCA_LOG_ERR("Ports allocation failed");
546  return result;
547  }
548  }
549 
550  /* Enable hairpin queues */
551  if (app_dpdk_config->port_config.nb_hairpin_q > 0) {
552  result = enable_hairpin_queues(app_dpdk_config->port_config.nb_ports);
553  if (result != DOCA_SUCCESS)
554  goto ports_cleanup;
555  }
556 
557  return DOCA_SUCCESS;
558 
559 ports_cleanup:
560  dpdk_ports_fini(app_dpdk_config, RTE_MAX_ETHPORTS);
561  return result;
562 }
563 
565 {
566  disable_hairpin_queues(RTE_MAX_ETHPORTS);
567 
568  dpdk_ports_fini(app_dpdk_config, RTE_MAX_ETHPORTS);
569 }
570 
571 /*
572  * Print ether address
573  *
574  * @dmac [in]: destination mac address
575  * @smac [in]: source mac address
576  * @ethertype [in]: eth type
577  */
578 static void print_ether_addr(const struct rte_ether_addr *dmac,
579  const struct rte_ether_addr *smac,
580  const uint32_t ethertype)
581 {
582  char dmac_buf[RTE_ETHER_ADDR_FMT_SIZE];
583  char smac_buf[RTE_ETHER_ADDR_FMT_SIZE];
584 
585  rte_ether_format_addr(dmac_buf, RTE_ETHER_ADDR_FMT_SIZE, dmac);
586  rte_ether_format_addr(smac_buf, RTE_ETHER_ADDR_FMT_SIZE, smac);
587  DOCA_LOG_DBG("DMAC=%s, SMAC=%s, ether_type=0x%04x", dmac_buf, smac_buf, ethertype);
588 }
589 
590 /*
591  * Print L2 header
592  *
593  * @packet [in]: packet mbuf
594  */
595 static void print_l2_header(const struct rte_mbuf *packet)
596 {
597  struct rte_ether_hdr *eth_hdr = rte_pktmbuf_mtod(packet, struct rte_ether_hdr *);
598 
599  print_ether_addr(&eth_hdr->dst_addr, &eth_hdr->src_addr, htonl(eth_hdr->ether_type) >> 16);
600 }
601 
602 /*
603  * Print IPV4 address
604  *
605  * @dip [in]: destination IP address
606  * @sip [in]: source IP address
607  * @packet_type [in]: packet type
608  */
609 static void print_ipv4_addr(const rte_be32_t dip, const rte_be32_t sip, const char *packet_type)
610 {
611  DOCA_LOG_DBG("DIP=%d.%d.%d.%d, SIP=%d.%d.%d.%d, %s",
612  (dip & 0xff000000) >> 24,
613  (dip & 0x00ff0000) >> 16,
614  (dip & 0x0000ff00) >> 8,
615  (dip & 0x000000ff),
616  (sip & 0xff000000) >> 24,
617  (sip & 0x00ff0000) >> 16,
618  (sip & 0x0000ff00) >> 8,
619  (sip & 0x000000ff),
620  packet_type);
621 }
622 
623 /*
624  * Print IPV6 address
625  *
626  * @dst_addr [in]: destination IP address
627  * @src_addr [in]: source IP address
628  * @packet_type [in]: packet type
629  */
630 static void print_ipv6_addr(const uint8_t dst_addr[16], const uint8_t src_addr[16], const char *packet_type)
631 {
632  DOCA_LOG_DBG("DIPv6=" IPv6_BYTES_FMT ", SIPv6=" IPv6_BYTES_FMT ", %s",
635  packet_type);
636 }
637 
638 /*
639  * Print L3 header
640  *
641  * @packet [in]: packet mbuf
642  */
643 static void print_l3_header(const struct rte_mbuf *packet)
644 {
645  if (RTE_ETH_IS_IPV4_HDR(packet->packet_type)) {
646  struct rte_ipv4_hdr *ipv4_hdr =
647  rte_pktmbuf_mtod_offset(packet, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
648 
650  htonl(ipv4_hdr->src_addr),
651  rte_get_ptype_l4_name(packet->packet_type));
652  } else if (RTE_ETH_IS_IPV6_HDR(packet->packet_type)) {
653  struct rte_ipv6_hdr *ipv6_hdr =
654  rte_pktmbuf_mtod_offset(packet, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
655 
656  print_ipv6_addr(ipv6_hdr->dst_addr, ipv6_hdr->src_addr, rte_get_ptype_l4_name(packet->packet_type));
657  }
658 }
659 
660 /*
661  * Print L4 header
662  *
663  * @packet [in]: packet mbuf
664  */
665 static void print_l4_header(const struct rte_mbuf *packet)
666 {
667  uint8_t *l4_hdr;
668  struct rte_ipv4_hdr *ipv4_hdr;
669  const struct rte_tcp_hdr *tcp_hdr;
670  const struct rte_udp_hdr *udp_hdr;
671 
672  if (!RTE_ETH_IS_IPV4_HDR(packet->packet_type))
673  return;
674 
675  ipv4_hdr = rte_pktmbuf_mtod_offset(packet, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
676  l4_hdr = (typeof(l4_hdr))ipv4_hdr + rte_ipv4_hdr_len(ipv4_hdr);
677 
678  switch (ipv4_hdr->next_proto_id) {
679  case IPPROTO_UDP:
680  udp_hdr = (typeof(udp_hdr))l4_hdr;
681  DOCA_LOG_DBG("UDP- DPORT %u, SPORT %u",
682  rte_be_to_cpu_16(udp_hdr->dst_port),
683  rte_be_to_cpu_16(udp_hdr->src_port));
684  break;
685 
686  case IPPROTO_TCP:
687  tcp_hdr = (typeof(tcp_hdr))l4_hdr;
688  DOCA_LOG_DBG("TCP- DPORT %u, SPORT %u",
689  rte_be_to_cpu_16(tcp_hdr->dst_port),
690  rte_be_to_cpu_16(tcp_hdr->src_port));
691  break;
692 
693  default:
694  DOCA_LOG_DBG("Unsupported L4 protocol!");
695  }
696 }
697 
698 /*
699  * Callback which creates a DOCA mmap out of a RTE chunk
700  * This callback can be used with 'rte_mempool_mem_iter()'
701  *
702  * @mp [in]: The RTE memory pool
703  * @opaque [in]: Opaque representing pointer to 'struct dpdk_mempool_shadow'
704  * @memhdr [in]: Header describing the RTE chunk
705  * @mem_idx [in]: Index of the RTE chunk
706  */
707 static void dpdk_chunk_to_mmap_cb(struct rte_mempool *mp,
708  void *opaque,
709  struct rte_mempool_memhdr *memhdr,
710  unsigned int mem_idx)
711 {
712  (void)mp;
713  (void)mem_idx;
714 
715  struct dpdk_mempool_shadow *mempool_shadow = opaque;
716  struct doca_mmap *new_mmap;
718 
719  result = doca_mmap_create(&new_mmap);
720  if (result != DOCA_SUCCESS) {
721  DOCA_LOG_ERR("Unable to create mmap");
722  return;
723  }
724 
725  result = doca_mmap_set_memrange(new_mmap, memhdr->addr, memhdr->len);
726  if (result != DOCA_SUCCESS) {
727  DOCA_LOG_ERR("Unable to set memory range of memory map (input): %s", doca_error_get_descr(result));
728  doca_mmap_destroy(new_mmap);
729  return;
730  }
731 
732  result = doca_mmap_add_dev(new_mmap, mempool_shadow->device);
733  if (result != DOCA_SUCCESS) {
734  DOCA_LOG_ERR("Unable to add device to mmap: %s", doca_error_get_descr(result));
735  doca_mmap_destroy(new_mmap);
736  return;
737  }
738 
739  result = doca_mmap_start(new_mmap);
740  if (result != DOCA_SUCCESS) {
741  DOCA_LOG_ERR("Unable to start memory map: %s", doca_error_get_descr(result));
742  doca_mmap_destroy(new_mmap);
743  return;
744  }
745 
746  mempool_shadow->mmap_arr[mempool_shadow->nb_mmaps++] = new_mmap;
747 }
748 
749 struct dpdk_mempool_shadow *dpdk_mempool_shadow_create(struct rte_mempool *mbuf_pool, struct doca_dev *device)
750 {
751  uint32_t nb_iterated_chunks, nb_chunks;
752  struct dpdk_mempool_shadow *mempool_shadow = rte_zmalloc(NULL, sizeof(*mempool_shadow), 0);
753 
754  if (mempool_shadow == NULL) {
755  DOCA_LOG_ERR("Dynamic allocation failed");
756  return NULL;
757  }
758  mempool_shadow->device = device;
759 
760  nb_chunks = mbuf_pool->nb_mem_chunks;
761  mempool_shadow->mmap_arr = (struct doca_mmap **)rte_zmalloc(NULL, sizeof(struct doca_mmap *) * nb_chunks, 0);
762  if (mempool_shadow->mmap_arr == NULL) {
763  DOCA_LOG_ERR("Dynamic allocation failed");
764  dpdk_mempool_shadow_destroy(mempool_shadow);
765  return NULL;
766  }
767 
768  /* Register each chunk in the mempool to an mmap */
769  nb_iterated_chunks = rte_mempool_mem_iter(mbuf_pool, dpdk_chunk_to_mmap_cb, mempool_shadow);
770  if (nb_iterated_chunks != mempool_shadow->nb_mmaps) {
771  dpdk_mempool_shadow_destroy(mempool_shadow);
772  return NULL;
773  }
774 
775  return mempool_shadow;
776 }
777 
778 /*
779  * Function which creates a DOCA mmap out of a RTE external chunk.
780  * This function will work with any chunk, not only RTE one.
781  *
782  * @mempool_shadow [in]: Pointer to 'struct dpdk_mempool_shadow'
783  * @chunk [in]: Contiguous memory used as external chunk to DPDK
784  * @len [in]: The length of chunk in bytes
785  * @return: DOCA_SUCCESS on success, and doca_error_t otherwise
786  */
787 static doca_error_t dpdk_ext_chunk_to_mmap(struct dpdk_mempool_shadow *mempool_shadow, void *chunk, size_t len)
788 {
789  struct doca_mmap *new_mmap;
791 
792  result = doca_mmap_create(&new_mmap);
793  if (result != DOCA_SUCCESS) {
794  DOCA_LOG_ERR("Unable to create mmap");
795  return result;
796  }
797 
798  result = doca_mmap_set_memrange(new_mmap, chunk, len);
799  if (result != DOCA_SUCCESS) {
800  DOCA_LOG_ERR("Unable to set memory range of memory map (input): %s", doca_error_get_descr(result));
801  doca_mmap_destroy(new_mmap);
802  return result;
803  }
804 
805  result = doca_mmap_add_dev(new_mmap, mempool_shadow->device);
806  if (result != DOCA_SUCCESS) {
807  DOCA_LOG_ERR("Unable to add device to mmap: %s", doca_error_get_descr(result));
808  doca_mmap_destroy(new_mmap);
809  return result;
810  }
811 
812  result = doca_mmap_start(new_mmap);
813  if (result != DOCA_SUCCESS) {
814  DOCA_LOG_ERR("Unable to start memory map: %s", doca_error_get_descr(result));
815  doca_mmap_destroy(new_mmap);
816  return result;
817  }
818 
819  mempool_shadow->mmap_arr[mempool_shadow->nb_mmaps++] = new_mmap;
820 
821  return DOCA_SUCCESS;
822 }
823 
824 struct dpdk_mempool_shadow *dpdk_mempool_shadow_create_extbuf(const struct rte_pktmbuf_extmem **ext_mem,
825  uint32_t ext_num,
826  struct doca_dev *device)
827 {
829  uint32_t i;
830  struct dpdk_mempool_shadow *mempool_shadow = rte_zmalloc(NULL, sizeof(*mempool_shadow), 0);
831 
832  if (mempool_shadow == NULL) {
833  DOCA_LOG_ERR("Dynamic allocation failed");
834  return NULL;
835  }
836  mempool_shadow->device = device;
837 
838  mempool_shadow->mmap_arr = (struct doca_mmap **)rte_zmalloc(NULL, sizeof(struct doca_mmap *) * ext_num, 0);
839  if (mempool_shadow->mmap_arr == NULL) {
840  DOCA_LOG_ERR("Dynamic allocation failed");
841  dpdk_mempool_shadow_destroy(mempool_shadow);
842  return NULL;
843  }
844 
845  /* Register each chunk in the mempool to an mmap */
846  for (i = 0; i < ext_num; ++i) {
847  result = dpdk_ext_chunk_to_mmap(mempool_shadow, ext_mem[i]->buf_ptr, ext_mem[i]->buf_len);
848  if (result != DOCA_SUCCESS) {
849  dpdk_mempool_shadow_destroy(mempool_shadow);
850  return NULL;
851  }
852  }
853 
854  return mempool_shadow;
855 }
856 
858 {
859  uint32_t mmap_idx;
860 
861  if (mempool_shadow->mmap_arr != NULL) {
862  for (mmap_idx = 0; mmap_idx < mempool_shadow->nb_mmaps; mmap_idx++)
863  doca_mmap_destroy(mempool_shadow->mmap_arr[mmap_idx]);
864  rte_free(mempool_shadow->mmap_arr);
865  }
866  rte_free(mempool_shadow);
867 }
868 
870  struct doca_buf_inventory *inventory,
871  uintptr_t mem_range_start,
872  size_t mem_range_size,
873  struct doca_buf **out_buf)
874 {
875  uintptr_t mmap_begin;
876  size_t mmap_len;
877  uint32_t mmap_idx;
878 
879  /* For most cases, only single mmap exists */
880  for (mmap_idx = 0; mmap_idx < mempool_shadow->nb_mmaps; mmap_idx++) {
881  struct doca_mmap *mmap = mempool_shadow->mmap_arr[mmap_idx];
882 
883  doca_mmap_get_memrange(mmap, (void **)&mmap_begin, &mmap_len);
884  /* Following checks that memory range is within the mmap while avoiding integer overflow */
885  if (mmap_begin <= mem_range_start && mem_range_start < mmap_begin + mmap_len &&
886  mem_range_size <= mmap_begin + mmap_len - mem_range_start)
887  return doca_buf_inventory_buf_get_by_data(inventory,
888  mmap,
889  (void *)mem_range_start,
890  mem_range_size,
891  out_buf);
892  }
893 
894  return DOCA_ERROR_NOT_FOUND;
895 }
896 
897 void print_header_info(const struct rte_mbuf *packet, const bool l2, const bool l3, const bool l4)
898 {
899  if (l2)
900  print_l2_header(packet);
901  if (l3)
902  print_l3_header(packet);
903  if (l4)
904  print_l4_header(packet);
905 }
906 
907 doca_error_t dpdk_init(int argc, char **argv)
908 {
909  int result;
910 
911  result = rte_eal_init(argc, argv);
912  if (result < 0) {
913  DOCA_LOG_ERR("EAL initialization failed");
914  return DOCA_ERROR_DRIVER;
915  }
916  return DOCA_SUCCESS;
917 }
918 
919 void dpdk_fini(void)
920 {
921  int result;
922 
923  result = rte_eal_cleanup();
924  if (result < 0) {
925  DOCA_LOG_ERR("rte_eal_cleanup() failed, error=%d", result);
926  return;
927  }
928 
929  DOCA_LOG_DBG("DPDK fini is done");
930 }
#define NULL
Definition: __stddef_null.h:26
int32_t result
struct rte_mempool * mp
Definition: device.c:34
struct rte_eth_dev_info dev_info
Definition: device.c:32
uintptr_t addr
doca_dpa_dev_mmap_t mmap
uint64_t len
if(bitoffset % 64+bitlength > 64) result|
static void print_ipv6_addr(const uint8_t dst_addr[16], const uint8_t src_addr[16], const char *packet_type)
Definition: dpdk_utils.c:630
static doca_error_t port_init(struct rte_mempool *mbuf_pool, uint8_t port, struct application_dpdk_config *app_config)
Definition: dpdk_utils.c:272
void dpdk_mempool_shadow_destroy(struct dpdk_mempool_shadow *mempool_shadow)
Definition: dpdk_utils.c:857
static doca_error_t enable_hairpin_queues(uint8_t nb_ports)
Definition: dpdk_utils.c:221
static void disable_hairpin_queues(uint16_t nb_ports)
Definition: dpdk_utils.c:201
doca_error_t dpdk_init(int argc, char **argv)
Definition: dpdk_utils.c:907
struct dpdk_mempool_shadow * dpdk_mempool_shadow_create_extbuf(const struct rte_pktmbuf_extmem **ext_mem, uint32_t ext_num, struct doca_dev *device)
Definition: dpdk_utils.c:824
static void print_l2_header(const struct rte_mbuf *packet)
Definition: dpdk_utils.c:595
#define IPv6_BYTES_FMT
Definition: dpdk_utils.c:41
void dpdk_fini(void)
Definition: dpdk_utils.c:919
static doca_error_t setup_hairpin_queues(uint16_t port_id, uint16_t peer_port_id, uint16_t *reserved_hairpin_q_list, int hairpin_queue_len)
Definition: dpdk_utils.c:148
static doca_error_t dpdk_ext_chunk_to_mmap(struct dpdk_mempool_shadow *mempool_shadow, void *chunk, size_t len)
Definition: dpdk_utils.c:787
static void print_ether_addr(const struct rte_ether_addr *dmac, const struct rte_ether_addr *smac, const uint32_t ethertype)
Definition: dpdk_utils.c:578
doca_error_t dpdk_queues_and_ports_init(struct application_dpdk_config *app_dpdk_config)
Definition: dpdk_utils.c:515
static void print_l3_header(const struct rte_mbuf *packet)
Definition: dpdk_utils.c:643
static doca_error_t dpdk_ports_init(struct application_dpdk_config *app_config)
Definition: dpdk_utils.c:472
void dpdk_queues_and_ports_fini(struct application_dpdk_config *app_dpdk_config)
Definition: dpdk_utils.c:564
static doca_error_t unbind_hairpin_queues(uint16_t port_id)
Definition: dpdk_utils.c:103
doca_error_t dpdk_mempool_shadow_find_buf_by_data(struct dpdk_mempool_shadow *mempool_shadow, struct doca_buf_inventory *inventory, uintptr_t mem_range_start, size_t mem_range_size, struct doca_buf **out_buf)
Definition: dpdk_utils.c:869
#define RSS_KEY_LEN
Definition: dpdk_utils.c:38
static doca_error_t bind_hairpin_queues(uint16_t port_id)
Definition: dpdk_utils.c:61
static doca_error_t allocate_mempool(const uint32_t total_nb_mbufs, const uint32_t mbuf_size, struct rte_mempool **mbuf_pool)
Definition: dpdk_utils.c:251
static void dpdk_ports_fini(struct application_dpdk_config *app_dpdk_config, uint16_t nb_ports)
Definition: dpdk_utils.c:444
#define IPv6_BYTES(addr)
Definition: dpdk_utils.c:44
static void print_ipv4_addr(const rte_be32_t dip, const rte_be32_t sip, const char *packet_type)
Definition: dpdk_utils.c:609
static void print_l4_header(const struct rte_mbuf *packet)
Definition: dpdk_utils.c:665
DOCA_LOG_REGISTER(NUTILS)
struct dpdk_mempool_shadow * dpdk_mempool_shadow_create(struct rte_mempool *mbuf_pool, struct doca_dev *device)
Definition: dpdk_utils.c:749
void print_header_info(const struct rte_mbuf *packet, const bool l2, const bool l3, const bool l4)
Definition: dpdk_utils.c:897
static void dpdk_chunk_to_mmap_cb(struct rte_mempool *mp, void *opaque, struct rte_mempool_memhdr *memhdr, unsigned int mem_idx)
Definition: dpdk_utils.c:707
#define MBUF_CACHE_SIZE
Definition: dpdk_utils.h:46
#define TX_RING_SIZE
Definition: dpdk_utils.h:44
#define NUM_MBUFS
Definition: dpdk_utils.h:45
#define RX_RING_SIZE
Definition: dpdk_utils.h:43
static doca_error_t doca_buf_inventory_buf_get_by_data(struct doca_buf_inventory *inventory, struct doca_mmap *mmap, void *data, size_t data_len, struct doca_buf **buf)
Allocate single element from buffer inventory and point it to the buffer defined by data & data_len a...
enum doca_error doca_error_t
DOCA API return codes.
DOCA_STABLE const char * doca_error_get_descr(doca_error_t error)
Returns the description string of an error code.
@ DOCA_ERROR_NOT_FOUND
Definition: doca_error.h:54
@ DOCA_SUCCESS
Definition: doca_error.h:38
@ DOCA_ERROR_DRIVER
Definition: doca_error.h:59
#define DOCA_LOG_ERR(format,...)
Generates an ERROR application log message.
Definition: doca_log.h:466
#define DOCA_LOG_WARN(format,...)
Generates a WARNING application log message.
Definition: doca_log.h:476
#define DOCA_LOG_INFO(format,...)
Generates an INFO application log message.
Definition: doca_log.h:486
#define DOCA_LOG_DBG(format,...)
Generates a DEBUG application log message.
Definition: doca_log.h:496
DOCA_STABLE doca_error_t doca_mmap_set_memrange(struct doca_mmap *mmap, void *addr, size_t len)
Set the memory range of DOCA memory map.
DOCA_STABLE doca_error_t doca_mmap_destroy(struct doca_mmap *mmap)
Destroy DOCA Memory Map structure.
DOCA_STABLE doca_error_t doca_mmap_create(struct doca_mmap **mmap)
Allocates zero size memory map object with default/unset attributes.
DOCA_STABLE doca_error_t doca_mmap_start(struct doca_mmap *mmap)
Start DOCA Memory Map.
DOCA_STABLE doca_error_t doca_mmap_get_memrange(const struct doca_mmap *mmap, void **addr, size_t *len)
Get the memory range of DOCA memory map.
DOCA_STABLE doca_error_t doca_mmap_add_dev(struct doca_mmap *mmap, struct doca_dev *dev)
Register DOCA memory map on a given device.
struct tcp_hdr l4_hdr
Definition: packets.h:2
uint32_t src_addr
Definition: packets.h:8
uint32_t dst_addr
Definition: packets.h:9
__UINTPTR_TYPE__ uintptr_t
Definition: stdint.h:298
struct rte_mempool * mbuf_pool
Definition: dpdk_utils.h:72
struct application_port_config port_config
Definition: dpdk_utils.h:70
struct doca_dev * device
Definition: dpdk_utils.c:50
struct doca_mmap ** mmap_arr
Definition: dpdk_utils.c:51
uint32_t src_addr
Definition: packets.h:75
uint32_t dst_addr
Definition: packets.h:76
uint8_t next_proto_id
Definition: packets.h:73
uint16_t src_port
Definition: packets.h:80
uint16_t dst_port
Definition: packets.h:81
uint16_t dst_port
Definition: packets.h:99
uint16_t src_port
Definition: packets.h:98
static int nb_ports
Definition: switch_core.c:44