API Reference Manual  1.46.0
odp_l2fwd_perf.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2024 Nokia
3  */
4 
16 #ifndef _GNU_SOURCE
17 #define _GNU_SOURCE
18 #endif
19 
20 #include <inttypes.h>
21 #include <signal.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 
26 #include <odp_api.h>
27 #include <odp/helper/odph_api.h>
28 
29 #define PROG_NAME "odp_l2fwd_perf"
30 #define PAIR_DELIMITER "@"
31 #define IF_DELIMITER ","
32 #define MAC_COMMON 0x2
33 
34 #define MAX_IFS 8U
35 #define MAX_QS 32U
36 #define MAX_WORKERS ((uint32_t)(ODP_THREAD_COUNT_MAX - 1))
37 
38 enum {
39  DIRECT,
40  SCHED_PARALLEL,
41  SCHED_ATOMIC,
42  SCHED_ORDERED
43 };
44 
45 #define DEF_MODE DIRECT
46 #define DEF_BURST 32U
47 #define DEF_CNT 32768U
48 #define DEF_LEN 1536U
49 #define DEF_RUNTIME 0U
50 #define DEF_WORKERS 1U
51 
52 #define GIGAS 1000000000U
53 #define MEGAS 1000000U
54 #define KILOS 1000U
55 
56 typedef struct {
57  uint32_t num_pkts;
58  uint32_t pkt_len;
59 } dynamic_defs_t;
60 
61 typedef enum {
62  PRS_OK,
63  PRS_NOK,
64  PRS_TERM
65 } parse_result_t;
66 
67 typedef struct {
68  uint64_t tm_ns;
69  uint64_t tx;
70  uint64_t tx_drops;
71 } stats_t;
72 
73 typedef struct pktio_s pktio_t;
74 
75 typedef struct pktio_s {
76  odp_pktin_queue_t in_qs[MAX_QS];
77  odp_pktout_queue_t out_qs[MAX_QS];
78  odp_pktio_t handle;
79  pktio_t *dst;
80  odph_ethaddr_t src_mac;
81  odph_ethaddr_t dst_mac;
82  char *name;
83  char *dst_name;
84  uint8_t num_in_qs;
85  uint8_t num_out_qs;
86 } pktio_t;
87 
88 typedef struct {
89  pktio_t *pktio;
90  /* Poll ID to determine which input queue this worker will use for this packet I/O. */
91  uint8_t rx_poll;
92  /* Poll ID to determine which output queue this worker will use for the destination of this
93  * packet I/O. */
94  uint8_t tx_poll;
95 } worker_pktio_t;
96 
97 typedef struct prog_config_s prog_config_t;
98 
99 typedef struct ODP_ALIGNED_CACHE {
100  worker_pktio_t pktios[MAX_IFS];
101  stats_t stats;
102  prog_config_t *prog_config;
103  uint8_t num_ifs;
104 } worker_config_t;
105 
106 typedef struct prog_config_s {
107  odph_thread_t thread_tbl[MAX_WORKERS];
108  worker_config_t worker_config[MAX_WORKERS];
109  pktio_t pktios[MAX_IFS];
110  dynamic_defs_t dyn_defs;
112  odp_cpumask_t worker_mask;
113  odp_barrier_t init_barrier;
114  odp_barrier_t term_barrier;
115  odp_atomic_u32_t is_running;
116  odp_pool_t pool;
117  uint32_t burst_size;
118  uint32_t num_pkts;
119  uint32_t pkt_len;
120  uint32_t orig_in_mtu;
121  uint32_t orig_out_mtu;
122  uint32_t mtu;
123  uint32_t runtime;
124  uint32_t num_workers;
125  uint8_t num_ifs;
126  uint8_t mode;
127  uint8_t orig_is_promisc;
128  uint8_t is_promisc;
129  uint8_t is_mac_mod;
130 } prog_config_t;
131 
132 typedef struct {
133  struct {
134  odp_pktin_queue_t pktin;
135  odp_pktout_queue_t pktout;
136  odph_ethaddr_t src_mac;
137  odph_ethaddr_t dst_mac;
138  } tbl[MAX_IFS];
139 } dir_fwd_tbl_t;
140 
141 typedef struct {
142  struct {
143  odp_pktout_queue_t pktout;
144  odph_ethaddr_t src_mac;
145  odph_ethaddr_t dst_mac;
146  } tbl[ODP_PKTIO_MAX_INDEX + 1];
147 } scd_fwd_tbl_t;
148 
149 typedef int (*run_func_t)(void *arg);
150 
151 static prog_config_t *prog_conf;
152 
153 static void terminate(int signal ODP_UNUSED)
154 {
155  odp_atomic_store_u32(&prog_conf->is_running, 0U);
156 }
157 
158 static void init_config(prog_config_t *config)
159 {
160  odp_pool_capability_t pool_capa;
161  pktio_t *pktio;
162 
163  if (odp_pool_capability(&pool_capa) == 0) {
164  config->dyn_defs.num_pkts = pool_capa.pkt.max_num > 0U ?
165  ODPH_MIN(pool_capa.pkt.max_num, DEF_CNT) : DEF_CNT;
166  config->dyn_defs.pkt_len = pool_capa.pkt.max_len > 0U ?
167  ODPH_MIN(pool_capa.pkt.max_len, DEF_LEN) : DEF_LEN;
168  }
169 
170  config->pool = ODP_POOL_INVALID;
171  config->burst_size = DEF_BURST;
172  config->num_pkts = config->dyn_defs.num_pkts;
173  config->pkt_len = config->dyn_defs.pkt_len;
174  config->runtime = DEF_RUNTIME;
175  config->num_workers = DEF_WORKERS;
176  config->mode = DEF_MODE;
177  config->is_mac_mod = 1U;
178 
179  for (uint32_t i = 0U; i < MAX_IFS; ++i) {
180  pktio = &config->pktios[i];
181 
182  pktio->handle = ODP_PKTIO_INVALID;
183  pktio->dst = pktio;
184  pktio->dst_mac.addr[0U] = MAC_COMMON;
185  pktio->dst_mac.addr[5U] = i + 1U;
186  }
187 }
188 
189 static void parse_interfaces(prog_config_t *config, const char *optarg)
190 {
191  char *tmp_str = strdup(optarg), *tmp;
192 
193  if (tmp_str == NULL)
194  ODPH_ABORT("Out of memory\n");
195 
196  tmp = strtok(tmp_str, IF_DELIMITER);
197 
198  while (tmp && config->num_ifs < MAX_IFS) {
199  tmp = strdup(tmp);
200 
201  if (tmp == NULL)
202  ODPH_ABORT("Out of memory\n");
203 
204  config->pktios[config->num_ifs].name = tmp;
205  ++config->num_ifs;
206  tmp = strtok(NULL, IF_DELIMITER);
207  }
208 
209  free(tmp_str);
210 }
211 
212 static odp_bool_t parse_mac_and_port(pktio_t *pktio, const char *pair)
213 {
214  const char *pos = strstr(pair, PAIR_DELIMITER);
215  uint32_t size;
216 
217  if (pos == NULL)
218  return false;
219 
220  size = pos - pair + 1U;
221  pktio->dst_name = strdup(pair + size);
222 
223  if (pktio->dst_name == NULL)
224  ODPH_ABORT("Out of memory\n");
225 
226  char mac[size];
227 
228  odph_strcpy(mac, pair, size);
229 
230  /* Temporarily save to 'src_mac' until destination binding. */
231  if (odph_eth_addr_parse(&pktio->src_mac, mac) < 0) {
232  ODPH_ERR("Warning: unable to parse portmap entry (%s)\n", pktio->dst_name);
233  free(pktio->dst_name);
234  pktio->dst_name = NULL;
235  return false;
236  }
237 
238  return true;
239 }
240 
241 static void parse_portmap(prog_config_t *config, const char *optarg)
242 {
243  char *tmp_str = strdup(optarg);
244  const char *tmp;
245  uint32_t i = 0U;
246 
247  if (tmp_str == NULL)
248  ODPH_ABORT("Out of memory\n");
249 
250  tmp = strtok(tmp_str, IF_DELIMITER);
251 
252  while (tmp && i < MAX_IFS) {
253  if (parse_mac_and_port(&config->pktios[i], tmp))
254  ++i;
255 
256  tmp = strtok(NULL, IF_DELIMITER);
257  }
258 
259  free(tmp_str);
260 }
261 
262 static void print_usage(const dynamic_defs_t *dyn_defs)
263 {
264  printf("\n"
265  "Simple L2 forwarder.\n"
266  "\n"
267  "Usage: %s [OPTIONS]\n", PROG_NAME);
268  printf("\n"
269  " E.g. %s -i eth0\n"
270  " %s -i eth0,eth1 -p 11:22:33:44:55:66@eth1,66:55:44:33:22:11@eth0 -b 16\n",
271  PROG_NAME, PROG_NAME);
272  printf("\n"
273  "Mandatory OPTIONS:\n"
274  "\n"
275  " -i, --interfaces Ethernet interfaces for packet I/O, '%s'-separated, no\n"
276  " whitespaces anywhere. By default, packets are looped back.\n"
277  " This can be overridden with '--portmap'. Maximum\n"
278  " interface count is %u.\n"
279  "\n"
280  "Optional OPTIONS:\n"
281  "\n"
282  " -m, --mode Packet input mode. %u by default.\n"
283  " 0: direct\n"
284  " 1: scheduled with parallel queues\n"
285  " 2: scheduled with atomic queues\n"
286  " 3: scheduled with ordered queues\n"
287  " Queue per direction per worker is always attempted. Output\n"
288  " is always in direct mode.\n"
289  " -p, --portmap List of destination MAC address and port pairs for passed\n"
290  " interfaces, MAC address and the port should be\n"
291  " '%s'-separated, the pairs '%s'-separated, no whitespaces\n"
292  " anywhere. Ordering follows the '--interface' option, e.g.\n"
293  " passing '-i eth0%seth1' and\n"
294  " '-p 11:22:33:44:55:66%seth1%s66:55:44:33:22:11%seth0' would\n"
295  " result in eth0 sending packets to eth1 with source MAC of\n"
296  " port eth0 and destination MAC of '11:22:33:44:55:66' and\n"
297  " eth1 to eth0 with source MAC of eth1 and destination MAC of\n"
298  " '66:55:44:33:22:11'.\n"
299  " -b, --burst_rx Receive burst size. %u by default.\n"
300  " -n, --num_pkts Number of packet buffers allocated for packet I/O pool.\n"
301  " %u by default.\n"
302  " -l, --pkt_len Maximum size of packet buffers in packet I/O pool. %u by\n"
303  " default.\n"
304  " -M, --mtu Interface MTU in bytes. Interface specific by default.\n"
305  " -P, --promisc_mode Enable promiscuous mode.\n"
306  " -t, --time Time in seconds to run. 0 means infinite. %u by default.\n"
307  " -s, --no_mac_mod Disable source and destination MAC address modification.\n"
308  " -c, --worker_count Number of workers. Workers are assigned to handle\n"
309  " interfaces in round-robin fashion. E.g. with two interfaces\n"
310  " eth0 and eth1 and with 5 workers, eth0 would be handled by\n"
311  " worker indexes 0, 2 and 4 and eth1 by worker indexes 1 and\n"
312  " 3. Assignment may be affected by '--portmap'. %u workers by\n"
313  " default.\n"
314  " -h, --help This help.\n"
315  "\n", IF_DELIMITER, MAX_IFS, DEF_MODE, PAIR_DELIMITER, IF_DELIMITER, IF_DELIMITER,
316  PAIR_DELIMITER, IF_DELIMITER, PAIR_DELIMITER, DEF_BURST, dyn_defs->num_pkts,
317  dyn_defs->pkt_len, DEF_RUNTIME, DEF_WORKERS);
318 }
319 
320 static parse_result_t check_options(prog_config_t *config)
321 {
322  odp_pool_capability_t pool_capa;
323  uint32_t max_workers;
324 
325  if (config->num_ifs == 0U) {
326  ODPH_ERR("Invalid number of interfaces: %u (min: 1, max: %u)\n", config->num_ifs,
327  MAX_IFS);
328  return PRS_NOK;
329  }
330 
331  if (config->mode != DIRECT && config->mode != SCHED_PARALLEL &&
332  config->mode != SCHED_ATOMIC && config->mode != SCHED_ORDERED) {
333  ODPH_ERR("Invalid packet input mode: %u\n", config->mode);
334  return PRS_NOK;
335  }
336 
337  if (config->burst_size == 0U) {
338  ODPH_ERR("Invalid burst size: %u (min: 1)\n", config->burst_size);
339  return PRS_NOK;
340  }
341 
342  if (odp_pool_capability(&pool_capa) < 0) {
343  ODPH_ERR("Error querying pool capabilities\n");
344  return PRS_NOK;
345  }
346 
347  if (config->num_pkts == 0U ||
348  (pool_capa.pkt.max_num > 0U && config->num_pkts > pool_capa.pkt.max_num)) {
349  ODPH_ERR("Invalid pool packet count: %u (min: 1, max: %u)\n", config->num_pkts,
350  pool_capa.pkt.max_num);
351  return PRS_NOK;
352  }
353 
354  if (config->pkt_len == 0U ||
355  (pool_capa.pkt.max_len > 0U && config->pkt_len > pool_capa.pkt.max_len)) {
356  ODPH_ERR("Invalid pool packet length: %u (min: 1, max: %u)\n", config->pkt_len,
357  pool_capa.pkt.max_len);
358  return PRS_NOK;
359  }
360 
361  max_workers = ODPH_MIN(MAX_WORKERS, (uint32_t)odp_cpumask_default_worker(NULL, 0));
362 
363  if (config->num_workers == 0U || config->num_workers > max_workers) {
364  ODPH_ERR("Invalid worker count: %u (min: 1, max: %u)\n", config->num_workers,
365  max_workers);
366  return PRS_NOK;
367  }
368 
369  (void)odp_cpumask_default_worker(&config->worker_mask, config->num_workers);
370 
371  return PRS_OK;
372 }
373 
374 static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
375 {
376  int opt;
377 
378  static const struct option longopts[] = {
379  { "interfaces", required_argument, NULL, 'i' },
380  { "mode", required_argument, NULL, 'm' },
381  { "portmap", required_argument, NULL, 'p' },
382  { "burst_rx", required_argument, NULL, 'b' },
383  { "num_pkts", required_argument, NULL, 'n' },
384  { "pkt_len", required_argument, NULL, 'l' },
385  { "mtu", required_argument, NULL, 'M' },
386  { "promisc_mode", no_argument, NULL, 'P' },
387  { "time", required_argument, NULL, 't' },
388  { "no_mac_mod", no_argument, NULL, 's' },
389  { "worker_count", required_argument, NULL, 'c' },
390  { "help", no_argument, NULL, 'h' },
391  { NULL, 0, NULL, 0 }
392  };
393 
394  static const char *shortopts = "i:m:p:b:n:l:M:Pt:sc:h";
395 
396  init_config(config);
397 
398  while (1) {
399  opt = getopt_long(argc, argv, shortopts, longopts, NULL);
400 
401  if (opt == -1)
402  break;
403 
404  switch (opt) {
405  case 'i':
406  parse_interfaces(config, optarg);
407  break;
408  case 'm':
409  config->mode = atoi(optarg);
410  break;
411  case 'p':
412  parse_portmap(config, optarg);
413  break;
414  case 'b':
415  config->burst_size = atoi(optarg);
416  break;
417  case 'n':
418  config->num_pkts = atoi(optarg);
419  break;
420  case 'l':
421  config->pkt_len = atoi(optarg);
422  break;
423  case 'M':
424  config->mtu = atoi(optarg);
425  break;
426  case 'P':
427  config->is_promisc = 1U;
428  break;
429  case 't':
430  config->runtime = atoi(optarg);
431  break;
432  case 's':
433  config->is_mac_mod = false;
434  break;
435  case 'c':
436  config->num_workers = atoi(optarg);
437  break;
438  case 'h':
439  print_usage(&config->dyn_defs);
440  return PRS_TERM;
441  case '?':
442  default:
443  print_usage(&config->dyn_defs);
444  return PRS_NOK;
445  }
446  }
447 
448  return check_options(config);
449 }
450 
451 static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
452 {
453  struct sigaction action = { .sa_handler = terminate };
454 
455  odp_atomic_init_u32(&config->is_running, 1U);
456 
457  if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
458  sigaddset(&action.sa_mask, SIGTERM) == -1 ||
459  sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
460  sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
461  ODPH_ERR("Error installing signal handler\n");
462  return PRS_NOK;
463  }
464 
465  return parse_options(argc, argv, config);
466 }
467 
468 static void bind_destinations(pktio_t *pktios, uint8_t num)
469 {
470  pktio_t *pktio_src, *pktio_dst;
471 
472  for (uint8_t i = 0U; i < num; ++i) {
473  pktio_src = &pktios[i];
474 
475  for (uint8_t j = 0U; j < num; ++j) {
476  pktio_dst = &pktios[j];
477 
478  if (pktio_src->dst_name != NULL &&
479  strcmp(pktio_src->dst_name, pktio_dst->name) == 0) {
480  pktio_src->dst = pktio_dst;
481  /* Copy the actual destination MAC as the 'dst_mac' that was
482  * earlier saved to source's 'src_mac'. */
483  pktio_dst->dst_mac = pktio_src->src_mac;
484  break;
485  }
486  }
487  }
488 }
489 
490 static worker_pktio_t *get_destination(const pktio_t *pktio, worker_pktio_t *pktios, uint8_t num)
491 {
492  worker_pktio_t *w_pktio;
493 
494  for (uint8_t i = 0U; i < num; ++i) {
495  w_pktio = &pktios[i];
496 
497  if (pktio == w_pktio->pktio->dst)
498  return w_pktio;
499  }
500 
501  return NULL;
502 }
503 
504 static void bind_workers(prog_config_t *config)
505 {
506  const uint32_t num_workers = config->num_workers, num_ifs = config->num_ifs;
507  uint32_t max, min, i = 0U, j = 0U, *work_idx_ptr, *pktio_idx_ptr;
508  worker_config_t *worker;
509  worker_pktio_t *w_pktio;
510  pktio_t *pktio;
511 
512  if (num_workers >= num_ifs) {
513  max = num_workers;
514  min = num_ifs;
515  work_idx_ptr = &i;
516  pktio_idx_ptr = &j;
517  } else {
518  max = num_ifs;
519  min = num_workers;
520  work_idx_ptr = &j;
521  pktio_idx_ptr = &i;
522  }
523 
524  /* Assign workers to packet I/Os. Based on worker and packet I/O counts, the outer loop
525  * will be looping the one with greater count and the inner with lesser count. */
526  for (; i < max; ++i) {
527  if (j == min)
528  j = 0U;
529 
530  worker = &config->worker_config[*work_idx_ptr];
531  worker->pktios[worker->num_ifs++].pktio = &config->pktios[*pktio_idx_ptr];
532  ++j;
533  }
534 
535  /* Check how many workers will end up polling a certain packet I/O input and increase input
536  * queue count for that packet I/O accordingly. */
537  for (i = 0U; i < num_workers; ++i) {
538  worker = &config->worker_config[i];
539 
540  for (j = 0U; j < worker->num_ifs; ++j) {
541  w_pktio = &worker->pktios[j];
542  ++w_pktio->pktio->num_in_qs;
543  w_pktio->rx_poll = w_pktio->pktio->num_in_qs;
544  }
545  }
546 
547  /* Check how many workers will end up outputting through a certain packet I/O and increase
548  * output queue count for that packet I/O accordingly. */
549  for (i = 0U; i < num_ifs; ++i) {
550  pktio = &config->pktios[i];
551 
552  for (j = 0U; j < num_workers; ++j) {
553  worker = &config->worker_config[j];
554  w_pktio = get_destination(pktio, worker->pktios, worker->num_ifs);
555 
556  if (w_pktio != NULL) {
557  ++pktio->num_out_qs;
558  w_pktio->tx_poll = pktio->num_out_qs;
559  }
560  }
561  }
562 }
563 
564 static odp_bool_t setup_config(prog_config_t *config)
565 {
566  if (config->mode != DIRECT) {
567  if (odp_schedule_config(NULL) < 0) {
568  ODPH_ERR("Error initializing scheduler\n");
569  return false;
570  }
571  }
572 
573  bind_destinations(config->pktios, config->num_ifs);
574  bind_workers(config);
575 
576  for (uint32_t i = 0U; i < config->num_workers; ++i)
577  config->worker_config[i].prog_config = config;
578 
579  return true;
580 }
581 
582 static odp_schedule_sync_t get_odp_sync(uint8_t mode)
583 {
584  switch (mode) {
585  case SCHED_PARALLEL:
587  case SCHED_ATOMIC:
588  return ODP_SCHED_SYNC_ATOMIC;
589  case SCHED_ORDERED:
590  return ODP_SCHED_SYNC_ORDERED;
591  default:
593  }
594 }
595 
596 static odp_bool_t setup_pktios(prog_config_t *config)
597 {
598  odp_pool_param_t pool_param;
599  pktio_t *pktio;
600  odp_pktio_param_t pktio_param;
601  odp_pktio_config_t pktio_config;
603  uint32_t num_input_qs, num_output_qs;
604  odp_pktin_queue_param_t pktin_param;
605  odp_pktout_queue_param_t pktout_param;
606  int ret;
607 
608  odp_pool_param_init(&pool_param);
609  pool_param.pkt.seg_len = config->pkt_len;
610  pool_param.pkt.len = config->pkt_len;
611  pool_param.pkt.num = config->num_pkts;
612  pool_param.type = ODP_POOL_PACKET;
613  config->pool = odp_pool_create(PROG_NAME, &pool_param);
614 
615  if (config->pool == ODP_POOL_INVALID) {
616  ODPH_ERR("Error creating packet I/O pool\n");
617  return false;
618  }
619 
620  for (uint8_t i = 0U; i < config->num_ifs; ++i) {
621  pktio = &config->pktios[i];
622  odp_pktio_param_init(&pktio_param);
623  pktio_param.in_mode = config->mode == DIRECT ?
625  pktio_param.out_mode = pktio->num_out_qs > 0U ?
627  pktio->handle = odp_pktio_open(pktio->name, config->pool, &pktio_param);
628 
629  if (pktio->handle == ODP_PKTIO_INVALID) {
630  ODPH_ERR("Error opening packet I/O (%s)\n", pktio->name);
631  return false;
632  }
633 
634  odp_pktio_config_init(&pktio_config);
635  pktio_config.parser.layer = ODP_PROTO_LAYER_NONE;
636 
637  if (odp_pktio_config(pktio->handle, &pktio_config) < 0) {
638  ODPH_ERR("Error configuring packet I/O (%s)\n", pktio->name);
639  return false;
640  }
641 
642  if (odp_pktio_capability(pktio->handle, &capa) < 0) {
643  ODPH_ERR("Error querying packet I/O capabilities (%s)\n", pktio->name);
644  return false;
645  }
646 
647  odp_pktin_queue_param_init(&pktin_param);
648  num_input_qs = config->mode == DIRECT ? pktio->num_in_qs : config->num_workers;
649  num_input_qs = ODPH_MIN(num_input_qs, capa.max_input_queues);
650  num_input_qs = ODPH_MIN(num_input_qs, MAX_QS);
651 
652  if (num_input_qs > 1) {
653  pktin_param.hash_enable = true;
654  pktin_param.hash_proto.proto.ipv4_udp = 1U;
655  }
656 
657  if (config->mode == DIRECT) {
658  if (num_input_qs == pktio->num_in_qs)
659  pktin_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
660  else
661  ODPH_ERR("Warning: not enough input queues supported for MT "
662  "unsafe operation (%s)\n", pktio->name);
663  }
664 
665  pktin_param.num_queues = num_input_qs;
667  pktin_param.queue_param.sched.sync = get_odp_sync(config->mode);
668  pktio->num_in_qs = num_input_qs;
669 
670  if (odp_pktin_queue_config(pktio->handle, &pktin_param) < 0) {
671  ODPH_ERR("Error configuring packet I/O input queues (%s)\n", pktio->name);
672  return false;
673  }
674 
675  if (config->mode == DIRECT &&
676  odp_pktin_queue(pktio->handle, pktio->in_qs, num_input_qs) !=
677  (int)num_input_qs) {
678  ODPH_ERR("Error querying packet I/O input queues (%s)\n", pktio->name);
679  return false;
680  }
681 
682  num_output_qs = pktio->num_out_qs;
683 
684  if (num_output_qs > 0U) {
685  odp_pktout_queue_param_init(&pktout_param);
686  num_output_qs = config->mode == DIRECT ?
687  num_output_qs : config->num_workers;
688  num_output_qs = ODPH_MIN(num_output_qs, capa.max_output_queues);
689  num_output_qs = ODPH_MIN(num_output_qs, MAX_QS);
690 
691  if (num_output_qs >= pktio->num_out_qs)
692  pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
693  else
694  ODPH_ERR("Warning: not enough output queues supported for MT "
695  "unsafe operation (%s)\n", pktio->name);
696 
697  pktout_param.num_queues = num_output_qs;
698  pktio->num_out_qs = num_output_qs;
699 
700  if (odp_pktout_queue_config(pktio->handle, &pktout_param) < 0) {
701  ODPH_ERR("Error configuring packet I/O output queues (%s)\n",
702  pktio->name);
703  return false;
704  }
705 
706  if (odp_pktout_queue(pktio->handle, pktio->out_qs, num_output_qs) !=
707  (int)num_output_qs) {
708  ODPH_ERR("Error querying packet I/O output queues (%s)\n",
709  pktio->name);
710  return false;
711  }
712  }
713 
714  if (config->mtu > 0U) {
715  if (capa.set_op.op.maxlen == 0U) {
716  config->mtu = 0U;
717  ODPH_ERR("MTU setting not supported (%s)\n", pktio->name);
718  return false;
719  }
720 
721  if (config->mtu < capa.maxlen.min_input ||
722  config->mtu > capa.maxlen.max_input ||
723  config->mtu < capa.maxlen.min_output ||
724  config->mtu > capa.maxlen.max_output) {
725  config->mtu = 0U;
726  ODPH_ERR("Invalid MTU requested: %u (input min: %u, input max: %u,"
727  " output min: %u, output max: %u, %s)\n",
728  config->mtu, capa.maxlen.min_input, capa.maxlen.max_input,
729  capa.maxlen.min_output, capa.maxlen.max_output,
730  pktio->name);
731  return false;
732  }
733 
734  config->orig_in_mtu = odp_pktin_maxlen(pktio->handle);
735  config->orig_out_mtu = odp_pktout_maxlen(pktio->handle);
736 
737  if (odp_pktio_maxlen_set(pktio->handle, config->mtu, config->mtu) < 0) {
738  config->mtu = 0U;
739  ODPH_ERR("Error setting promiscuous mode (%s)\n", pktio->name);
740  return false;
741  }
742  }
743 
744  if (config->is_promisc) {
745  if (capa.set_op.op.promisc_mode == 0U) {
746  config->is_promisc = 0U;
747  ODPH_ERR("Promiscuous mode setting not supported (%s)\n",
748  pktio->name);
749  return false;
750  }
751 
752  ret = odp_pktio_promisc_mode(pktio->handle);
753 
754  if (odp_pktio_promisc_mode_set(pktio->handle, true) < 0) {
755  config->is_promisc = 0U;
756  ODPH_ERR("Error setting promiscuous mode (%s)\n", pktio->name);
757  return false;
758  }
759 
760  config->orig_is_promisc = ret < 0 ? 1U : (uint8_t)ret;
761  }
762 
763  if (odp_pktio_mac_addr(pktio->handle, pktio->src_mac.addr, ODPH_ETHADDR_LEN) < 0) {
764  ODPH_ERR("Error querying MAC address (%s)\n", pktio->name);
765  return false;
766  }
767 
768  if (odp_pktio_start(pktio->handle) < 0) {
769  ODPH_ERR("Error starting packet I/O (%s)\n", pktio->name);
770  return false;
771  }
772  }
773 
774  return true;
775 }
776 
777 static void print_mac(odph_ethaddr_t mac)
778 {
779  for (int i = 0; i < ODPH_ETHADDR_LEN; ++i)
780  printf("%02x%s", mac.addr[i], i < ODPH_ETHADDR_LEN - 1 ? ":" : "");
781 
782  printf("\n");
783 }
784 
785 static void print_rx_workers(pktio_t *pktio, prog_config_t *config)
786 {
787  worker_config_t *worker;
788  const uint32_t num_workers = config->num_workers;
789  uint32_t ids[num_workers], num_ids = 0U;
790 
791  if (config->mode != DIRECT) {
792  printf("(scheduled)\n");
793  return;
794  }
795 
796  for (uint32_t i = 0U; i < num_workers; ++i) {
797  worker = &config->worker_config[i];
798 
799  for (uint8_t j = 0U; j < worker->num_ifs; ++j) {
800  if (pktio == worker->pktios[j].pktio) {
801  ids[num_ids++] = i;
802  break;
803  }
804  }
805  }
806 
807  for (uint32_t i = 0U; i < num_ids; ++i)
808  printf("%u%s", ids[i], i < num_ids - 1U ? ", " : "");
809 
810  printf("\n");
811 }
812 
813 static void print_tx_workers(pktio_t *pktio, prog_config_t *config)
814 {
815  worker_config_t *worker;
816  const uint32_t num_workers = config->num_workers;
817  uint32_t ids[num_workers], num_ids = 0U;
818 
819  if (config->mode != DIRECT) {
820  printf("(scheduled)\n");
821  return;
822  }
823 
824  for (uint32_t i = 0U; i < num_workers; ++i) {
825  worker = &config->worker_config[i];
826 
827  if (get_destination(pktio, worker->pktios, worker->num_ifs) != NULL)
828  ids[num_ids++] = i;
829  }
830 
831  if (num_ids == 0U)
832  printf("-");
833  else
834  for (uint32_t i = 0U; i < num_ids; ++i)
835  printf("%u%s", ids[i], i < num_ids - 1U ? ", " : "");
836 
837  printf("\n");
838 }
839 
840 static odp_bool_t print_summary(prog_config_t *config)
841 {
842  pktio_t *pktio;
843 
844  printf("\nprogram options:\n================\n\n"
845  " interfaces:\n\n");
846 
847  for (uint8_t i = 0U; i < config->num_ifs; ++i) {
848  pktio = &config->pktios[i];
849  printf(" %s:\n\n"
850  " handle: 0x%" PRIx64 "\n"
851  " src MAC: ", pktio->name, odp_pktio_to_u64(pktio->handle));
852  print_mac(pktio->src_mac);
853  printf(" dst MAC: ");
854  print_mac(pktio->dst_mac);
855  printf(" dst name: %s\n"
856  " input queues: %u\n"
857  " output queues: %u\n", pktio->dst->name, pktio->num_in_qs,
858  pktio->num_out_qs);
859  printf(" rx worker IDs: ");
860  print_rx_workers(pktio, config);
861  printf(" tx worker IDs: ");
862  print_tx_workers(pktio, config);
863  printf("\n");
864  }
865 
866  printf(" mode: %s\n"
867  " burst size: %u\n"
868  " pool size: %u\n"
869  " packet length: %u\n", config->mode == DIRECT ?
870  "direct" : config->mode == SCHED_PARALLEL ?
871  "scheduled-parallel" : config->mode == SCHED_ATOMIC ?
872  "scheduled-atomic" : "scheduled-ordered",
873  config->burst_size, config->num_pkts, config->pkt_len);
874 
875  if (config->mtu > 0U)
876  printf(" MTU: %u\n", config->mtu);
877 
878  printf(" promiscuous mode: %s\n", config->is_promisc ? "enabled" : "disabled");
879 
880  if (config->runtime > 0U)
881  printf(" runtime: %u sec\n", config->runtime);
882  else
883  printf(" runtime: infinite\n");
884 
885  printf(" MAC modification: %s\n"
886  " workers: %u\n\n", config->is_mac_mod ? "enabled" : "disabled",
887  config->num_workers);
888 
889  return true;
890 }
891 
892 static dir_fwd_tbl_t build_dir_fwd_tbl(worker_pktio_t *pktios, uint8_t num_ifs)
893 {
894  worker_pktio_t *w_pktio;
895  pktio_t *pktio;
896  dir_fwd_tbl_t tbl;
897 
898  for (uint8_t i = 0U; i < num_ifs; ++i) {
899  w_pktio = &pktios[i];
900  pktio = w_pktio->pktio;
901  tbl.tbl[i].pktin = pktio->in_qs[w_pktio->rx_poll % pktio->num_in_qs];
902  tbl.tbl[i].pktout = pktio->dst->out_qs[w_pktio->tx_poll % pktio->dst->num_out_qs];
903  tbl.tbl[i].src_mac = pktio->dst->src_mac;
904  tbl.tbl[i].dst_mac = pktio->dst->dst_mac;
905  }
906 
907  return tbl;
908 }
909 
910 static int run_direct_single_if(void *args)
911 {
912  worker_config_t *config = args;
913  prog_config_t *prog_config = config->prog_config;
914  const uint32_t burst_size = prog_config->burst_size;
915  const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, config->num_ifs);
916  odp_time_t start;
917  odp_atomic_u32_t *is_running = &prog_config->is_running;
918  const odp_pktin_queue_t pktin = tbl.tbl[0U].pktin;
919  odp_packet_t pkts[burst_size];
920  int num_recv, num_sent, diff;
921  const odp_pktout_queue_t pktout = tbl.tbl[0U].pktout;
922  uint64_t tx = 0U, tx_drops = 0U;
923  stats_t *stats = &config->stats;
924 
925  odp_barrier_wait(&prog_config->init_barrier);
926  start = odp_time_local_strict();
927 
928  while (odp_atomic_load_u32(is_running)) {
929  num_recv = odp_pktin_recv(pktin, pkts, burst_size);
930 
931  if (odp_unlikely(num_recv <= 0))
932  continue;
933 
934  num_sent = odp_pktout_send(pktout, pkts, num_recv);
935 
936  if (odp_unlikely(num_sent < num_recv)) {
937  num_sent = num_sent < 0 ? 0 : num_sent;
938  diff = num_recv - num_sent;
939  odp_packet_free_multi(&pkts[num_sent], diff);
940  tx_drops += diff;
941  }
942 
943  tx += num_sent;
944  }
945 
946  stats->tm_ns = odp_time_diff_ns(odp_time_local_strict(), start);
947  stats->tx = tx;
948  stats->tx_drops = tx_drops;
949  odp_barrier_wait(&prog_config->term_barrier);
950 
951  return 0;
952 }
953 
954 static int run_direct_single_if_mac_mod(void *args)
955 {
956  worker_config_t *config = args;
957  prog_config_t *prog_config = config->prog_config;
958  const uint32_t burst_size = prog_config->burst_size;
959  const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, config->num_ifs);
960  odp_time_t start;
961  odp_atomic_u32_t *is_running = &prog_config->is_running;
962  const odp_pktin_queue_t pktin = tbl.tbl[0U].pktin;
963  odp_packet_t pkts[burst_size];
964  int num_recv, num_sent, diff;
965  const odph_ethaddr_t src_mac = tbl.tbl[0U].src_mac, dst_mac = tbl.tbl[0U].dst_mac;
966  odph_ethhdr_t *eth;
967  const odp_pktout_queue_t pktout = tbl.tbl[0U].pktout;
968  uint64_t tx = 0U, tx_drops = 0U;
969  stats_t *stats = &config->stats;
970 
971  odp_barrier_wait(&prog_config->init_barrier);
972  start = odp_time_local_strict();
973 
974  while (odp_atomic_load_u32(is_running)) {
975  num_recv = odp_pktin_recv(pktin, pkts, burst_size);
976 
977  if (odp_unlikely(num_recv <= 0))
978  continue;
979 
980  for (int i = 0U; i < num_recv; ++i) {
981  eth = odp_packet_data(pkts[i]);
982  eth->src = src_mac;
983  eth->dst = dst_mac;
984  }
985 
986  num_sent = odp_pktout_send(pktout, pkts, num_recv);
987 
988  if (odp_unlikely(num_sent < num_recv)) {
989  num_sent = num_sent < 0 ? 0 : num_sent;
990  diff = num_recv - num_sent;
991  odp_packet_free_multi(&pkts[num_sent], diff);
992  tx_drops += diff;
993  }
994 
995  tx += num_sent;
996  }
997 
998  stats->tm_ns = odp_time_diff_ns(odp_time_local_strict(), start);
999  stats->tx = tx;
1000  stats->tx_drops = tx_drops;
1001  odp_barrier_wait(&prog_config->term_barrier);
1002 
1003  return 0;
1004 }
1005 
1006 static int run_direct_multi_if(void *args)
1007 {
1008  worker_config_t *config = args;
1009  prog_config_t *prog_config = config->prog_config;
1010  const uint8_t num_ifs = config->num_ifs;
1011  const uint32_t burst_size = prog_config->burst_size;
1012  const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, num_ifs);
1013  odp_time_t start;
1014  odp_atomic_u32_t *is_running = &prog_config->is_running;
1015  uint8_t i = 0U;
1016  odp_pktin_queue_t pktin;
1017  odp_packet_t pkts[burst_size];
1018  int num_recv, num_sent, diff;
1019  odp_pktout_queue_t pktout;
1020  uint64_t tx = 0U, tx_drops = 0U;
1021  stats_t *stats = &config->stats;
1022 
1023  odp_barrier_wait(&prog_config->init_barrier);
1024  start = odp_time_local_strict();
1025 
1026  while (odp_atomic_load_u32(is_running)) {
1027  pktin = tbl.tbl[i].pktin;
1028  pktout = tbl.tbl[i].pktout;
1029 
1030  if (++i == num_ifs)
1031  i = 0U;
1032 
1033  num_recv = odp_pktin_recv(pktin, pkts, burst_size);
1034 
1035  if (odp_unlikely(num_recv <= 0))
1036  continue;
1037 
1038  num_sent = odp_pktout_send(pktout, pkts, num_recv);
1039 
1040  if (odp_unlikely(num_sent < num_recv)) {
1041  num_sent = num_sent < 0 ? 0 : num_sent;
1042  diff = num_recv - num_sent;
1043  odp_packet_free_multi(&pkts[num_sent], diff);
1044  tx_drops += diff;
1045  }
1046 
1047  tx += num_sent;
1048  }
1049 
1050  stats->tm_ns = odp_time_diff_ns(odp_time_local_strict(), start);
1051  stats->tx = tx;
1052  stats->tx_drops = tx_drops;
1053  odp_barrier_wait(&prog_config->term_barrier);
1054 
1055  return 0;
1056 }
1057 
1058 static int run_direct_multi_if_mac_mod(void *args)
1059 {
1060  worker_config_t *config = args;
1061  prog_config_t *prog_config = config->prog_config;
1062  const uint8_t num_ifs = config->num_ifs;
1063  const uint32_t burst_size = prog_config->burst_size;
1064  const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, num_ifs);
1065  odp_time_t start;
1066  odp_atomic_u32_t *is_running = &prog_config->is_running;
1067  uint8_t i = 0U;
1068  odp_pktin_queue_t pktin;
1069  odp_packet_t pkts[burst_size];
1070  int num_recv, num_sent, diff;
1071  odph_ethaddr_t src_mac, dst_mac;
1072  odph_ethhdr_t *eth;
1073  odp_pktout_queue_t pktout;
1074  uint64_t tx = 0U, tx_drops = 0U;
1075  stats_t *stats = &config->stats;
1076 
1077  odp_barrier_wait(&prog_config->init_barrier);
1078  start = odp_time_local_strict();
1079 
1080  while (odp_atomic_load_u32(is_running)) {
1081  pktin = tbl.tbl[i].pktin;
1082  pktout = tbl.tbl[i].pktout;
1083  src_mac = tbl.tbl[i].src_mac;
1084  dst_mac = tbl.tbl[i].dst_mac;
1085 
1086  if (++i == num_ifs)
1087  i = 0U;
1088 
1089  num_recv = odp_pktin_recv(pktin, pkts, burst_size);
1090 
1091  if (odp_unlikely(num_recv <= 0))
1092  continue;
1093 
1094  for (int j = 0U; j < num_recv; ++j) {
1095  eth = odp_packet_data(pkts[j]);
1096  eth->src = src_mac;
1097  eth->dst = dst_mac;
1098  }
1099 
1100  num_sent = odp_pktout_send(pktout, pkts, num_recv);
1101 
1102  if (odp_unlikely(num_sent < num_recv)) {
1103  num_sent = num_sent < 0 ? 0 : num_sent;
1104  diff = num_recv - num_sent;
1105  odp_packet_free_multi(&pkts[num_sent], diff);
1106  tx_drops += diff;
1107  }
1108 
1109  tx += num_sent;
1110  }
1111 
1112  stats->tm_ns = odp_time_diff_ns(odp_time_local_strict(), start);
1113  stats->tx = tx;
1114  stats->tx_drops = tx_drops;
1115  odp_barrier_wait(&prog_config->term_barrier);
1116 
1117  return 0;
1118 }
1119 
1120 static scd_fwd_tbl_t build_scd_fwd_tbl(const pktio_t *pktios, uint8_t num_ifs, int thread_idx)
1121 {
1122  const pktio_t *pktio;
1123  int idx;
1124  scd_fwd_tbl_t tbl;
1125 
1126  for (uint8_t i = 0U; i < num_ifs; ++i) {
1127  pktio = &pktios[i];
1128  idx = odp_pktio_index(pktio->handle);
1129  tbl.tbl[idx].pktout = pktio->dst->out_qs[thread_idx % pktio->dst->num_out_qs];
1130  tbl.tbl[idx].src_mac = pktio->dst->src_mac;
1131  tbl.tbl[idx].dst_mac = pktio->dst->dst_mac;
1132  }
1133 
1134  return tbl;
1135 }
1136 
1137 static void drain_events(void)
1138 {
1139  while (true) {
1140  odp_event_t ev;
1141 
1142  ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
1143 
1144  if (ev == ODP_EVENT_INVALID)
1145  break;
1146 
1147  odp_event_free(ev);
1148  }
1149 }
1150 
1151 static int run_scheduled(void *args)
1152 {
1153  worker_config_t *config = args;
1154  prog_config_t *prog_config = config->prog_config;
1155  odp_time_t start;
1156  odp_atomic_u32_t *is_running = &prog_config->is_running;
1157  const uint8_t num_ifs = prog_config->num_ifs;
1158  const uint32_t burst_size = prog_config->burst_size;
1159  odp_event_t evs[burst_size];
1160  int num_recv, num_sent, diff;
1161  odp_packet_t pkts[burst_size];
1162  const scd_fwd_tbl_t tbl = build_scd_fwd_tbl(prog_config->pktios, num_ifs, odp_thread_id());
1163  odp_pktout_queue_t pktout;
1164  uint64_t tx = 0U, tx_drops = 0U;
1165  stats_t *stats = &config->stats;
1166 
1167  odp_barrier_wait(&config->prog_config->init_barrier);
1168  start = odp_time_local_strict();
1169 
1170  while (odp_atomic_load_u32(is_running)) {
1171  num_recv = odp_schedule_multi_no_wait(NULL, evs, burst_size);
1172 
1173  if (odp_unlikely(num_recv <= 0))
1174  continue;
1175 
1176  odp_packet_from_event_multi(pkts, evs, num_recv);
1177  pktout = tbl.tbl[odp_packet_input_index(pkts[0U])].pktout;
1178  num_sent = odp_pktout_send(pktout, pkts, num_recv);
1179 
1180  if (odp_unlikely(num_sent < num_recv)) {
1181  num_sent = num_sent < 0 ? 0 : num_sent;
1182  diff = num_recv - num_sent;
1183  odp_packet_free_multi(&pkts[num_sent], diff);
1184  tx_drops += diff;
1185  }
1186 
1187  tx += num_sent;
1188  }
1189 
1190  stats->tm_ns = odp_time_diff_ns(odp_time_local_strict(), start);
1191  stats->tx = tx;
1192  stats->tx_drops = tx_drops;
1193  /* With certain feature combo that involves HW prefetching, the prefetched events may block
1194  * other threads from forward progress, so drain them before termination barrier and the
1195  * potential rest after the barrier. */
1197  drain_events();
1198  odp_barrier_wait(&config->prog_config->term_barrier);
1200  drain_events();
1201 
1202  return 0;
1203 }
1204 
1205 static int run_scheduled_mac_mod(void *args)
1206 {
1207  worker_config_t *config = args;
1208  prog_config_t *prog_config = config->prog_config;
1209  odp_time_t start;
1210  odp_atomic_u32_t *is_running = &prog_config->is_running;
1211  const uint8_t num_ifs = prog_config->num_ifs;
1212  const uint32_t burst_size = prog_config->burst_size;
1213  odp_event_t evs[burst_size];
1214  int num_recv, num_sent, idx, diff;
1215  odp_packet_t pkts[burst_size];
1216  const scd_fwd_tbl_t tbl = build_scd_fwd_tbl(prog_config->pktios, num_ifs, odp_thread_id());
1217  odph_ethaddr_t src_mac, dst_mac;
1218  odph_ethhdr_t *eth;
1219  odp_pktout_queue_t pktout;
1220  uint64_t tx = 0U, tx_drops = 0U;
1221  stats_t *stats = &config->stats;
1222 
1223  odp_barrier_wait(&config->prog_config->init_barrier);
1224  start = odp_time_local_strict();
1225 
1226  while (odp_atomic_load_u32(is_running)) {
1227  num_recv = odp_schedule_multi_no_wait(NULL, evs, burst_size);
1228 
1229  if (odp_unlikely(num_recv <= 0))
1230  continue;
1231 
1232  odp_packet_from_event_multi(pkts, evs, num_recv);
1233  idx = odp_packet_input_index(pkts[0U]);
1234  src_mac = tbl.tbl[idx].src_mac;
1235  dst_mac = tbl.tbl[idx].dst_mac;
1236  pktout = tbl.tbl[idx].pktout;
1237 
1238  for (int i = 0U; i < num_recv; ++i) {
1239  eth = odp_packet_data(pkts[i]);
1240  eth->src = src_mac;
1241  eth->dst = dst_mac;
1242  }
1243 
1244  num_sent = odp_pktout_send(pktout, pkts, num_recv);
1245 
1246  if (odp_unlikely(num_sent < num_recv)) {
1247  num_sent = num_sent < 0 ? 0 : num_sent;
1248  diff = num_recv - num_sent;
1249  odp_packet_free_multi(&pkts[num_sent], diff);
1250  tx_drops += diff;
1251  }
1252 
1253  tx += num_sent;
1254  }
1255 
1256  stats->tm_ns = odp_time_diff_ns(odp_time_local_strict(), start);
1257  stats->tx = tx;
1258  stats->tx_drops = tx_drops;
1259  /* With certain feature combo that involves HW prefetching, the prefetched events may block
1260  * other threads from forward progress, so drain them before termination barrier and the
1261  * potential rest after the barrier. */
1263  drain_events();
1264  odp_barrier_wait(&config->prog_config->term_barrier);
1266  drain_events();
1267 
1268  return 0;
1269 }
1270 
1271 static run_func_t get_run_func(const prog_config_t *config, const worker_config_t *worker)
1272 {
1273  if (config->mode == DIRECT) {
1274  if (config->is_mac_mod)
1275  return worker->num_ifs == 1U ?
1276  run_direct_single_if_mac_mod : run_direct_multi_if_mac_mod;
1277  else
1278  return worker->num_ifs == 1U ?
1279  run_direct_single_if : run_direct_multi_if;
1280  } else {
1281  return config->is_mac_mod ? run_scheduled_mac_mod : run_scheduled;
1282  }
1283 }
1284 
1285 static odp_bool_t setup_workers(prog_config_t *config)
1286 {
1287  odph_thread_common_param_t thr_common;
1288  odph_thread_param_t thr_params[config->num_workers], *thr_param;
1289  worker_config_t *worker;
1290 
1291  odp_barrier_init(&config->init_barrier, config->num_workers + 1);
1292  odp_barrier_init(&config->term_barrier, config->num_workers + 1);
1293  odph_thread_common_param_init(&thr_common);
1294  thr_common.instance = config->odp_instance;
1295  thr_common.cpumask = &config->worker_mask;
1296 
1297  for (uint32_t i = 0; i < config->num_workers; ++i) {
1298  thr_param = &thr_params[i];
1299  worker = &config->worker_config[i];
1300  odph_thread_param_init(thr_param);
1301  thr_param->start = get_run_func(config, worker);
1302  thr_param->thr_type = ODP_THREAD_WORKER;
1303  thr_param->arg = worker;
1304  }
1305 
1306  if ((uint32_t)odph_thread_create(config->thread_tbl, &thr_common, thr_params,
1307  config->num_workers) != config->num_workers) {
1308  ODPH_ERR("Error configuring worker threads\n");
1309  return false;
1310  }
1311 
1312  odp_barrier_wait(&config->init_barrier);
1313 
1314  return true;
1315 }
1316 
1317 static odp_bool_t setup_test(prog_config_t *config)
1318 {
1319  return setup_config(config) && setup_pktios(config) && print_summary(config) &&
1320  setup_workers(config);
1321 }
1322 
1323 static void run_control(prog_config_t *config)
1324 {
1325  if (config->runtime > 0U) {
1326  sleep(config->runtime);
1327  odp_atomic_store_u32(&config->is_running, 0U);
1328  } else {
1329  while (odp_atomic_load_u32(&config->is_running))
1330  sleep(1U);
1331  }
1332 }
1333 
1334 static void stop_test(prog_config_t *config)
1335 {
1336  const pktio_t *pktio;
1337 
1338  for (uint8_t i = 0U; i < config->num_ifs; ++i) {
1339  pktio = &config->pktios[i];
1340 
1341  if (pktio->handle != ODP_PKTIO_INVALID)
1342  (void)odp_pktio_stop(pktio->handle);
1343  }
1344 
1345  odp_barrier_wait(&config->term_barrier);
1346  (void)odph_thread_join(config->thread_tbl, config->num_workers);
1347 }
1348 
1349 static void print_humanised(uint64_t value)
1350 {
1351  if (value > GIGAS)
1352  printf("(%.2f Gpps)", (double)value / GIGAS);
1353  else if (value > MEGAS)
1354  printf("(%.2f Mpps)", (double)value / MEGAS);
1355  else if (value > KILOS)
1356  printf("(%.2f kpps)", (double)value / KILOS);
1357  else
1358  printf("(%" PRIu64 " pps)", value);
1359 }
1360 
1361 static void print_stats(const prog_config_t *config)
1362 {
1363  const stats_t *stats;
1364  uint64_t tot_tx = 0U, tot_tx_drops = 0U, pps, tot_pps = 0U;
1365 
1366  printf("L2 forwarding done:\n"
1367  "===================\n\n");
1368 
1369  for (uint32_t i = 0U; i < config->num_workers; ++i) {
1370  stats = &config->worker_config[i].stats;
1371  tot_tx += stats->tx;
1372  tot_tx_drops += stats->tx_drops;
1373  pps = stats->tx / ((double)stats->tm_ns / ODP_TIME_SEC_IN_NS);
1374  tot_pps += pps;
1375 
1376  printf(" worker %u:\n\n"
1377  " packets sent: %" PRIu64 "\n"
1378  " packets dropped: %" PRIu64 "\n"
1379  " packets per second: %" PRIu64 " ", i, stats->tx, stats->tx_drops,
1380  pps);
1381  print_humanised(pps);
1382  printf("\n\n");
1383  }
1384 
1385  printf(" total packets sent: %" PRIu64 "\n"
1386  " total packets dropped: %" PRIu64 "\n"
1387  " total packets per second: %" PRIu64 " ", tot_tx, tot_tx_drops, tot_pps);
1388  print_humanised(tot_pps);
1389  printf("\n\n");
1390 
1391  /* TODO: Results exporting can go here. */
1392 }
1393 
1394 static void teardown(const prog_config_t *config)
1395 {
1396  const pktio_t *pktio;
1397 
1398  for (uint8_t i = 0U; i < config->num_ifs; ++i) {
1399  pktio = &config->pktios[i];
1400 
1401  if (config->pktios[i].handle != ODP_PKTIO_INVALID) {
1402  if (config->is_promisc != config->orig_is_promisc)
1403  (void)odp_pktio_promisc_mode_set(pktio->handle,
1404  config->orig_is_promisc);
1405 
1406  if (config->mtu > 0U && config->orig_in_mtu > 0U &&
1407  config->orig_out_mtu > 0U)
1408  (void)odp_pktio_maxlen_set(pktio->handle, config->orig_in_mtu,
1409  config->orig_out_mtu);
1410 
1411  (void)odp_pktio_close(pktio->handle);
1412  }
1413 
1414  free(pktio->name);
1415  free(pktio->dst_name);
1416  }
1417 
1418  if (config->pool != ODP_POOL_INVALID)
1419  (void)odp_pool_destroy(config->pool);
1420 }
1421 
1422 int main(int argc, char **argv)
1423 {
1424  odph_helper_options_t odph_opts;
1425  odp_init_t init_param;
1427  odp_shm_t shm_cfg = ODP_SHM_INVALID;
1428  int ret = EXIT_SUCCESS;
1429  parse_result_t parse_res;
1430 
1431  argc = odph_parse_options(argc, argv);
1432 
1433  if (odph_options(&odph_opts) == -1) {
1434  ODPH_ERR("Error while reading ODP helper options, exiting\n");
1435  exit(EXIT_FAILURE);
1436  }
1437 
1438  odp_init_param_init(&init_param);
1439  init_param.mem_model = odph_opts.mem_model;
1440 
1441  if (odp_init_global(&odp_instance, &init_param, NULL)) {
1442  ODPH_ERR("ODP global init failed, exiting\n");
1443  exit(EXIT_FAILURE);
1444  }
1445 
1447  ODPH_ERR("ODP local init failed, exiting\n");
1448  exit(EXIT_FAILURE);
1449  }
1450 
1451  shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
1452  0U);
1453 
1454  if (shm_cfg == ODP_SHM_INVALID) {
1455  ODPH_ERR("Error reserving shared memory\n");
1456  ret = EXIT_FAILURE;
1457  goto out;
1458  }
1459 
1460  prog_conf = odp_shm_addr(shm_cfg);
1461 
1462  if (prog_conf == NULL) {
1463  ODPH_ERR("Error resolving shared memory address\n");
1464  ret = EXIT_FAILURE;
1465  goto out;
1466  }
1467 
1468  memset(prog_conf, 0, sizeof(*prog_conf));
1469  prog_conf->odp_instance = odp_instance;
1470  parse_res = setup_program(argc, argv, prog_conf);
1471 
1472  if (parse_res == PRS_NOK) {
1473  ret = EXIT_FAILURE;
1474  goto out;
1475  }
1476 
1477  if (parse_res == PRS_TERM) {
1478  ret = EXIT_SUCCESS;
1479  goto out;
1480  }
1481 
1482  if (!setup_test(prog_conf)) {
1483  ret = EXIT_FAILURE;
1484  goto out;
1485  }
1486 
1487  run_control(prog_conf);
1488  stop_test(prog_conf);
1489  print_stats(prog_conf);
1490 
1491 out:
1492  teardown(prog_conf);
1493 
1494  if (shm_cfg != ODP_SHM_INVALID)
1495  (void)odp_shm_free(shm_cfg);
1496 
1497  if (odp_term_local()) {
1498  ODPH_ERR("ODP local terminate failed, exiting\n");
1499  exit(EXIT_FAILURE);
1500  }
1501 
1503  ODPH_ERR("ODP global terminate failed, exiting\n");
1504  exit(EXIT_FAILURE);
1505  }
1506 
1507  return ret;
1508 }
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
#define ODP_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
#define odp_unlikely(x)
Branch unlikely taken.
Definition: spec/hints.h:64
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition: spec/hints.h:54
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
void odp_event_free(odp_event_t event)
Free event.
#define ODP_EVENT_INVALID
Invalid event.
int odp_instance(odp_instance_t *instance)
Get instance handle.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
int odp_pktio_mac_addr(odp_pktio_t pktio, void *mac_addr, int size)
Get the default MAC address of a packet IO interface.
void odp_pktin_queue_param_init(odp_pktin_queue_param_t *param)
Initialize packet input queue parameters.
void odp_pktio_param_init(odp_pktio_param_t *param)
Initialize pktio params.
int odp_pktio_promisc_mode(odp_pktio_t pktio)
Determine if promiscuous mode is enabled for a packet IO interface.
int odp_pktio_close(odp_pktio_t pktio)
Close a packet IO interface.
uint32_t odp_pktin_maxlen(odp_pktio_t pktio)
Maximum frame length at packet input.
uint32_t odp_pktout_maxlen(odp_pktio_t pktio)
Maximum frame length at packet output.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
int odp_pktio_maxlen_set(odp_pktio_t pktio, uint32_t maxlen_input, uint32_t maxlen_output)
Set maximum frame lengths.
int odp_pktio_promisc_mode_set(odp_pktio_t pktio, odp_bool_t enable)
Set promiscuous mode.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_config(odp_pktio_t pktio, const odp_pktio_config_t *config)
Configure packet IO interface options.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
Direct packet input queues.
void odp_pktout_queue_param_init(odp_pktout_queue_param_t *param)
Initialize packet output queue parameters.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
Receive packets directly from an interface input queue.
int odp_pktio_index(odp_pktio_t pktio)
Get pktio interface index.
int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa)
Query packet IO interface capabilities.
#define ODP_PKTIO_MAX_INDEX
Maximum packet IO interface index.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
uint64_t odp_pktio_to_u64(odp_pktio_t pktio)
Get printable value for an odp_pktio_t.
int odp_pktin_queue_config(odp_pktio_t pktio, const odp_pktin_queue_param_t *param)
Configure packet input queues.
int odp_pktout_queue_config(odp_pktio_t pktio, const odp_pktout_queue_param_t *param)
Configure packet output queues.
@ ODP_PKTOUT_MODE_DIRECT
Direct packet output on the interface.
@ ODP_PKTOUT_MODE_DISABLED
Application will never send to this interface.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIN_MODE_DIRECT
Direct packet input from the interface.
@ ODP_PKTIN_MODE_SCHED
Packet input through scheduler and scheduled event queues.
void odp_packet_from_event_multi(odp_packet_t pkt[], const odp_event_t ev[], int num)
Convert multiple packet events to packet handles.
int odp_packet_input_index(odp_packet_t pkt)
Packet input interface index.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
@ ODP_PROTO_LAYER_NONE
No layers.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
int odp_schedule_sync_t
Scheduler synchronization method.
int odp_schedule_multi_no_wait(odp_queue_t *from, odp_event_t events[], int num)
Schedule, do not wait for events.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_default_prio(void)
Default scheduling priority level.
void odp_schedule_pause(void)
Pause scheduling.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
void odp_schedule_resume(void)
Resume scheduling.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
odp_time_t odp_time_local_strict(void)
Current local time (strict)
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Packet input queue parameters.
uint32_t num_queues
Number of input queues to be created.
odp_pktio_op_mode_t op_mode
Operation mode.
odp_queue_param_t queue_param
Queue parameters.
odp_pktin_hash_proto_t hash_proto
Protocol field selection for hashing.
odp_bool_t hash_enable
Enable flow hashing.
odp_pktio_set_op_t set_op
Supported set operations.
uint32_t max_output
Maximum valid value for 'maxlen_output'.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_input
Maximum valid value for 'maxlen_input'.
uint32_t min_output
Minimum valid value for 'maxlen_output'.
uint32_t max_output_queues
Maximum number of output queues.
uint32_t min_input
Minimum valid value for 'maxlen_input'.
struct odp_pktio_capability_t::@107 maxlen
Supported frame lengths for odp_pktio_maxlen_set()
Packet IO configuration options.
odp_pktio_parser_config_t parser
Packet input parser configuration.
Packet IO parameters.
odp_pktin_mode_t in_mode
Packet input mode.
odp_pktout_mode_t out_mode
Packet output mode.
odp_proto_layer_t layer
Protocol parsing level in packet input.
Packet output queue parameters.
odp_pktio_op_mode_t op_mode
Operation mode.
uint32_t num_queues
Number of output queues to be created.
struct odp_pool_capability_t::@122 pkt
Packet pool capabilities
uint32_t max_num
Maximum number of buffers of any size.
uint32_t max_len
Maximum packet data length in bytes.
Pool parameters.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
odp_schedule_param_t sched
Scheduler parameters.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.
struct odp_pktin_hash_proto_t::@99 proto
Protocol header fields for hashing.
struct odp_pktio_set_op_t::@104 op
Operation flags.
uint32_t maxlen
Maximum frame length.
uint32_t promisc_mode
Promiscuous mode.