NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
simple_fwd_vnf_core.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 <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdint.h>
30 #include <sys/time.h>
31 #include <inttypes.h>
32 #include <sys/types.h>
33 #include <sys/queue.h>
34 #include <netinet/in.h>
35 #include <setjmp.h>
36 #include <stdarg.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdbool.h>
40 
41 #include <rte_eal.h>
42 #include <rte_common.h>
43 #include <rte_malloc.h>
44 #include <rte_ether.h>
45 #include <rte_ethdev.h>
46 #include <rte_mempool.h>
47 #include <rte_mbuf.h>
48 #include <rte_net.h>
49 #include <rte_flow.h>
50 
51 #include <doca_argp.h>
52 #include <doca_flow.h>
53 #include <doca_log.h>
54 
55 #include <dpdk_utils.h>
56 #include <utils.h>
57 
58 #include "simple_fwd_ft.h"
59 #include "simple_fwd_port.h"
60 #include "simple_fwd_vnf_core.h"
61 
62 DOCA_LOG_REGISTER(SIMPLE_FWD_VNF : Core);
63 
64 #define VNF_PKT_L2(M) rte_pktmbuf_mtod(M, uint8_t *) /* A marco that points to the start of the data in the mbuf */
65 #define VNF_PKT_LEN(M) rte_pktmbuf_pkt_len(M) /* A marco that returns the length of the packet */
66 #define VNF_RX_BURST_SIZE (32) /* Burst size of packets to read, RX burst read size */
67 
68 /* Flag for forcing lcores to stop processing packets, and gracefully terminate the application */
69 static volatile bool force_quit;
70 
71 /* Parameters used by each core */
73  int ports[NUM_OF_PORTS]; /* Ports identifiers */
74  int queues[NUM_OF_PORTS]; /* Queue mapped for the core running */
75  bool used; /* Whether the core is used or not */
76 };
77 
78 /* per core parameters */
79 static struct vnf_per_core_params core_params_arr[RTE_MAX_LCORE];
80 
81 /*
82  * Adjust the mbuf pointer, to point on the packet's raw data
83  *
84  * @m [in]: DPDK structure represent the packet received
85  * @pinfo [in]: packet info representation in the application
86  */
87 static void vnf_adjust_mbuf(struct rte_mbuf *m, struct simple_fwd_pkt_info *pinfo)
88 {
89  int diff = pinfo->outer.l2 - VNF_PKT_L2(m);
90 
91  rte_pktmbuf_adj(m, diff);
92 }
93 
94 /*
95  * Process received packets, mainly retrieving packet's key, then checking if there is an entry found
96  * matching the generated key, in the entries table.
97  * If no entry found, the function will create and add new one.
98  * In addition, this function handles aging as well
99  *
100  * @mbuf [in]: DPDK structure represent the packet received
101  * @queue_id [in]: Queue ID
102  * @vnf [in]: Holder for all functions pointers used by the application
103  */
104 static void simple_fwd_process_offload(struct rte_mbuf *mbuf, uint16_t queue_id, struct app_vnf *vnf)
105 {
106  struct simple_fwd_pkt_info pinfo;
107 
108  memset(&pinfo, 0, sizeof(struct simple_fwd_pkt_info));
109  if (simple_fwd_parse_packet(VNF_PKT_L2(mbuf), VNF_PKT_LEN(mbuf), &pinfo))
110  return;
111  pinfo.orig_data = mbuf;
112  pinfo.orig_port_id = mbuf->port;
113  pinfo.pipe_queue = queue_id;
114  pinfo.rss_hash = mbuf->hash.rss;
115  if (pinfo.outer.l3_type != IPV4)
116  return;
117  vnf->vnf_process_pkt(&pinfo);
118  vnf_adjust_mbuf(mbuf, &pinfo);
119 }
120 
121 int simple_fwd_process_pkts(void *process_pkts_params)
122 {
123  int result;
124  uint64_t cur_tsc, last_tsc;
125  struct rte_mbuf *mbufs[VNF_RX_BURST_SIZE];
126  uint16_t j, nb_rx, queue_id;
127  uint32_t port_id = 0, core_id = rte_lcore_id();
128  struct vnf_per_core_params *params = &core_params_arr[core_id];
129  struct simple_fwd_config *app_config = ((struct simple_fwd_process_pkts_params *)process_pkts_params)->cfg;
130  struct app_vnf *vnf = ((struct simple_fwd_process_pkts_params *)process_pkts_params)->vnf;
131 
132  if (!params->used) {
133  DOCA_LOG_DBG("Core %u nothing need to do", core_id);
134  return 0;
135  }
136  DOCA_LOG_TRC("Core %u process queue %u start", core_id, params->queues[0]);
137  last_tsc = rte_rdtsc();
138  while (!force_quit) {
139  if (core_id == rte_get_main_lcore()) {
140  cur_tsc = rte_rdtsc();
141  if (cur_tsc > last_tsc + app_config->stats_timer) {
142  result = vnf->vnf_dump_stats(0);
143  if (result != 0)
144  return result;
145  last_tsc = cur_tsc;
146  }
147  }
148  for (port_id = 0; port_id < NUM_OF_PORTS; port_id++) {
149  queue_id = params->queues[port_id];
150  nb_rx = rte_eth_rx_burst(port_id, queue_id, mbufs, VNF_RX_BURST_SIZE);
151  for (j = 0; j < nb_rx; j++) {
152  if (app_config->hw_offload)
154  if (app_config->rx_only)
155  rte_pktmbuf_free(mbufs[j]);
156  else
157  rte_eth_tx_burst(port_id ^ 1, queue_id, &mbufs[j], 1);
158  }
159  if (app_config->age_thread)
160  vnf->vnf_flow_age(port_id, queue_id);
161  }
162  }
163  return 0;
164 }
165 
167 {
168  force_quit = true;
169 }
170 
171 /*
172  * Callback function for setting time stats dump
173  *
174  * @param [in]: time for dumping stats
175  * @config [out]: application configuration for setting the time
176  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
177  */
178 static doca_error_t stats_callback(void *param, void *config)
179 {
180  struct simple_fwd_config *app_config = (struct simple_fwd_config *)config;
181 
182  app_config->stats_timer = *(int *)param;
183  DOCA_LOG_DBG("Set stats_timer:%lu", app_config->stats_timer);
184  return DOCA_SUCCESS;
185 }
186 
187 /*
188  * Callback function for setting number of queues
189  *
190  * @param [in]: number of queues to set
191  * @config [out]: application configuration for setting the number of queues
192  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
193  */
194 static doca_error_t nr_queues_callback(void *param, void *config)
195 {
196  struct simple_fwd_config *app_config = (struct simple_fwd_config *)config;
197  int nr_queues = *(int *)param;
198 
199  if (nr_queues < 2) {
200  DOCA_LOG_ERR("Invalid nr_queues should >= 2");
202  }
203  app_config->dpdk_cfg->port_config.nb_queues = nr_queues;
204  app_config->dpdk_cfg->port_config.rss_support = 1;
205  DOCA_LOG_DBG("Set nr_queues:%u", nr_queues);
206  return DOCA_SUCCESS;
207 }
208 
209 /*
210  * Callback function for setting the "rx-only" mode, where the application only receives packets
211  *
212  * @param [in]: parameter indicates whther or not to set the "rx-only" mode
213  * @config [out]: application configuration to set the "rx-only" mode
214  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
215  */
216 static doca_error_t rx_only_callback(void *param, void *config)
217 {
218  struct simple_fwd_config *app_config = (struct simple_fwd_config *)config;
219 
220  app_config->rx_only = *(bool *)param ? 1 : 0;
221  DOCA_LOG_DBG("Set rx_only:%u", app_config->rx_only);
222  return DOCA_SUCCESS;
223 }
224 
225 /*
226  * Callback function for the HW offload
227  *
228  * @param [in]: parameter indicates whther or not to set the HW offload
229  * @config [out]: application configuration to set the HW offload
230  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
231  */
232 static doca_error_t hw_offload_callback(void *param, void *config)
233 {
234  struct simple_fwd_config *app_config = (struct simple_fwd_config *)config;
235 
236  app_config->hw_offload = *(bool *)param ? 1 : 0;
237  DOCA_LOG_DBG("Set hw_offload:%u", app_config->hw_offload);
238  return DOCA_SUCCESS;
239 }
240 
241 /*
242  * Callback function for setting the hairpin usage
243  *
244  * @param [in]: parameter indicates whther or not to use hairpin queues
245  * @config [out]: application configuration to set hairpin usage
246  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
247  */
248 static doca_error_t hairpinq_callback(void *param, void *config)
249 {
250  struct simple_fwd_config *app_config = (struct simple_fwd_config *)config;
251 
252  app_config->is_hairpin = *(bool *)param;
253  DOCA_LOG_DBG("Set is_hairpin:%u", app_config->is_hairpin);
254  return DOCA_SUCCESS;
255 }
256 
257 /*
258  * Callback function for setting dedicated thread for aging handling
259  *
260  * @param [in]: parameter indicates whther or not to use dedicated thread for aging
261  * @config [out]: application configuration to set the usage of a dedicated thread for aged flows
262  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
263  */
264 static doca_error_t age_thread_callback(void *param, void *config)
265 {
266  struct simple_fwd_config *app_config = (struct simple_fwd_config *)config;
267 
268  app_config->age_thread = *(bool *)param;
269  DOCA_LOG_DBG("Set age_thread:%s", app_config->age_thread ? "true" : "false");
270  return DOCA_SUCCESS;
271 }
272 
273 /*
274  * Registers all flags used by the application for DOCA argument parser, so that when parsing
275  * it can be parsed accordingly
276  * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise
277  */
279 {
281  struct doca_argp_param *stats_param, *nr_queues_param, *rx_only_param, *hw_offload_param;
282  struct doca_argp_param *hairpinq_param, *age_thread_param;
283 
284  /* Create and register stats timer param */
285  result = doca_argp_param_create(&stats_param);
286  if (result != DOCA_SUCCESS) {
287  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
288  return result;
289  }
290  doca_argp_param_set_short_name(stats_param, "t");
291  doca_argp_param_set_long_name(stats_param, "stats-timer");
292  doca_argp_param_set_arguments(stats_param, "<time>");
293  doca_argp_param_set_description(stats_param, "Set interval to dump stats information");
296  result = doca_argp_register_param(stats_param);
297  if (result != DOCA_SUCCESS) {
298  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
299  return result;
300  }
301 
302  /* Create and register queues number param */
303  result = doca_argp_param_create(&nr_queues_param);
304  if (result != DOCA_SUCCESS) {
305  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
306  return result;
307  }
308  doca_argp_param_set_short_name(nr_queues_param, "q");
309  doca_argp_param_set_long_name(nr_queues_param, "nr-queues");
310  doca_argp_param_set_arguments(nr_queues_param, "<num>");
311  doca_argp_param_set_description(nr_queues_param, "Set queues number");
314  result = doca_argp_register_param(nr_queues_param);
315  if (result != DOCA_SUCCESS) {
316  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
317  return result;
318  }
319 
320  /* Create and register RX only param */
321  result = doca_argp_param_create(&rx_only_param);
322  if (result != DOCA_SUCCESS) {
323  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
324  return result;
325  }
326  doca_argp_param_set_short_name(rx_only_param, "r");
327  doca_argp_param_set_long_name(rx_only_param, "rx-only");
328  doca_argp_param_set_description(rx_only_param, "Set rx only");
331  result = doca_argp_register_param(rx_only_param);
332  if (result != DOCA_SUCCESS) {
333  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
334  return result;
335  }
336 
337  /* Create and register HW offload param */
338  result = doca_argp_param_create(&hw_offload_param);
339  if (result != DOCA_SUCCESS) {
340  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
341  return result;
342  }
343  doca_argp_param_set_short_name(hw_offload_param, "o");
344  doca_argp_param_set_long_name(hw_offload_param, "hw-offload");
345  doca_argp_param_set_description(hw_offload_param, "Set PCI address of the RXP engine to use");
348  result = doca_argp_register_param(hw_offload_param);
349  if (result != DOCA_SUCCESS) {
350  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
351  return result;
352  }
353 
354  /* Create and register hairpin queue param */
355  result = doca_argp_param_create(&hairpinq_param);
356  if (result != DOCA_SUCCESS) {
357  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
358  return result;
359  }
360  doca_argp_param_set_short_name(hairpinq_param, "hq");
361  doca_argp_param_set_long_name(hairpinq_param, "hairpinq");
362  doca_argp_param_set_description(hairpinq_param, "Set forwarding to hairpin queue");
365  result = doca_argp_register_param(hairpinq_param);
366  if (result != DOCA_SUCCESS) {
367  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
368  return result;
369  }
370 
371  /* Create and register age thread param */
372  result = doca_argp_param_create(&age_thread_param);
373  if (result != DOCA_SUCCESS) {
374  DOCA_LOG_ERR("Failed to create ARGP param: %s", doca_error_get_descr(result));
375  return result;
376  }
377  doca_argp_param_set_short_name(age_thread_param, "a");
378  doca_argp_param_set_long_name(age_thread_param, "age-thread");
379  doca_argp_param_set_description(age_thread_param, "Start thread do aging");
382  result = doca_argp_register_param(age_thread_param);
383  if (result != DOCA_SUCCESS) {
384  DOCA_LOG_ERR("Failed to register program param: %s", doca_error_get_descr(result));
385  return result;
386  }
387 
388  /* Register version callback for DOCA SDK & RUNTIME */
390  if (result != DOCA_SUCCESS) {
391  DOCA_LOG_ERR("Failed to register version callback: %s", doca_error_get_descr(result));
392  return result;
393  }
394  return DOCA_SUCCESS;
395 }
396 
397 void simple_fwd_map_queue(uint16_t nb_queues)
398 {
399  int i, queue_idx = 0;
400 
401  memset(core_params_arr, 0, sizeof(core_params_arr));
402  for (i = 0; i < RTE_MAX_LCORE; i++) {
403  if (!rte_lcore_is_enabled(i))
404  continue;
405  core_params_arr[i].ports[0] = 0;
406  core_params_arr[i].ports[1] = 1;
407  core_params_arr[i].queues[0] = queue_idx;
408  core_params_arr[i].queues[1] = queue_idx;
409  core_params_arr[i].used = true;
410  queue_idx++;
411  if (queue_idx >= nb_queues)
412  break;
413  }
414 }
415 
416 void simple_fwd_destroy(struct app_vnf *vnf)
417 {
418  vnf->vnf_destroy();
419 }
int32_t result
DOCA_EXPERIMENTAL void doca_argp_param_set_description(struct doca_argp_param *param, const char *description)
Set the description of the program param, used during program usage.
DOCA_EXPERIMENTAL void doca_argp_param_set_long_name(struct doca_argp_param *param, const char *name)
Set the long name of the program param.
DOCA_EXPERIMENTAL void doca_argp_param_set_arguments(struct doca_argp_param *param, const char *arguments)
Set the description of the expected arguments of the program param, used during program usage.
DOCA_EXPERIMENTAL void doca_argp_param_set_callback(struct doca_argp_param *param, doca_argp_param_cb_t callback)
Set the callback function of the program param.
DOCA_EXPERIMENTAL doca_error_t doca_argp_param_create(struct doca_argp_param **param)
Create new program param.
DOCA_EXPERIMENTAL void doca_argp_param_set_type(struct doca_argp_param *param, enum doca_argp_type type)
Set the type of the param arguments.
DOCA_EXPERIMENTAL doca_error_t doca_argp_register_version_callback(doca_argp_param_cb_t callback)
Register an alternative version callback.
DOCA_EXPERIMENTAL void doca_argp_param_set_short_name(struct doca_argp_param *param, const char *name)
Set the short name of the program param.
DOCA_EXPERIMENTAL doca_error_t doca_argp_register_param(struct doca_argp_param *input_param)
Register a program flag.
@ DOCA_ARGP_TYPE_BOOLEAN
Definition: doca_argp.h:58
@ DOCA_ARGP_TYPE_INT
Definition: doca_argp.h:57
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_INVALID_VALUE
Definition: doca_error.h:44
@ DOCA_SUCCESS
Definition: doca_error.h:38
#define DOCA_LOG_ERR(format,...)
Generates an ERROR application log message.
Definition: doca_log.h:466
#define DOCA_LOG_TRC(format,...)
Generates a TRACE application log message.
Definition: doca_log.h:513
#define DOCA_LOG_DBG(format,...)
Generates a DEBUG application log message.
Definition: doca_log.h:496
uint16_t queue_id
Definition: ip_frag_dp.c:1
int simple_fwd_parse_packet(uint8_t *data, int len, struct simple_fwd_pkt_info *pinfo)
#define NUM_OF_PORTS
#define VNF_PKT_L2(M)
void simple_fwd_destroy(struct app_vnf *vnf)
static doca_error_t age_thread_callback(void *param, void *config)
static void vnf_adjust_mbuf(struct rte_mbuf *m, struct simple_fwd_pkt_info *pinfo)
int simple_fwd_process_pkts(void *process_pkts_params)
DOCA_LOG_REGISTER(SIMPLE_FWD_VNF :Core)
static doca_error_t nr_queues_callback(void *param, void *config)
static doca_error_t stats_callback(void *param, void *config)
static struct vnf_per_core_params core_params_arr[RTE_MAX_LCORE]
static doca_error_t rx_only_callback(void *param, void *config)
#define VNF_RX_BURST_SIZE
static doca_error_t hw_offload_callback(void *param, void *config)
void simple_fwd_process_pkts_stop(void)
void simple_fwd_map_queue(uint16_t nb_queues)
static void simple_fwd_process_offload(struct rte_mbuf *mbuf, uint16_t queue_id, struct app_vnf *vnf)
doca_error_t register_simple_fwd_params(void)
static volatile bool force_quit
static doca_error_t hairpinq_callback(void *param, void *config)
#define VNF_PKT_LEN(M)
int(* vnf_destroy)(void)
Definition: app_vnf.h:39
int(* vnf_dump_stats)(uint32_t port_id)
Definition: app_vnf.h:38
void(* vnf_flow_age)(uint32_t port_id, uint16_t queue)
Definition: app_vnf.h:37
int(* vnf_process_pkt)(struct simple_fwd_pkt_info *pinfo)
Definition: app_vnf.h:36
struct simple_fwd_pkt_format outer
int ports[NUM_OF_PORTS]
int queues[NUM_OF_PORTS]
noreturn doca_error_t sdk_version_callback(void *param, void *doca_config)
Definition: utils.c:41