API Reference Manual  1.46.0
odp_l2fwd.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2014-2018 Linaro Limited
3  * Copyright (c) 2019-2024 Nokia
4  * Copyright (c) 2020-2021 Marvell
5  */
6 
21 /* enable strtok */
22 #ifndef _GNU_SOURCE
23 #define _GNU_SOURCE
24 #endif
25 
26 #include <stdlib.h>
27 #include <getopt.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <inttypes.h>
31 #include <signal.h>
32 
33 #include <odp_api.h>
34 #include <odp/helper/odph_api.h>
35 
36 /* Maximum number of worker threads */
37 #define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
38 
39 /* Default number of packets per pool */
40 #define DEFAULT_NUM_PKT (16 * 1024)
41 
42 /* Packet length to pool create */
43 #define POOL_PKT_LEN 1536
44 
45 /* Maximum number of packet in a burst */
46 #define MAX_PKT_BURST 32
47 
48 /* Maximum number of pktio queues per interface */
49 #define MAX_QUEUES 32
50 
51 /* Maximum number of schedule groups */
52 #define MAX_GROUPS 32
53 
54 /* Maximum number of pktio interfaces */
55 #define MAX_PKTIOS 8
56 
57 /* Default vector size */
58 #define DEFAULT_VEC_SIZE MAX_PKT_BURST
59 
60 /* Default vector timeout */
61 #define DEFAULT_VEC_TMO ODP_TIME_MSEC_IN_NS
62 
63 /* Maximum thread info string length */
64 #define EXTRA_STR_LEN 32
65 
66 /* Packet input mode */
67 typedef enum pktin_mode_t {
68  DIRECT_RECV,
69  PLAIN_QUEUE,
70  SCHED_PARALLEL,
71  SCHED_ATOMIC,
72  SCHED_ORDERED,
73 } pktin_mode_t;
74 
75 /* Packet output modes */
76 typedef enum pktout_mode_t {
77  PKTOUT_DIRECT,
78  PKTOUT_QUEUE
79 } pktout_mode_t;
80 
81 static inline int sched_mode(pktin_mode_t in_mode)
82 {
83  return (in_mode == SCHED_PARALLEL) ||
84  (in_mode == SCHED_ATOMIC) ||
85  (in_mode == SCHED_ORDERED);
86 }
87 
88 /* Get rid of path in filename - only for unix-type paths using '/' */
89 #define NO_PATH(file_name) (strrchr((file_name), '/') ? \
90  strrchr((file_name), '/') + 1 : (file_name))
91 /*
92  * Parsed command line application arguments
93  */
94 typedef struct {
95  /* Some extra features (e.g. error checks) have been enabled */
96  uint8_t extra_feat;
97 
98  /* Has some state that needs to be maintained across tx and/or rx */
99  uint8_t has_state;
100 
101  /* Prefetch packet data */
102  uint8_t prefetch;
103 
104  /* Change destination eth addresses */
105  uint8_t dst_change;
106 
107  /* Change source eth addresses */
108  uint8_t src_change;
109 
110  /* Read packet data in uint64_t words */
111  uint16_t data_rd;
112 
113  /* Check packet errors */
114  uint8_t error_check;
115 
116  /* Packet copy */
117  uint8_t packet_copy;
118 
119  /* Checksum offload */
120  uint8_t chksum;
121 
122  /* Print debug info on every packet */
123  uint8_t verbose_pkt;
124 
125  unsigned int cpu_count;
126  int if_count; /* Number of interfaces to be used */
127  int addr_count; /* Number of dst addresses to be used */
128  int num_workers; /* Number of worker threads */
129  char **if_names; /* Array of pointers to interface names */
130  odph_ethaddr_t addrs[MAX_PKTIOS]; /* Array of dst addresses */
131  pktin_mode_t in_mode; /* Packet input mode */
132  pktout_mode_t out_mode; /* Packet output mode */
133  int time; /* Time in seconds to run. */
134  int accuracy; /* Number of seconds to get and print stats */
135  char *if_str; /* Storage for interface names */
136  int sched_mode; /* Scheduler mode */
137  int num_groups; /* Number of scheduling groups */
138  int group_mode; /* How threads join groups */
139  int burst_rx; /* Receive burst size */
140  int rx_queues; /* RX queues per interface */
141  int pool_per_if; /* Create pool per interface */
142  uint32_t num_pkt; /* Number of packets per pool */
143  int flow_control; /* Flow control mode */
144  bool pause_rx; /* Reception of pause frames enabled */
145  bool pause_tx; /* Transmission of pause frames enabled */
146  bool vector_mode; /* Vector mode enabled */
147  uint32_t num_vec; /* Number of vectors per pool */
148  uint64_t vec_tmo_ns; /* Vector formation timeout in ns */
149  uint32_t vec_size; /* Vector size */
150  int verbose; /* Verbose output */
151  uint32_t packet_len; /* Maximum packet length supported */
152  uint32_t seg_len; /* Pool segment length */
153  int promisc_mode; /* Promiscuous mode enabled */
154  int flow_aware; /* Flow aware scheduling enabled */
155  uint8_t input_ts; /* Packet input timestamping enabled */
156  int mtu; /* Interface MTU */
157  int num_om;
158  int num_prio;
159 
160  struct {
162  uint32_t nth;
163  uint32_t thr_compl_id;
164  uint32_t tot_compl_id;
165  } tx_compl;
166 
167  char *output_map[MAX_PKTIOS]; /* Destination port mappings for interfaces */
168  odp_schedule_prio_t prio[MAX_PKTIOS]; /* Priority of input queues of an interface */
169 
170 } appl_args_t;
171 
172 /* Statistics */
173 typedef union ODP_ALIGNED_CACHE {
174  struct {
175  /* Number of forwarded packets */
176  uint64_t packets;
177  /* Packets dropped due to receive error */
178  uint64_t rx_drops;
179  /* Packets dropped due to transmit error */
180  uint64_t tx_drops;
181  /* Number of transmit completion start misses (previous incomplete) */
182  uint64_t tx_c_misses;
183  /* Number of transmit completion start failures */
184  uint64_t tx_c_fails;
185  /* Number of failed packet copies */
186  uint64_t copy_fails;
187  /* Dummy sum of packet data */
188  uint64_t dummy_sum;
189  } s;
190 
191  uint8_t padding[ODP_CACHE_LINE_SIZE];
192 } stats_t;
193 
194 /* Transmit completion specific state data */
195 typedef struct {
196  /* Options that are passed to transmit completion requests */
198  /* Thread specific initial value for transmit completion IDs */
199  uint32_t init;
200  /* Thread specific maximum value for transmit completion IDs */
201  uint32_t max;
202  /* Next free completion ID to be used for a transmit completion request */
203  uint32_t free_head;
204  /* Next completion ID to be polled for transmit completion readiness */
205  uint32_t poll_head;
206  /* Number of active requests */
207  uint32_t num_act;
208  /* Maximum number of active requests */
209  uint32_t max_act;
210  /* Transmit completion request interval for packets */
211  int interval;
212  /* Next packet in a send burst for which to request transmit completion */
213  int next_req;
214 } tx_compl_t;
215 
216 /* Thread specific state data */
217 typedef struct {
218  tx_compl_t tx_compl;
219 } state_t;
220 
221 /* Thread specific data */
222 typedef struct thread_args_t {
223  stats_t stats;
224  state_t state;
225 
226  struct {
227  odp_pktin_queue_t pktin;
228  odp_pktout_queue_t pktout;
229  odp_queue_t rx_queue;
230  odp_queue_t tx_queue;
231  int rx_idx;
232  int tx_idx;
233  int rx_queue_idx;
234  int tx_queue_idx;
235  } pktio[MAX_PKTIOS];
236 
237  /* Groups to join */
238  odp_schedule_group_t group[MAX_GROUPS];
239 
240  int thr_idx;
241  int num_pktio;
242  int num_grp_join;
243 
244 } thread_args_t;
245 
246 /*
247  * Grouping of all global data
248  */
249 typedef struct {
250  /* Thread table */
251  odph_thread_t thread_tbl[MAX_WORKERS];
252  /* Thread specific arguments */
253  thread_args_t thread_args[MAX_WORKERS];
254  /* Barriers to synchronize main and workers */
255  odp_barrier_t init_barrier;
256  odp_barrier_t term_barrier;
257  /* Application (parsed) arguments */
258  appl_args_t appl;
259  /* Table of port ethernet addresses */
260  odph_ethaddr_t port_eth_addr[MAX_PKTIOS];
261  /* Table of dst ethernet addresses */
262  odph_ethaddr_t dst_eth_addr[MAX_PKTIOS];
263  /* Table of dst ports. This is used by non-sched modes. */
264  int dst_port[MAX_PKTIOS];
265  /* Table of pktio handles */
266  struct {
267  odp_pktio_t pktio;
268  odp_pktin_queue_t pktin[MAX_QUEUES];
269  odp_pktout_queue_t pktout[MAX_QUEUES];
270  odp_queue_t rx_q[MAX_QUEUES];
271  odp_queue_t tx_q[MAX_QUEUES];
272  odp_queue_t compl_q;
273  int num_rx_thr;
274  int num_tx_thr;
275  int num_rx_queue;
276  int num_tx_queue;
277  int next_rx_queue;
278  int next_tx_queue;
279  } pktios[MAX_PKTIOS];
280 
281  /* Destination port lookup table.
282  * Table index is pktio_index of the API. This is used by the sched
283  * mode. */
284  uint8_t dst_port_from_idx[ODP_PKTIO_MAX_INDEX + 1];
285  /* Break workers loop if set to 1 */
286  odp_atomic_u32_t exit_threads;
287 
288  uint32_t pkt_len;
289  uint32_t num_pkt;
290  uint32_t seg_len;
291  uint32_t vector_num;
292  uint32_t vector_max_size;
293  char cpumaskstr[ODP_CPUMASK_STR_SIZE];
294 
295 } args_t;
296 
297 /* Global pointer to args */
298 static args_t *gbl_args;
299 
300 static void sig_handler(int signo ODP_UNUSED)
301 {
302  if (gbl_args == NULL)
303  return;
304  odp_atomic_store_u32(&gbl_args->exit_threads, 1);
305 }
306 
307 static int setup_sig_handler(void)
308 {
309  struct sigaction action = { .sa_handler = sig_handler };
310 
311  if (sigemptyset(&action.sa_mask) || sigaction(SIGINT, &action, NULL))
312  return -1;
313 
314  return 0;
315 }
316 
317 /*
318  * Drop packets which input parsing marked as containing errors.
319  *
320  * Frees packets with error and modifies pkt_tbl[] to only contain packets with
321  * no detected errors.
322  *
323  * pkt_tbl Array of packets
324  * num Number of packets in pkt_tbl[]
325  *
326  * Returns number of packets dropped
327  */
328 static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)
329 {
330  odp_packet_t pkt;
331  unsigned dropped = 0;
332  unsigned i, j;
333 
334  for (i = 0, j = 0; i < num; ++i) {
335  pkt = pkt_tbl[i];
336 
338  odp_packet_free(pkt); /* Drop */
339  dropped++;
340  } else if (odp_unlikely(i != j++)) {
341  pkt_tbl[j - 1] = pkt;
342  }
343  }
344 
345  return dropped;
346 }
347 
348 static inline void prefetch_data(uint8_t prefetch, odp_packet_t pkt_tbl[], uint32_t num)
349 {
350  if (prefetch == 0)
351  return;
352 
353  for (uint32_t i = 0; i < num; i++)
354  odp_packet_prefetch(pkt_tbl[i], 0, prefetch * 64);
355 }
356 
357 /*
358  * Fill packets' eth addresses according to the destination port
359  *
360  * pkt_tbl Array of packets
361  * num Number of packets in the array
362  * dst_port Destination port
363  */
364 static inline void fill_eth_addrs(odp_packet_t pkt_tbl[],
365  unsigned num, int dst_port)
366 {
367  odp_packet_t pkt;
368  odph_ethhdr_t *eth;
369  unsigned i;
370 
371  if (!gbl_args->appl.dst_change && !gbl_args->appl.src_change)
372  return;
373 
374  for (i = 0; i < num; ++i) {
375  pkt = pkt_tbl[i];
376  eth = odp_packet_data(pkt);
377 
378  if (gbl_args->appl.src_change)
379  eth->src = gbl_args->port_eth_addr[dst_port];
380 
381  if (gbl_args->appl.dst_change)
382  eth->dst = gbl_args->dst_eth_addr[dst_port];
383  }
384 }
385 
386 static inline int event_queue_send(odp_queue_t queue, odp_packet_t *pkt_tbl,
387  unsigned pkts)
388 {
389  int ret;
390  unsigned sent = 0;
391  odp_event_t ev_tbl[pkts];
392 
393  odp_packet_to_event_multi(pkt_tbl, ev_tbl, pkts);
394 
395  while (sent < pkts) {
396  ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent);
397 
398  if (odp_unlikely(ret <= 0)) {
399  if (ret < 0 || odp_atomic_load_u32(&gbl_args->exit_threads))
400  break;
401  }
402 
403  sent += ret;
404  }
405 
406  return sent;
407 }
408 
409 static inline void chksum_insert(odp_packet_t *pkt_tbl, int pkts)
410 {
411  odp_packet_t pkt;
412  int i;
413 
414  for (i = 0; i < pkts; i++) {
415  pkt = pkt_tbl[i];
418  }
419 }
420 
421 static void print_packets(odp_packet_t *pkt_tbl, int num)
422 {
423  odp_packet_t pkt;
424  uintptr_t data_ptr;
425  uint32_t bit, align;
426 
427  for (int i = 0; i < num; i++) {
428  pkt = pkt_tbl[i];
429  data_ptr = (uintptr_t)odp_packet_data(pkt);
430 
431  for (bit = 0, align = 1; bit < 32; bit++, align *= 2)
432  if (data_ptr & (0x1 << bit))
433  break;
434 
435  printf(" Packet data: 0x%" PRIxPTR "\n"
436  " Packet len: %u\n"
437  " Packet seg len: %u\n"
438  " Data align: %u\n"
439  " Num segments: %i\n"
440  " Headroom size: %u\n"
441  " User area size: %u\n\n",
442  data_ptr, odp_packet_len(pkt), odp_packet_seg_len(pkt), align,
445  }
446 }
447 
448 static inline void data_rd(odp_packet_t *pkt_tbl, int num, uint16_t rd_words, stats_t *stats)
449 {
450  odp_packet_t pkt;
451  uint64_t *data;
452  int i;
453  uint32_t len, words, j;
454  uint64_t sum = 0;
455 
456  for (i = 0; i < num; i++) {
457  pkt = pkt_tbl[i];
458  data = odp_packet_data(pkt);
459  len = odp_packet_seg_len(pkt);
460 
461  words = rd_words;
462  if (rd_words * 8 > len)
463  words = len / 8;
464 
465  for (j = 0; j < words; j++)
466  sum += data[j];
467  }
468 
469  stats->s.dummy_sum += sum;
470 }
471 
472 static inline int copy_packets(odp_packet_t *pkt_tbl, int pkts)
473 {
474  odp_packet_t old_pkt, new_pkt;
475  odp_pool_t pool;
476  int i;
477  int copy_fails = 0;
478 
479  for (i = 0; i < pkts; i++) {
480  old_pkt = pkt_tbl[i];
481  pool = odp_packet_pool(old_pkt);
482  new_pkt = odp_packet_copy(old_pkt, pool);
483  if (odp_likely(new_pkt != ODP_PACKET_INVALID)) {
484  pkt_tbl[i] = new_pkt;
485  odp_packet_free(old_pkt);
486  } else {
487  copy_fails++;
488  }
489  }
490 
491  return copy_fails;
492 }
493 
494 /*
495  * Return number of packets remaining in the pkt_tbl
496  */
497 static inline int process_extra_features(const appl_args_t *appl_args, odp_packet_t *pkt_tbl,
498  int pkts, stats_t *stats)
499 {
500  if (odp_unlikely(appl_args->extra_feat)) {
501  uint16_t rd_words = appl_args->data_rd;
502 
503  if (appl_args->verbose_pkt)
504  print_packets(pkt_tbl, pkts);
505 
506  if (rd_words)
507  data_rd(pkt_tbl, pkts, rd_words, stats);
508 
509  if (appl_args->packet_copy) {
510  int fails;
511 
512  fails = copy_packets(pkt_tbl, pkts);
513  stats->s.copy_fails += fails;
514  }
515 
516  if (appl_args->chksum)
517  chksum_insert(pkt_tbl, pkts);
518 
519  if (appl_args->error_check) {
520  int rx_drops;
521 
522  /* Drop packets with errors */
523  rx_drops = drop_err_pkts(pkt_tbl, pkts);
524 
525  if (odp_unlikely(rx_drops)) {
526  stats->s.rx_drops += rx_drops;
527  if (pkts == rx_drops)
528  return 0;
529 
530  pkts -= rx_drops;
531  }
532  }
533  }
534  return pkts;
535 }
536 
537 static inline void handle_tx_event_compl(tx_compl_t *tx_c, odp_packet_t pkts[], int num,
538  int tx_idx, stats_t *stats)
539 {
540  odp_packet_t pkt;
541  int next_req = tx_c->next_req;
542  const int interval = tx_c->interval;
543 
544  tx_c->opt.queue = gbl_args->pktios[tx_idx].compl_q;
545 
546  while (next_req <= num) {
547  pkt = pkts[next_req - 1];
548 
549  if (odp_packet_tx_compl_request(pkt, &tx_c->opt) < 0) {
550  stats->s.tx_c_fails++;
551  /* Missed one, try requesting for the first packet of next burst. */
552  next_req = num + 1;
553  break;
554  }
555 
556  next_req += interval;
557  }
558 
559  tx_c->next_req = next_req - num;
560 }
561 
562 static inline void handle_tx_poll_compl(tx_compl_t *tx_c, odp_packet_t pkts[], int num, int tx_idx,
563  stats_t *stats)
564 {
565  uint32_t num_act = tx_c->num_act, poll_head = tx_c->poll_head, free_head = tx_c->free_head;
566  const uint32_t max = tx_c->max, init = tx_c->init, max_act = tx_c->max_act;
567  odp_pktio_t pktio = gbl_args->pktios[tx_idx].pktio;
568  int next_req = tx_c->next_req;
569  odp_packet_t pkt;
570  const int interval = tx_c->interval;
571 
572  while (num_act > 0) {
573  if (odp_packet_tx_compl_done(pktio, poll_head) < 1)
574  break;
575 
576  --num_act;
577 
578  if (++poll_head > max)
579  poll_head = init;
580  }
581 
582  while (next_req <= num) {
583  pkt = pkts[next_req - 1];
584 
585  if (num_act == max_act) {
586  stats->s.tx_c_misses++;
587  /* Missed one, try requesting for the first packet of next burst. */
588  next_req = num + 1;
589  break;
590  }
591 
592  tx_c->opt.compl_id = free_head;
593 
594  if (odp_packet_tx_compl_request(pkt, &tx_c->opt) < 0) {
595  stats->s.tx_c_fails++;
596  /* Missed one, try requesting for the first packet of next burst. */
597  next_req = num + 1;
598  break;
599  }
600 
601  if (++free_head > max)
602  free_head = init;
603 
604  ++num_act;
605  next_req += interval;
606  }
607 
608  tx_c->free_head = free_head;
609  tx_c->poll_head = poll_head;
610  tx_c->num_act = num_act;
611  tx_c->next_req = next_req - num;
612 }
613 
614 static inline void handle_tx_state(state_t *state, odp_packet_t pkts[], int num, int tx_idx,
615  stats_t *stats)
616 {
617  tx_compl_t *tx_c = &state->tx_compl;
618 
619  if (tx_c->opt.mode == ODP_PACKET_TX_COMPL_EVENT)
620  handle_tx_event_compl(tx_c, pkts, num, tx_idx, stats);
621  else if (tx_c->opt.mode == ODP_PACKET_TX_COMPL_POLL)
622  handle_tx_poll_compl(tx_c, pkts, num, tx_idx, stats);
623 }
624 
625 static inline void handle_state_failure(state_t *state, odp_packet_t packet)
626 {
627  if (odp_packet_has_tx_compl_request(packet) != 0) {
628  --state->tx_compl.num_act;
629  --state->tx_compl.free_head;
630 
631  if (state->tx_compl.free_head == UINT32_MAX ||
632  state->tx_compl.free_head < state->tx_compl.init)
633  state->tx_compl.free_head = state->tx_compl.max;
634  }
635 }
636 
637 static inline void send_packets(odp_packet_t *pkt_tbl,
638  int pkts,
639  int use_event_queue,
640  int tx_idx,
641  odp_queue_t tx_queue,
642  odp_pktout_queue_t pktout_queue,
643  state_t *state,
644  stats_t *stats)
645 {
646  int sent;
647  unsigned int tx_drops;
648  int i;
649  odp_packet_t pkt;
650 
651  if (odp_unlikely(state != NULL))
652  handle_tx_state(state, pkt_tbl, pkts, tx_idx, stats);
653 
654  if (odp_unlikely(use_event_queue))
655  sent = event_queue_send(tx_queue, pkt_tbl, pkts);
656  else
657  sent = odp_pktout_send(pktout_queue, pkt_tbl, pkts);
658 
659  sent = odp_unlikely(sent < 0) ? 0 : sent;
660  tx_drops = pkts - sent;
661 
662  if (odp_unlikely(tx_drops)) {
663  stats->s.tx_drops += tx_drops;
664 
665  /* Drop rejected packets */
666  for (i = sent; i < pkts; i++) {
667  pkt = pkt_tbl[i];
668  handle_state_failure(state, pkt);
669  odp_packet_free(pkt);
670  }
671  }
672 
673  stats->s.packets += pkts;
674 }
675 
676 static int handle_rx_state(state_t *state, odp_event_t evs[], int num)
677 {
678  if (state->tx_compl.opt.mode != ODP_PACKET_TX_COMPL_EVENT ||
679  odp_event_type(evs[0]) != ODP_EVENT_PACKET_TX_COMPL)
680  return num;
681 
682  odp_event_free_multi(evs, num);
683 
684  return 0;
685 }
686 
687 /*
688  * Packet IO worker thread using scheduled queues and vector mode.
689  *
690  * arg thread arguments of type 'thread_args_t *'
691  */
692 static int run_worker_sched_mode_vector(void *arg)
693 {
694  int thr;
695  int i;
696  int pktio, num_pktio;
697  uint16_t max_burst;
698  odp_thrmask_t mask;
699  odp_pktout_queue_t pktout[MAX_PKTIOS];
700  odp_queue_t tx_queue[MAX_PKTIOS];
701  thread_args_t *thr_args = arg;
702  stats_t *stats = &thr_args->stats;
703  const appl_args_t *appl_args = &gbl_args->appl;
704  state_t *state = appl_args->has_state ? &thr_args->state : NULL;
705  int use_event_queue = gbl_args->appl.out_mode;
706  pktin_mode_t in_mode = gbl_args->appl.in_mode;
707 
708  thr = odp_thread_id();
709  max_burst = gbl_args->appl.burst_rx;
710 
711  odp_thrmask_zero(&mask);
712  odp_thrmask_set(&mask, thr);
713 
714  /* Join non-default groups */
715  for (i = 0; i < thr_args->num_grp_join; i++) {
716  if (odp_schedule_group_join(thr_args->group[i], &mask)) {
717  ODPH_ERR("Join failed: %i\n", i);
718  return -1;
719  }
720  }
721 
722  num_pktio = thr_args->num_pktio;
723 
724  if (num_pktio > MAX_PKTIOS) {
725  ODPH_ERR("Too many pktios %i\n", num_pktio);
726  return -1;
727  }
728 
729  for (pktio = 0; pktio < num_pktio; pktio++) {
730  tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
731  pktout[pktio] = thr_args->pktio[pktio].pktout;
732  }
733 
734  printf("[%02i] PKTIN_SCHED_%s_VECTOR, %s\n", thr,
735  (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
736  ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
737  (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
738 
739  odp_barrier_wait(&gbl_args->init_barrier);
740 
741  /* Loop packets */
742  while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
743  odp_event_t ev_tbl[MAX_PKT_BURST];
744  int events;
745 
746  events = odp_schedule_multi_no_wait(NULL, ev_tbl, max_burst);
747 
748  if (events <= 0)
749  continue;
750 
751  for (i = 0; i < events; i++) {
753  odp_packet_t *pkt_tbl = NULL;
754  odp_packet_t pkt;
755  int src_idx, dst_idx;
756  int pkts = 0;
757 
758  if (odp_event_type(ev_tbl[i]) == ODP_EVENT_PACKET) {
759  pkt = odp_packet_from_event(ev_tbl[i]);
760  pkt_tbl = &pkt;
761  pkts = 1;
762  } else if (odp_event_type(ev_tbl[i]) == ODP_EVENT_PACKET_VECTOR) {
763  pkt_vec = odp_packet_vector_from_event(ev_tbl[i]);
764  pkts = odp_packet_vector_tbl(pkt_vec, &pkt_tbl);
765  } else if (state != NULL) {
766  pkts = handle_rx_state(state, ev_tbl, events);
767 
768  if (pkts <= 0)
769  continue;
770  }
771 
772  prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
773 
774  pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
775 
776  if (odp_unlikely(pkts == 0)) {
777  if (pkt_vec != ODP_PACKET_VECTOR_INVALID)
778  odp_packet_vector_free(pkt_vec);
779  continue;
780  }
781 
782  /* packets from the same queue are from the same interface */
783  src_idx = odp_packet_input_index(pkt_tbl[0]);
784  ODPH_ASSERT(src_idx >= 0);
785  dst_idx = gbl_args->dst_port_from_idx[src_idx];
786  fill_eth_addrs(pkt_tbl, pkts, dst_idx);
787 
788  send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue[dst_idx],
789  pktout[dst_idx], state, stats);
790 
791  if (pkt_vec != ODP_PACKET_VECTOR_INVALID)
792  odp_packet_vector_free(pkt_vec);
793  }
794  }
795 
796  /*
797  * Free prefetched packets before entering the thread barrier.
798  * Such packets can block sending of later packets in other threads
799  * that then would never enter the thread barrier and we would
800  * end up in a dead-lock.
801  */
803  while (1) {
804  odp_event_t ev;
805 
806  ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
807  if (ev == ODP_EVENT_INVALID)
808  break;
809 
810  odp_event_free(ev);
811  }
812 
813  /* Make sure that latest stat writes are visible to other threads */
814  odp_mb_full();
815 
816  /* Wait until pktio devices are stopped */
817  odp_barrier_wait(&gbl_args->term_barrier);
818 
819  /* Free remaining events in queues */
821  while (1) {
822  odp_event_t ev;
823 
824  ev = odp_schedule(NULL,
826 
827  if (ev == ODP_EVENT_INVALID)
828  break;
829 
830  odp_event_free(ev);
831  }
832 
833  return 0;
834 }
835 
836 /*
837  * Packet IO worker thread using scheduled queues
838  *
839  * arg thread arguments of type 'thread_args_t *'
840  */
841 static int run_worker_sched_mode(void *arg)
842 {
843  int pkts;
844  int thr;
845  int dst_idx;
846  int i;
847  int pktio, num_pktio;
848  uint16_t max_burst;
849  odp_thrmask_t mask;
850  odp_pktout_queue_t pktout[MAX_PKTIOS];
851  odp_queue_t tx_queue[MAX_PKTIOS];
852  char extra_str[EXTRA_STR_LEN];
853  thread_args_t *thr_args = arg;
854  stats_t *stats = &thr_args->stats;
855  const appl_args_t *appl_args = &gbl_args->appl;
856  state_t *state = appl_args->has_state ? &thr_args->state : NULL;
857  int use_event_queue = gbl_args->appl.out_mode;
858  pktin_mode_t in_mode = gbl_args->appl.in_mode;
859 
860  thr = odp_thread_id();
861  max_burst = gbl_args->appl.burst_rx;
862 
863  memset(extra_str, 0, EXTRA_STR_LEN);
864  odp_thrmask_zero(&mask);
865  odp_thrmask_set(&mask, thr);
866 
867  /* Join non-default groups */
868  for (i = 0; i < thr_args->num_grp_join; i++) {
869  if (odp_schedule_group_join(thr_args->group[i], &mask)) {
870  ODPH_ERR("Join failed: %i\n", i);
871  return -1;
872  }
873 
874  if (gbl_args->appl.verbose) {
875  uint64_t tmp = (uint64_t)(uintptr_t)thr_args->group[i];
876 
877  printf("[%02i] Joined group 0x%" PRIx64 "\n", thr, tmp);
878  }
879  }
880 
881  if (thr_args->num_grp_join)
882  snprintf(extra_str, EXTRA_STR_LEN, ", joined %i groups", thr_args->num_grp_join);
883  else if (gbl_args->appl.num_groups == 0)
884  snprintf(extra_str, EXTRA_STR_LEN, ", GROUP_ALL");
885  else if (gbl_args->appl.num_groups)
886  snprintf(extra_str, EXTRA_STR_LEN, ", GROUP_WORKER");
887 
888  num_pktio = thr_args->num_pktio;
889 
890  if (num_pktio > MAX_PKTIOS) {
891  ODPH_ERR("Too many pktios %i\n", num_pktio);
892  return -1;
893  }
894 
895  for (pktio = 0; pktio < num_pktio; pktio++) {
896  tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
897  pktout[pktio] = thr_args->pktio[pktio].pktout;
898  }
899 
900  printf("[%02i] PKTIN_SCHED_%s, %s%s\n", thr,
901  (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
902  ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
903  (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT", extra_str);
904 
905  odp_barrier_wait(&gbl_args->init_barrier);
906 
907  /* Loop packets */
908  while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
909  odp_event_t ev_tbl[MAX_PKT_BURST];
910  odp_packet_t pkt_tbl[MAX_PKT_BURST];
911  int src_idx;
912 
913  pkts = odp_schedule_multi_no_wait(NULL, ev_tbl, max_burst);
914 
915  if (pkts <= 0)
916  continue;
917 
918  if (odp_unlikely(state != NULL)) {
919  pkts = handle_rx_state(state, ev_tbl, pkts);
920 
921  if (pkts <= 0)
922  continue;
923  }
924 
925  odp_packet_from_event_multi(pkt_tbl, ev_tbl, pkts);
926 
927  prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
928 
929  pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
930 
931  if (odp_unlikely(pkts == 0))
932  continue;
933 
934  /* packets from the same queue are from the same interface */
935  src_idx = odp_packet_input_index(pkt_tbl[0]);
936  ODPH_ASSERT(src_idx >= 0);
937  dst_idx = gbl_args->dst_port_from_idx[src_idx];
938  fill_eth_addrs(pkt_tbl, pkts, dst_idx);
939 
940  send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue[dst_idx],
941  pktout[dst_idx], state, stats);
942  }
943 
944  /*
945  * Free prefetched packets before entering the thread barrier.
946  * Such packets can block sending of later packets in other threads
947  * that then would never enter the thread barrier and we would
948  * end up in a dead-lock.
949  */
951  while (1) {
952  odp_event_t ev;
953 
954  ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
955  if (ev == ODP_EVENT_INVALID)
956  break;
957  odp_event_free(ev);
958  }
959 
960  /* Make sure that latest stat writes are visible to other threads */
961  odp_mb_full();
962 
963  /* Wait until pktio devices are stopped */
964  odp_barrier_wait(&gbl_args->term_barrier);
965 
966  /* Free remaining events in queues */
968  while (1) {
969  odp_event_t ev;
970 
971  ev = odp_schedule(NULL,
973 
974  if (ev == ODP_EVENT_INVALID)
975  break;
976 
977  odp_event_free(ev);
978  }
979 
980  return 0;
981 }
982 
983 /*
984  * Packet IO worker thread using plain queues
985  *
986  * arg thread arguments of type 'thread_args_t *'
987  */
988 static int run_worker_plain_queue_mode(void *arg)
989 {
990  int thr;
991  int pkts;
992  uint16_t max_burst;
993  odp_packet_t pkt_tbl[MAX_PKT_BURST];
994  int dst_idx, num_pktio;
995  odp_queue_t queue;
996  odp_pktout_queue_t pktout;
997  odp_queue_t tx_queue;
998  int pktio = 0;
999  thread_args_t *thr_args = arg;
1000  stats_t *stats = &thr_args->stats;
1001  const appl_args_t *appl_args = &gbl_args->appl;
1002  state_t *state = appl_args->has_state ? &thr_args->state : NULL;
1003  int use_event_queue = gbl_args->appl.out_mode;
1004  int i;
1005 
1006  thr = odp_thread_id();
1007  max_burst = gbl_args->appl.burst_rx;
1008 
1009  num_pktio = thr_args->num_pktio;
1010  dst_idx = thr_args->pktio[pktio].tx_idx;
1011  queue = thr_args->pktio[pktio].rx_queue;
1012  pktout = thr_args->pktio[pktio].pktout;
1013  tx_queue = thr_args->pktio[pktio].tx_queue;
1014 
1015  printf("[%02i] num pktios %i, PKTIN_QUEUE, %s\n", thr, num_pktio,
1016  (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
1017 
1018  odp_barrier_wait(&gbl_args->init_barrier);
1019 
1020  /* Loop packets */
1021  while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
1022  odp_event_t event[MAX_PKT_BURST];
1023 
1024  if (num_pktio > 1) {
1025  dst_idx = thr_args->pktio[pktio].tx_idx;
1026  queue = thr_args->pktio[pktio].rx_queue;
1027  pktout = thr_args->pktio[pktio].pktout;
1028  if (odp_unlikely(use_event_queue))
1029  tx_queue = thr_args->pktio[pktio].tx_queue;
1030 
1031  pktio++;
1032  if (pktio == num_pktio)
1033  pktio = 0;
1034  }
1035 
1036  pkts = odp_queue_deq_multi(queue, event, max_burst);
1037  if (odp_unlikely(pkts <= 0))
1038  continue;
1039 
1040  odp_packet_from_event_multi(pkt_tbl, event, pkts);
1041 
1042  prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
1043 
1044  pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
1045 
1046  if (odp_unlikely(pkts == 0))
1047  continue;
1048 
1049  fill_eth_addrs(pkt_tbl, pkts, dst_idx);
1050 
1051  send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue, pktout, state,
1052  stats);
1053  }
1054 
1055  /* Make sure that latest stat writes are visible to other threads */
1056  odp_mb_full();
1057 
1058  /* Wait until pktio devices are stopped */
1059  odp_barrier_wait(&gbl_args->term_barrier);
1060 
1061  /* Free remaining events in queues */
1062  for (i = 0; i < num_pktio; i++) {
1063  odp_time_t recv_last = odp_time_local();
1064  odp_time_t since_last;
1065 
1066  queue = thr_args->pktio[i].rx_queue;
1067  do {
1068  odp_event_t ev = odp_queue_deq(queue);
1069 
1070  if (ev != ODP_EVENT_INVALID) {
1071  recv_last = odp_time_local();
1072  odp_event_free(ev);
1073  }
1074 
1075  since_last = odp_time_diff(odp_time_local(), recv_last);
1076  } while (odp_time_to_ns(since_last) < ODP_TIME_SEC_IN_NS);
1077  }
1078 
1079  return 0;
1080 }
1081 
1082 /*
1083  * Packet IO worker thread accessing IO resources directly
1084  *
1085  * arg thread arguments of type 'thread_args_t *'
1086  */
1087 static int run_worker_direct_mode(void *arg)
1088 {
1089  int thr;
1090  int pkts;
1091  uint16_t max_burst;
1092  odp_packet_t pkt_tbl[MAX_PKT_BURST];
1093  int dst_idx, num_pktio;
1094  odp_pktin_queue_t pktin;
1095  odp_pktout_queue_t pktout;
1096  odp_queue_t tx_queue;
1097  int pktio = 0;
1098  thread_args_t *thr_args = arg;
1099  stats_t *stats = &thr_args->stats;
1100  const appl_args_t *appl_args = &gbl_args->appl;
1101  state_t *state = appl_args->has_state ? &thr_args->state : NULL;
1102  int use_event_queue = gbl_args->appl.out_mode;
1103 
1104  thr = odp_thread_id();
1105  max_burst = gbl_args->appl.burst_rx;
1106 
1107  num_pktio = thr_args->num_pktio;
1108  dst_idx = thr_args->pktio[pktio].tx_idx;
1109  pktin = thr_args->pktio[pktio].pktin;
1110  pktout = thr_args->pktio[pktio].pktout;
1111  tx_queue = thr_args->pktio[pktio].tx_queue;
1112 
1113  printf("[%02i] num pktios %i, PKTIN_DIRECT, %s\n", thr, num_pktio,
1114  (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
1115 
1116  odp_barrier_wait(&gbl_args->init_barrier);
1117 
1118  /* Loop packets */
1119  while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
1120  if (num_pktio > 1) {
1121  dst_idx = thr_args->pktio[pktio].tx_idx;
1122  pktin = thr_args->pktio[pktio].pktin;
1123  pktout = thr_args->pktio[pktio].pktout;
1124  if (odp_unlikely(use_event_queue))
1125  tx_queue = thr_args->pktio[pktio].tx_queue;
1126 
1127  pktio++;
1128  if (pktio == num_pktio)
1129  pktio = 0;
1130  }
1131 
1132  pkts = odp_pktin_recv(pktin, pkt_tbl, max_burst);
1133  if (odp_unlikely(pkts <= 0))
1134  continue;
1135 
1136  prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
1137 
1138  pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats);
1139 
1140  if (odp_unlikely(pkts == 0))
1141  continue;
1142 
1143  fill_eth_addrs(pkt_tbl, pkts, dst_idx);
1144 
1145  send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue, pktout, state,
1146  stats);
1147  }
1148 
1149  /* Make sure that latest stat writes are visible to other threads */
1150  odp_mb_full();
1151 
1152  return 0;
1153 }
1154 
1155 static int set_pktin_vector_params(odp_pktin_queue_param_t *pktin_param, odp_pool_t vec_pool,
1156  odp_pktio_capability_t pktio_capa)
1157 {
1158  uint64_t vec_tmo_ns;
1159  uint32_t vec_size;
1160 
1161  pktin_param->vector.enable = true;
1162  pktin_param->vector.pool = vec_pool;
1163 
1164  if (gbl_args->appl.vec_size == 0)
1165  vec_size = DEFAULT_VEC_SIZE;
1166  else
1167  vec_size = gbl_args->appl.vec_size;
1168 
1169  if (vec_size > pktio_capa.vector.max_size ||
1170  vec_size < pktio_capa.vector.min_size) {
1171  if (gbl_args->appl.vec_size == 0) {
1172  vec_size = (vec_size > pktio_capa.vector.max_size) ?
1173  pktio_capa.vector.max_size : pktio_capa.vector.min_size;
1174  printf("\nWarning: Modified vector size to %u\n\n", vec_size);
1175  } else {
1176  ODPH_ERR("Invalid pktio vector size %u, valid range [%u, %u]\n",
1177  vec_size, pktio_capa.vector.min_size, pktio_capa.vector.max_size);
1178  return -1;
1179  }
1180  }
1181  pktin_param->vector.max_size = vec_size;
1182 
1183  if (gbl_args->appl.vec_tmo_ns == 0)
1184  vec_tmo_ns = DEFAULT_VEC_TMO;
1185  else
1186  vec_tmo_ns = gbl_args->appl.vec_tmo_ns;
1187 
1188  if (vec_tmo_ns > pktio_capa.vector.max_tmo_ns ||
1189  vec_tmo_ns < pktio_capa.vector.min_tmo_ns) {
1190  if (gbl_args->appl.vec_tmo_ns == 0) {
1191  vec_tmo_ns = (vec_tmo_ns > pktio_capa.vector.max_tmo_ns) ?
1192  pktio_capa.vector.max_tmo_ns : pktio_capa.vector.min_tmo_ns;
1193  printf("\nWarning: Modified vector timeout to %" PRIu64 "\n\n", vec_tmo_ns);
1194  } else {
1195  ODPH_ERR("Invalid vector timeout %" PRIu64 ", valid range [%" PRIu64
1196  ", %" PRIu64 "]\n", vec_tmo_ns,
1197  pktio_capa.vector.min_tmo_ns, pktio_capa.vector.max_tmo_ns);
1198  return -1;
1199  }
1200  }
1201  pktin_param->vector.max_tmo_ns = vec_tmo_ns;
1202 
1203  return 0;
1204 }
1205 
1206 /*
1207  * Create a pktio handle, optionally associating a default input queue.
1208  *
1209  * dev Name of device to open
1210  * index Pktio index
1211  * pool Pool to associate with device for packet RX/TX
1212  *
1213  * Returns 0 on success, -1 on failure
1214  */
1215 static int create_pktio(const char *dev, int idx, int num_rx, int num_tx, odp_pool_t pool,
1216  odp_pool_t vec_pool, odp_schedule_group_t group)
1217 {
1218  odp_pktio_t pktio;
1219  odp_pktio_param_t pktio_param;
1220  odp_schedule_sync_t sync_mode;
1221  odp_pktio_capability_t pktio_capa;
1222  odp_pktio_config_t config;
1223  odp_pktin_queue_param_t pktin_param;
1224  odp_pktout_queue_param_t pktout_param;
1225  odp_queue_param_t compl_queue;
1226  odp_pktio_op_mode_t mode_rx;
1227  odp_pktio_op_mode_t mode_tx;
1228  pktin_mode_t in_mode = gbl_args->appl.in_mode;
1229  odp_pktio_info_t info;
1230  uint8_t *addr;
1231 
1232  odp_pktio_param_init(&pktio_param);
1233 
1234  if (in_mode == PLAIN_QUEUE)
1235  pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
1236  else if (in_mode != DIRECT_RECV) /* pktin_mode SCHED_* */
1237  pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
1238 
1239  if (gbl_args->appl.out_mode != PKTOUT_DIRECT)
1240  pktio_param.out_mode = ODP_PKTOUT_MODE_QUEUE;
1241 
1242  if (num_rx == 0)
1243  pktio_param.in_mode = ODP_PKTIN_MODE_DISABLED;
1244 
1245  if (num_tx == 0)
1246  pktio_param.out_mode = ODP_PKTOUT_MODE_DISABLED;
1247 
1248  pktio = odp_pktio_open(dev, pool, &pktio_param);
1249  if (pktio == ODP_PKTIO_INVALID) {
1250  ODPH_ERR("Pktio open failed: %s\n", dev);
1251  return -1;
1252  }
1253 
1254  if (odp_pktio_info(pktio, &info)) {
1255  ODPH_ERR("Pktio info failed: %s\n", dev);
1256  return -1;
1257  }
1258 
1259  if (odp_pktio_capability(pktio, &pktio_capa)) {
1260  ODPH_ERR("Pktio capability query failed: %s\n", dev);
1261  return -1;
1262  }
1263 
1264  odp_pktio_config_init(&config);
1265 
1266  if (gbl_args->appl.input_ts) {
1267  if (!pktio_capa.config.pktin.bit.ts_all) {
1268  ODPH_ERR("Packet input timestamping not supported: %s\n", dev);
1269  return -1;
1270  }
1271  config.pktin.bit.ts_all = 1;
1272  }
1273 
1275  if (gbl_args->appl.error_check || gbl_args->appl.chksum)
1276  config.parser.layer = ODP_PROTO_LAYER_ALL;
1277 
1278  if (gbl_args->appl.chksum) {
1279  config.pktout.bit.ipv4_chksum_ena = 1;
1280  config.pktout.bit.udp_chksum_ena = 1;
1281  config.pktout.bit.tcp_chksum_ena = 1;
1282  }
1283 
1284  if (gbl_args->appl.tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED) {
1285  if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT &&
1286  !(pktio_capa.tx_compl.mode_event && pktio_capa.tx_compl.queue_type_sched)) {
1287  ODPH_ERR("Transmit event completion not supported: %s\n", dev);
1288  return -1;
1289  }
1290 
1291  if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_POLL &&
1292  !(pktio_capa.tx_compl.mode_poll &&
1293  pktio_capa.tx_compl.max_compl_id >= gbl_args->appl.tx_compl.tot_compl_id)) {
1294  ODPH_ERR("Transmit poll completion not supported: %s\n", dev);
1295  return -1;
1296  }
1297 
1298  if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT)
1299  config.tx_compl.mode_event = 1;
1300 
1301  if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_POLL) {
1302  config.tx_compl.mode_poll = 1;
1303  config.tx_compl.max_compl_id = gbl_args->appl.tx_compl.tot_compl_id;
1304  }
1305  }
1306 
1307  /* Provide hint to pktio that packet references are not used */
1308  config.pktout.bit.no_packet_refs = 1;
1309 
1310  if (gbl_args->appl.pause_rx) {
1311  if (!pktio_capa.flow_control.pause_rx) {
1312  ODPH_ERR("Reception of pause frames not supported: %s\n", dev);
1313  return -1;
1314  }
1316  }
1317 
1318  if (gbl_args->appl.pause_tx) {
1319  if (!pktio_capa.flow_control.pause_tx) {
1320  ODPH_ERR("Transmission of pause frames not supported: %s\n", dev);
1321  return -1;
1322  }
1324  }
1325 
1326  odp_pktio_config(pktio, &config);
1327 
1328  if (gbl_args->appl.promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
1329  if (!pktio_capa.set_op.op.promisc_mode) {
1330  ODPH_ERR("Promisc mode set not supported: %s\n", dev);
1331  return -1;
1332  }
1333 
1334  /* Enable promisc mode */
1335  if (odp_pktio_promisc_mode_set(pktio, true)) {
1336  ODPH_ERR("Promisc mode enable failed: %s\n", dev);
1337  return -1;
1338  }
1339  }
1340 
1341  if (gbl_args->appl.mtu) {
1342  uint32_t maxlen_input = pktio_capa.maxlen.max_input ? gbl_args->appl.mtu : 0;
1343  uint32_t maxlen_output = pktio_capa.maxlen.max_output ? gbl_args->appl.mtu : 0;
1344 
1345  if (!pktio_capa.set_op.op.maxlen) {
1346  ODPH_ERR("Modifying interface MTU not supported: %s\n", dev);
1347  return -1;
1348  }
1349 
1350  if (maxlen_input &&
1351  (maxlen_input < pktio_capa.maxlen.min_input ||
1352  maxlen_input > pktio_capa.maxlen.max_input)) {
1353  ODPH_ERR("Unsupported MTU value %" PRIu32 " for %s "
1354  "(min %" PRIu32 ", max %" PRIu32 ")\n", maxlen_input, dev,
1355  pktio_capa.maxlen.min_input, pktio_capa.maxlen.max_input);
1356  return -1;
1357  }
1358  if (maxlen_output &&
1359  (maxlen_output < pktio_capa.maxlen.min_output ||
1360  maxlen_output > pktio_capa.maxlen.max_output)) {
1361  ODPH_ERR("Unsupported MTU value %" PRIu32 " for %s "
1362  "(min %" PRIu32 ", max %" PRIu32 ")\n", maxlen_output, dev,
1363  pktio_capa.maxlen.min_output, pktio_capa.maxlen.max_output);
1364  return -1;
1365  }
1366 
1367  if (odp_pktio_maxlen_set(pktio, maxlen_input, maxlen_output)) {
1368  ODPH_ERR("Setting MTU failed: %s\n", dev);
1369  return -1;
1370  }
1371  }
1372 
1373  odp_pktin_queue_param_init(&pktin_param);
1374  odp_pktout_queue_param_init(&pktout_param);
1375 
1376  /* By default use a queue per worker. Sched mode ignores rx side
1377  * setting. */
1378  mode_rx = ODP_PKTIO_OP_MT_UNSAFE;
1379  mode_tx = ODP_PKTIO_OP_MT_UNSAFE;
1380 
1381  if (gbl_args->appl.sched_mode) {
1382  odp_schedule_prio_t prio;
1383 
1384  if (gbl_args->appl.num_prio) {
1385  prio = gbl_args->appl.prio[idx];
1386  } else {
1387  prio = odp_schedule_default_prio();
1388  gbl_args->appl.prio[idx] = prio;
1389  }
1390 
1391  if (gbl_args->appl.in_mode == SCHED_ATOMIC)
1392  sync_mode = ODP_SCHED_SYNC_ATOMIC;
1393  else if (gbl_args->appl.in_mode == SCHED_ORDERED)
1394  sync_mode = ODP_SCHED_SYNC_ORDERED;
1395  else
1396  sync_mode = ODP_SCHED_SYNC_PARALLEL;
1397 
1398  pktin_param.queue_param.sched.prio = prio;
1399  pktin_param.queue_param.sched.sync = sync_mode;
1400  pktin_param.queue_param.sched.group = group;
1401 
1402  if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT) {
1403  odp_queue_param_init(&compl_queue);
1404  compl_queue.type = ODP_QUEUE_TYPE_SCHED;
1405  compl_queue.sched.prio = prio;
1406  compl_queue.sched.sync = ODP_SCHED_SYNC_PARALLEL;
1407  compl_queue.sched.group = group;
1408  gbl_args->pktios[idx].compl_q = odp_queue_create(NULL, &compl_queue);
1409 
1410  if (gbl_args->pktios[idx].compl_q == ODP_QUEUE_INVALID) {
1411  ODPH_ERR("Creating completion queue failed: %s\n", dev);
1412  return -1;
1413  }
1414  }
1415  }
1416 
1417  if (num_rx > (int)pktio_capa.max_input_queues) {
1418  num_rx = pktio_capa.max_input_queues;
1419  mode_rx = ODP_PKTIO_OP_MT;
1420  printf("Warning: %s: maximum number of input queues: %i\n", dev, num_rx);
1421  }
1422 
1423  if (num_rx < gbl_args->appl.num_workers)
1424  printf("Warning: %s: sharing %i input queues between %i workers\n",
1425  dev, num_rx, gbl_args->appl.num_workers);
1426 
1427  if (num_tx > (int)pktio_capa.max_output_queues) {
1428  printf("Warning: %s: sharing %i output queues between %i workers\n",
1429  dev, pktio_capa.max_output_queues, num_tx);
1430  num_tx = pktio_capa.max_output_queues;
1431  mode_tx = ODP_PKTIO_OP_MT;
1432  }
1433 
1434  pktin_param.hash_enable = (num_rx > 1 || gbl_args->appl.flow_aware) ? 1 : 0;
1435  pktin_param.hash_proto.proto.ipv4_udp = 1;
1436  pktin_param.num_queues = num_rx;
1437  pktin_param.op_mode = mode_rx;
1438 
1439  pktout_param.op_mode = mode_tx;
1440  pktout_param.num_queues = num_tx;
1441 
1442  if (gbl_args->appl.vector_mode) {
1443  if (!pktio_capa.vector.supported) {
1444  ODPH_ERR("Packet vector input not supported: %s\n", dev);
1445  return -1;
1446  }
1447  if (set_pktin_vector_params(&pktin_param, vec_pool, pktio_capa))
1448  return -1;
1449  }
1450 
1451  if (num_rx > 0 && odp_pktin_queue_config(pktio, &pktin_param)) {
1452  ODPH_ERR("Input queue config failed: %s\n", dev);
1453  return -1;
1454  }
1455 
1456  if (num_tx > 0 && odp_pktout_queue_config(pktio, &pktout_param)) {
1457  ODPH_ERR("Output queue config failed: %s\n", dev);
1458  return -1;
1459  }
1460 
1461  if (num_rx > 0) {
1462  if (gbl_args->appl.in_mode == DIRECT_RECV) {
1463  if (odp_pktin_queue(pktio, gbl_args->pktios[idx].pktin, num_rx)
1464  != num_rx) {
1465  ODPH_ERR("Pktin queue query failed: %s\n", dev);
1466  return -1;
1467  }
1468  } else {
1469  if (odp_pktin_event_queue(pktio, gbl_args->pktios[idx].rx_q, num_rx)
1470  != num_rx) {
1471  ODPH_ERR("Pktin event queue query failed: %s\n", dev);
1472  return -1;
1473  }
1474  }
1475  }
1476 
1477  if (num_tx > 0) {
1478  if (gbl_args->appl.out_mode == PKTOUT_DIRECT) {
1479  if (odp_pktout_queue(pktio, gbl_args->pktios[idx].pktout, num_tx)
1480  != num_tx) {
1481  ODPH_ERR("Pktout queue query failed: %s\n", dev);
1482  return -1;
1483  }
1484  } else {
1485  if (odp_pktout_event_queue(pktio, gbl_args->pktios[idx].tx_q, num_tx)
1486  != num_tx) {
1487  ODPH_ERR("Event queue query failed: %s\n", dev);
1488  return -1;
1489  }
1490  }
1491  }
1492 
1493  if (odp_pktio_mac_addr(pktio, gbl_args->port_eth_addr[idx].addr,
1494  ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
1495  ODPH_ERR("Reading interface Ethernet address failed: %s\n", dev);
1496  return -1;
1497  }
1498  addr = gbl_args->port_eth_addr[idx].addr;
1499 
1500  printf(" dev: %s, drv: %s, rx_queues: %i, tx_queues: %i, mac: "
1501  "%02x:%02x:%02x:%02x:%02x:%02x\n", dev, info.drv_name, num_rx, num_tx,
1502  addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
1503 
1504  if (gbl_args->appl.verbose)
1505  odp_pktio_print(pktio);
1506 
1507  gbl_args->pktios[idx].num_rx_queue = num_rx;
1508  gbl_args->pktios[idx].num_tx_queue = num_tx;
1509  gbl_args->pktios[idx].pktio = pktio;
1510 
1511  return 0;
1512 }
1513 
1514 /*
1515  * Print statistics
1516  *
1517  * num_workers Number of worker threads
1518  * thr_stats Pointers to stats storage
1519  * duration Number of seconds to loop in
1520  * timeout Number of seconds for stats calculation
1521  */
1522 static int print_speed_stats(int num_workers, stats_t **thr_stats,
1523  int duration, int timeout)
1524 {
1525  uint64_t pkts = 0;
1526  uint64_t pkts_prev = 0;
1527  uint64_t pps;
1528  uint64_t rx_drops, tx_drops, tx_c_misses, tx_c_fails, copy_fails;
1529  uint64_t maximum_pps = 0;
1530  int i;
1531  int elapsed = 0;
1532  int stats_enabled = 1;
1533  int loop_forever = (duration == 0);
1534 
1535  if (timeout <= 0) {
1536  stats_enabled = 0;
1537  timeout = 1;
1538  }
1539  /* Wait for all threads to be ready*/
1540  odp_barrier_wait(&gbl_args->init_barrier);
1541 
1542  do {
1543  pkts = 0;
1544  rx_drops = 0;
1545  tx_drops = 0;
1546  tx_c_misses = 0;
1547  tx_c_fails = 0;
1548  copy_fails = 0;
1549 
1550  sleep(timeout);
1551 
1552  for (i = 0; i < num_workers; i++) {
1553  pkts += thr_stats[i]->s.packets;
1554  rx_drops += thr_stats[i]->s.rx_drops;
1555  tx_drops += thr_stats[i]->s.tx_drops;
1556  tx_c_misses += thr_stats[i]->s.tx_c_misses;
1557  tx_c_fails += thr_stats[i]->s.tx_c_fails;
1558  copy_fails += thr_stats[i]->s.copy_fails;
1559  }
1560  if (stats_enabled) {
1561  pps = (pkts - pkts_prev) / timeout;
1562  if (pps > maximum_pps)
1563  maximum_pps = pps;
1564  printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps,
1565  maximum_pps);
1566 
1567  if (gbl_args->appl.packet_copy)
1568  printf("%" PRIu64 " copy fails, ", copy_fails);
1569 
1570  if (gbl_args->appl.tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED)
1571  printf("%" PRIu64 " tx compl misses, %" PRIu64 " tx compl fails, ",
1572  tx_c_misses, tx_c_fails);
1573 
1574  printf("%" PRIu64 " rx drops, %" PRIu64 " tx drops\n",
1575  rx_drops, tx_drops);
1576 
1577  pkts_prev = pkts;
1578  }
1579  elapsed += timeout;
1580  } while (!odp_atomic_load_u32(&gbl_args->exit_threads) && (loop_forever ||
1581  (elapsed < duration)));
1582 
1583  if (stats_enabled)
1584  printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
1585  maximum_pps);
1586 
1587  return pkts > 100 ? 0 : -1;
1588 }
1589 
1590 static void print_port_mapping(void)
1591 {
1592  int if_count;
1593  int pktio;
1594 
1595  if_count = gbl_args->appl.if_count;
1596 
1597  printf("\nPort config\n--------------------\n");
1598 
1599  for (pktio = 0; pktio < if_count; pktio++) {
1600  const char *dev = gbl_args->appl.if_names[pktio];
1601 
1602  printf("Port %i (%s)\n", pktio, dev);
1603  printf(" rx workers %i\n",
1604  gbl_args->pktios[pktio].num_rx_thr);
1605  printf(" tx workers %i\n",
1606  gbl_args->pktios[pktio].num_tx_thr);
1607  printf(" rx queues %i\n",
1608  gbl_args->pktios[pktio].num_rx_queue);
1609  printf(" tx queues %i\n",
1610  gbl_args->pktios[pktio].num_tx_queue);
1611  }
1612 
1613  printf("\n");
1614 }
1615 
1616 /*
1617  * Find the destination port for a given input port
1618  *
1619  * port Input port index
1620  */
1621 static int find_dest_port(int port)
1622 {
1623  const char *output = gbl_args->appl.output_map[port];
1624 
1625  /* Check output mappings first */
1626  if (output != NULL)
1627  for (int i = 0; i < gbl_args->appl.if_count; i++)
1628  if (strcmp(output, gbl_args->appl.if_names[i]) == 0)
1629  return i;
1630 
1631  /* Even number of ports */
1632  if (gbl_args->appl.if_count % 2 == 0)
1633  return (port % 2 == 0) ? port + 1 : port - 1;
1634 
1635  /* Odd number of ports */
1636  if (port == gbl_args->appl.if_count - 1)
1637  return 0;
1638  else
1639  return port + 1;
1640 }
1641 
1642 /*
1643  * Bind worker threads to interfaces and calculate number of queues needed
1644  *
1645  * less workers (N) than interfaces (M)
1646  * - assign each worker to process every Nth interface
1647  * - workers process inequal number of interfaces, when M is not divisible by N
1648  * - needs only single queue per interface
1649  * otherwise
1650  * - assign an interface to every Mth worker
1651  * - interfaces are processed by inequal number of workers, when N is not
1652  * divisible by M
1653  * - tries to configure a queue per worker per interface
1654  * - shares queues, if interface capability does not allows a queue per worker
1655  */
1656 static void bind_workers(void)
1657 {
1658  int if_count, num_workers;
1659  int rx_idx, tx_idx, thr, pktio, i;
1660  thread_args_t *thr_args;
1661 
1662  if_count = gbl_args->appl.if_count;
1663  num_workers = gbl_args->appl.num_workers;
1664 
1665  if (gbl_args->appl.sched_mode) {
1666  /* all threads receive and send on all pktios */
1667  for (i = 0; i < if_count; i++) {
1668  gbl_args->pktios[i].num_rx_thr = num_workers;
1669  gbl_args->pktios[i].num_tx_thr = num_workers;
1670  }
1671 
1672  for (thr = 0; thr < num_workers; thr++) {
1673  thr_args = &gbl_args->thread_args[thr];
1674  thr_args->num_pktio = if_count;
1675 
1676  /* In sched mode, pktios are not cross connected with
1677  * local pktio indexes */
1678  for (i = 0; i < if_count; i++) {
1679  thr_args->pktio[i].rx_idx = i;
1680  thr_args->pktio[i].tx_idx = i;
1681  }
1682  }
1683  } else {
1684  /* initialize port forwarding table */
1685  for (rx_idx = 0; rx_idx < if_count; rx_idx++)
1686  gbl_args->dst_port[rx_idx] = find_dest_port(rx_idx);
1687 
1688  if (if_count > num_workers) {
1689  /* Less workers than pktios. Assign single worker per
1690  * pktio. */
1691  thr = 0;
1692 
1693  for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
1694  thr_args = &gbl_args->thread_args[thr];
1695  pktio = thr_args->num_pktio;
1696  /* Cross connect rx to tx */
1697  tx_idx = gbl_args->dst_port[rx_idx];
1698  thr_args->pktio[pktio].rx_idx = rx_idx;
1699  thr_args->pktio[pktio].tx_idx = tx_idx;
1700  thr_args->num_pktio++;
1701 
1702  gbl_args->pktios[rx_idx].num_rx_thr++;
1703  gbl_args->pktios[tx_idx].num_tx_thr++;
1704 
1705  thr++;
1706  if (thr >= num_workers)
1707  thr = 0;
1708  }
1709  } else {
1710  /* More workers than pktios. Assign at least one worker
1711  * per pktio. */
1712  rx_idx = 0;
1713 
1714  for (thr = 0; thr < num_workers; thr++) {
1715  thr_args = &gbl_args->thread_args[thr];
1716  pktio = thr_args->num_pktio;
1717  /* Cross connect rx to tx */
1718  tx_idx = gbl_args->dst_port[rx_idx];
1719  thr_args->pktio[pktio].rx_idx = rx_idx;
1720  thr_args->pktio[pktio].tx_idx = tx_idx;
1721  thr_args->num_pktio++;
1722 
1723  gbl_args->pktios[rx_idx].num_rx_thr++;
1724  gbl_args->pktios[tx_idx].num_tx_thr++;
1725 
1726  rx_idx++;
1727  if (rx_idx >= if_count)
1728  rx_idx = 0;
1729  }
1730  }
1731  }
1732 }
1733 
1734 /*
1735  * Bind queues to threads and fill in missing thread arguments (handles)
1736  */
1737 static void bind_queues(void)
1738 {
1739  int num_workers;
1740  int thr, i;
1741 
1742  num_workers = gbl_args->appl.num_workers;
1743 
1744  printf("\nQueue binding (indexes)\n-----------------------\n");
1745 
1746  for (thr = 0; thr < num_workers; thr++) {
1747  int rx_idx, tx_idx;
1748  thread_args_t *thr_args = &gbl_args->thread_args[thr];
1749  int num = thr_args->num_pktio;
1750 
1751  printf("worker %i\n", thr);
1752 
1753  for (i = 0; i < num; i++) {
1754  int rx_queue, tx_queue;
1755 
1756  rx_idx = thr_args->pktio[i].rx_idx;
1757  tx_idx = thr_args->pktio[i].tx_idx;
1758  rx_queue = gbl_args->pktios[rx_idx].next_rx_queue;
1759  tx_queue = gbl_args->pktios[tx_idx].next_tx_queue;
1760 
1761  thr_args->pktio[i].rx_queue_idx = rx_queue;
1762  thr_args->pktio[i].tx_queue_idx = tx_queue;
1763  thr_args->pktio[i].pktin =
1764  gbl_args->pktios[rx_idx].pktin[rx_queue];
1765  thr_args->pktio[i].rx_queue =
1766  gbl_args->pktios[rx_idx].rx_q[rx_queue];
1767  thr_args->pktio[i].pktout =
1768  gbl_args->pktios[tx_idx].pktout[tx_queue];
1769  thr_args->pktio[i].tx_queue =
1770  gbl_args->pktios[tx_idx].tx_q[tx_queue];
1771 
1772  if (!gbl_args->appl.sched_mode)
1773  printf(" rx: pktio %i, queue %i\n",
1774  rx_idx, rx_queue);
1775 
1776  printf(" tx: pktio %i, queue %i\n",
1777  tx_idx, tx_queue);
1778 
1779  rx_queue++;
1780  tx_queue++;
1781 
1782  if (rx_queue >= gbl_args->pktios[rx_idx].num_rx_queue)
1783  rx_queue = 0;
1784  if (tx_queue >= gbl_args->pktios[tx_idx].num_tx_queue)
1785  tx_queue = 0;
1786 
1787  gbl_args->pktios[rx_idx].next_rx_queue = rx_queue;
1788  gbl_args->pktios[tx_idx].next_tx_queue = tx_queue;
1789  }
1790  }
1791 
1792  printf("\n");
1793 }
1794 
1795 static void init_state(const appl_args_t *args, state_t *state, int thr_idx)
1796 {
1797  const uint32_t cnt = args->tx_compl.thr_compl_id + 1;
1798 
1799  state->tx_compl.opt.mode = args->tx_compl.mode;
1800  state->tx_compl.init = thr_idx * cnt;
1801  state->tx_compl.max = state->tx_compl.init + cnt - 1;
1802  state->tx_compl.free_head = state->tx_compl.init;
1803  state->tx_compl.poll_head = state->tx_compl.init;
1804  state->tx_compl.num_act = 0;
1805  state->tx_compl.max_act = state->tx_compl.max - state->tx_compl.init + 1;
1806  state->tx_compl.interval = args->tx_compl.nth;
1807  state->tx_compl.next_req = state->tx_compl.interval;
1808 }
1809 
1810 static void init_port_lookup_tbl(void)
1811 {
1812  int rx_idx, if_count;
1813 
1814  if_count = gbl_args->appl.if_count;
1815 
1816  for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
1817  odp_pktio_t pktio = gbl_args->pktios[rx_idx].pktio;
1818  int pktio_idx = odp_pktio_index(pktio);
1819  int dst_port = find_dest_port(rx_idx);
1820 
1821  if (pktio_idx < 0) {
1822  ODPH_ERR("Reading pktio (%s) index failed: %i\n",
1823  gbl_args->appl.if_names[rx_idx], pktio_idx);
1824 
1825  exit(EXIT_FAILURE);
1826  }
1827 
1828  gbl_args->dst_port_from_idx[pktio_idx] = dst_port;
1829  }
1830 }
1831 
1832 /*
1833  * Print usage information
1834  */
1835 static void usage(char *progname)
1836 {
1837  printf("\n"
1838  "OpenDataPlane L2 forwarding application.\n"
1839  "\n"
1840  "Usage: %s [options]\n"
1841  "\n"
1842  " E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
1843  " In the above example,\n"
1844  " eth0 will send pkts to eth1 and vice versa\n"
1845  " eth2 will send pkts to eth3 and vice versa\n"
1846  "\n"
1847  "Mandatory OPTIONS:\n"
1848  " -i, --interface <name> Eth interfaces (comma-separated, no spaces)\n"
1849  " Interface count min 1, max %i\n"
1850  "\n"
1851  "Optional OPTIONS:\n"
1852  " -m, --mode <arg> Packet input mode\n"
1853  " 0: Direct mode: PKTIN_MODE_DIRECT (default)\n"
1854  " 1: Scheduler mode with parallel queues:\n"
1855  " PKTIN_MODE_SCHED + SCHED_SYNC_PARALLEL\n"
1856  " 2: Scheduler mode with atomic queues:\n"
1857  " PKTIN_MODE_SCHED + SCHED_SYNC_ATOMIC\n"
1858  " 3: Scheduler mode with ordered queues:\n"
1859  " PKTIN_MODE_SCHED + SCHED_SYNC_ORDERED\n"
1860  " 4: Plain queue mode: PKTIN_MODE_QUEUE\n"
1861  " -o, --out_mode <arg> Packet output mode\n"
1862  " 0: Direct mode: PKTOUT_MODE_DIRECT (default)\n"
1863  " 1: Queue mode: PKTOUT_MODE_QUEUE\n"
1864  " -O, --output_map <list> List of destination ports for passed interfaces\n"
1865  " (comma-separated, no spaces). Ordering follows\n"
1866  " the '--interface' option, e.g. passing\n"
1867  " '-i eth0,eth1' and '-O eth0,eth1' would result\n"
1868  " in eth0 and eth1 looping packets back.\n"
1869  " -c, --count <num> CPU count, 0=all available, default=1\n"
1870  " -t, --time <sec> Time in seconds to run.\n"
1871  " -a, --accuracy <sec> Time in seconds get print statistics\n"
1872  " (default is 1 second).\n"
1873  " -d, --dst_change <arg> 0: Don't change packets' dst eth addresses\n"
1874  " 1: Change packets' dst eth addresses (default)\n"
1875  " -s, --src_change <arg> 0: Don't change packets' src eth addresses\n"
1876  " 1: Change packets' src eth addresses (default)\n"
1877  " -r, --dst_addr <addr> Destination addresses (comma-separated, no\n"
1878  " spaces) Requires also the -d flag to be set\n"
1879  " -e, --error_check <arg> 0: Don't check packet errors (default)\n"
1880  " 1: Check packet errors\n"
1881  " -k, --chksum <arg> 0: Don't use checksum offload (default)\n"
1882  " 1: Use checksum offload\n",
1883  NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS);
1884 
1885  printf(" -g, --groups <num> Number of new groups to create (1 ... num).\n"
1886  " Interfaces are placed into the groups in round\n"
1887  " robin.\n"
1888  " 0: Use SCHED_GROUP_ALL (default)\n"
1889  " -1: Use SCHED_GROUP_WORKER\n"
1890  " -G, --group_mode <arg> Select how threads join new groups\n"
1891  " (when -g > 0)\n"
1892  " 0: All threads join all created groups\n"
1893  " (default)\n"
1894  " 1: All threads join first N created groups.\n"
1895  " N is number of interfaces (== active\n"
1896  " groups).\n"
1897  " 2: Each thread joins a part of the first N\n"
1898  " groups (in round robin).\n"
1899  " -I, --prio <prio list> Schedule priority of packet input queues.\n"
1900  " Comma separated list of priorities (no spaces).\n"
1901  " A value per interface. All queues of an\n"
1902  " interface have the same priority. Values must\n"
1903  " be between odp_schedule_min_prio and\n"
1904  " odp_schedule_max_prio.\n"
1905  " odp_schedule_default_prio is used by default.\n"
1906  " -b, --burst_rx <num> 0: Use max burst size (default)\n"
1907  " num: Max number of packets per receive call\n"
1908  " -q, --rx_queues <num> Number of RX queues per interface in scheduler\n"
1909  " mode\n"
1910  " 0: RX queue per worker CPU (default)\n"
1911  " -p, --packet_copy 0: Don't copy packet (default)\n"
1912  " 1: Create and send copy of the received packet.\n"
1913  " Free the original packet.\n"
1914  " -R, --data_rd <num> Number of packet data words (uint64_t) to read\n"
1915  " from every received packet. Number of words is\n"
1916  " rounded down to fit into the first segment of a\n"
1917  " packet. Default is 0.\n"
1918  " -y, --pool_per_if Create a packet (and packet vector) pool per\n"
1919  " interface.\n"
1920  " 0: Share a single pool between all interfaces\n"
1921  " (default)\n"
1922  " 1: Create a pool per interface\n"
1923  " -n, --num_pkt <num> Number of packets per pool. Default is 16k or\n"
1924  " the maximum capability. Use 0 for the default.\n"
1925  " -u, --vector_mode Enable vector mode.\n"
1926  " Supported only with scheduler packet input\n"
1927  " modes (1-3).\n"
1928  " -w, --num_vec <num> Number of vectors per pool.\n"
1929  " Default is num_pkts divided by vec_size.\n"
1930  " -x, --vec_size <num> Vector size (default %i).\n"
1931  " -z, --vec_tmo_ns <ns> Vector timeout in ns (default %llu ns).\n"
1932  " -M, --mtu <len> Interface MTU in bytes.\n"
1933  " -P, --promisc_mode Enable promiscuous mode.\n"
1934  " -l, --packet_len <len> Maximum length of packets supported\n"
1935  " (default %d).\n"
1936  " -L, --seg_len <len> Packet pool segment length\n"
1937  " (default equal to packet length).\n"
1938  " -F, --prefetch <num> Prefetch packet data in 64 byte multiples\n"
1939  " (default 1).\n"
1940  " -f, --flow_aware Enable flow aware scheduling.\n"
1941  " -T, --input_ts Enable packet input timestamping.\n",
1942  DEFAULT_VEC_SIZE, DEFAULT_VEC_TMO, POOL_PKT_LEN);
1943 
1944  printf(" -C, --tx_compl <mode,n,max_id> Enable transmit completion with a specified\n"
1945  " completion mode for nth packet, with maximum\n"
1946  " completion ID per worker thread in case of poll\n"
1947  " completion (comma-separated, no spaces).\n"
1948  " 0: Event completion mode\n"
1949  " 1: Poll completion mode\n"
1950  " -X, --flow_control <mode> Ethernet flow control mode.\n"
1951  " 0: Flow control disabled (default)\n"
1952  " 1: Enable reception of pause frames\n"
1953  " 2: Enable transmission of pause frames\n"
1954  " 3: Enable reception and transmission of pause\n"
1955  " frames\n"
1956  " -v, --verbose Verbose output.\n"
1957  " -V, --verbose_pkt Print debug information on every received\n"
1958  " packet.\n"
1959  " -h, --help Display help and exit.\n\n"
1960  "\n");
1961 }
1962 
1963 /*
1964  * Parse and store the command line arguments
1965  *
1966  * argc argument count
1967  * argv[] argument vector
1968  * appl_args Store application arguments here
1969  */
1970 static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
1971 {
1972  int opt;
1973  char *token;
1974  char *tmp_str, *tmp;
1975  size_t str_len, len;
1976  int i;
1977  static const struct option longopts[] = {
1978  {"count", required_argument, NULL, 'c'},
1979  {"time", required_argument, NULL, 't'},
1980  {"accuracy", required_argument, NULL, 'a'},
1981  {"interface", required_argument, NULL, 'i'},
1982  {"mode", required_argument, NULL, 'm'},
1983  {"out_mode", required_argument, NULL, 'o'},
1984  {"output_map", required_argument, NULL, 'O'},
1985  {"dst_addr", required_argument, NULL, 'r'},
1986  {"dst_change", required_argument, NULL, 'd'},
1987  {"src_change", required_argument, NULL, 's'},
1988  {"error_check", required_argument, NULL, 'e'},
1989  {"chksum", required_argument, NULL, 'k'},
1990  {"groups", required_argument, NULL, 'g'},
1991  {"group_mode", required_argument, NULL, 'G'},
1992  {"prio", required_argument, NULL, 'I'},
1993  {"burst_rx", required_argument, NULL, 'b'},
1994  {"rx_queues", required_argument, NULL, 'q'},
1995  {"packet_copy", required_argument, NULL, 'p'},
1996  {"data_rd", required_argument, NULL, 'R'},
1997  {"pool_per_if", required_argument, NULL, 'y'},
1998  {"num_pkt", required_argument, NULL, 'n'},
1999  {"num_vec", required_argument, NULL, 'w'},
2000  {"vec_size", required_argument, NULL, 'x'},
2001  {"vec_tmo_ns", required_argument, NULL, 'z'},
2002  {"vector_mode", no_argument, NULL, 'u'},
2003  {"mtu", required_argument, NULL, 'M'},
2004  {"promisc_mode", no_argument, NULL, 'P'},
2005  {"packet_len", required_argument, NULL, 'l'},
2006  {"seg_len", required_argument, NULL, 'L'},
2007  {"prefetch", required_argument, NULL, 'F'},
2008  {"flow_aware", no_argument, NULL, 'f'},
2009  {"input_ts", no_argument, NULL, 'T'},
2010  {"tx_compl", required_argument, NULL, 'C'},
2011  {"flow_control", required_argument, NULL, 'X'},
2012  {"verbose", no_argument, NULL, 'v'},
2013  {"verbose_pkt", no_argument, NULL, 'V'},
2014  {"help", no_argument, NULL, 'h'},
2015  {NULL, 0, NULL, 0}
2016  };
2017 
2018  static const char *shortopts = "+c:t:a:i:m:o:O:r:d:s:e:k:g:G:I:"
2019  "b:q:p:R:y:n:l:L:w:x:X:z:M:F:uPfTC:vVh";
2020 
2021  appl_args->time = 0; /* loop forever if time to run is 0 */
2022  appl_args->accuracy = 1; /* get and print pps stats second */
2023  appl_args->cpu_count = 1; /* use one worker by default */
2024  appl_args->dst_change = 1; /* change eth dst address by default */
2025  appl_args->src_change = 1; /* change eth src address by default */
2026  appl_args->num_groups = 0; /* use default group */
2027  appl_args->group_mode = 0;
2028  appl_args->error_check = 0; /* don't check packet errors by default */
2029  appl_args->packet_copy = 0;
2030  appl_args->burst_rx = 0;
2031  appl_args->rx_queues = 0;
2032  appl_args->verbose = 0;
2033  appl_args->verbose_pkt = 0;
2034  appl_args->chksum = 0; /* don't use checksum offload by default */
2035  appl_args->pool_per_if = 0;
2036  appl_args->num_pkt = 0;
2037  appl_args->packet_len = POOL_PKT_LEN;
2038  appl_args->seg_len = UINT32_MAX;
2039  appl_args->mtu = 0;
2040  appl_args->promisc_mode = 0;
2041  appl_args->vector_mode = 0;
2042  appl_args->num_vec = 0;
2043  appl_args->vec_size = 0;
2044  appl_args->vec_tmo_ns = 0;
2045  appl_args->flow_aware = 0;
2046  appl_args->input_ts = 0;
2047  appl_args->num_prio = 0;
2048  appl_args->prefetch = 1;
2049  appl_args->data_rd = 0;
2050  appl_args->flow_control = 0;
2051 
2052  while (1) {
2053  opt = getopt_long(argc, argv, shortopts, longopts, NULL);
2054 
2055  if (opt == -1)
2056  break; /* No more options */
2057 
2058  switch (opt) {
2059  case 'c':
2060  appl_args->cpu_count = atoi(optarg);
2061  break;
2062  case 't':
2063  appl_args->time = atoi(optarg);
2064  break;
2065  case 'a':
2066  appl_args->accuracy = atoi(optarg);
2067  break;
2068  case 'r':
2069  len = strlen(optarg);
2070  if (len == 0) {
2071  ODPH_ERR("Bad dest address string\n");
2072  exit(EXIT_FAILURE);
2073  }
2074 
2075  str_len = len + 1;
2076 
2077  tmp_str = malloc(str_len);
2078  if (tmp_str == NULL) {
2079  ODPH_ERR("Dest address malloc() failed\n");
2080  exit(EXIT_FAILURE);
2081  }
2082 
2083  /* store the mac addresses names */
2084  memcpy(tmp_str, optarg, str_len);
2085  for (token = strtok(tmp_str, ","), i = 0;
2086  token != NULL; token = strtok(NULL, ","), i++) {
2087  if (i >= MAX_PKTIOS) {
2088  ODPH_ERR("Too many MAC addresses\n");
2089  exit(EXIT_FAILURE);
2090  }
2091  if (odph_eth_addr_parse(&appl_args->addrs[i], token) != 0) {
2092  ODPH_ERR("Invalid MAC address\n");
2093  exit(EXIT_FAILURE);
2094  }
2095  }
2096  appl_args->addr_count = i;
2097  if (appl_args->addr_count < 1) {
2098  ODPH_ERR("Bad dest address count\n");
2099  exit(EXIT_FAILURE);
2100  }
2101  free(tmp_str);
2102  break;
2103  case 'i':
2104  len = strlen(optarg);
2105  if (len == 0) {
2106  ODPH_ERR("Bad pktio interface string\n");
2107  exit(EXIT_FAILURE);
2108  }
2109 
2110  str_len = len + 1;
2111 
2112  appl_args->if_str = malloc(str_len);
2113  if (appl_args->if_str == NULL) {
2114  ODPH_ERR("Pktio interface malloc() failed\n");
2115  exit(EXIT_FAILURE);
2116  }
2117 
2118  /* count the number of tokens separated by ',' */
2119  memcpy(appl_args->if_str, optarg, str_len);
2120  for (token = strtok(appl_args->if_str, ","), i = 0;
2121  token != NULL;
2122  token = strtok(NULL, ","), i++)
2123  ;
2124 
2125  appl_args->if_count = i;
2126 
2127  if (appl_args->if_count < 1 || appl_args->if_count > MAX_PKTIOS) {
2128  ODPH_ERR("Bad pktio interface count: %i\n", appl_args->if_count);
2129  exit(EXIT_FAILURE);
2130  }
2131 
2132  /* allocate storage for the if names */
2133  appl_args->if_names = calloc(appl_args->if_count, sizeof(char *));
2134 
2135  /* store the if names (reset names string) */
2136  memcpy(appl_args->if_str, optarg, str_len);
2137  for (token = strtok(appl_args->if_str, ","), i = 0;
2138  token != NULL; token = strtok(NULL, ","), i++) {
2139  appl_args->if_names[i] = token;
2140  }
2141  break;
2142  case 'm':
2143  i = atoi(optarg);
2144  if (i == 1)
2145  appl_args->in_mode = SCHED_PARALLEL;
2146  else if (i == 2)
2147  appl_args->in_mode = SCHED_ATOMIC;
2148  else if (i == 3)
2149  appl_args->in_mode = SCHED_ORDERED;
2150  else if (i == 4)
2151  appl_args->in_mode = PLAIN_QUEUE;
2152  else
2153  appl_args->in_mode = DIRECT_RECV;
2154  break;
2155  case 'o':
2156  i = atoi(optarg);
2157  if (i != 0)
2158  appl_args->out_mode = PKTOUT_QUEUE;
2159  break;
2160  case 'O':
2161  if (strlen(optarg) == 0) {
2162  ODPH_ERR("Bad output map string\n");
2163  exit(EXIT_FAILURE);
2164  }
2165 
2166  tmp_str = strdup(optarg);
2167 
2168  if (tmp_str == NULL) {
2169  ODPH_ERR("Output map string duplication failed\n");
2170  exit(EXIT_FAILURE);
2171  }
2172 
2173  token = strtok(tmp_str, ",");
2174 
2175  while (token) {
2176  if (appl_args->num_om >= MAX_PKTIOS) {
2177  ODPH_ERR("Bad output map element count\n");
2178  exit(EXIT_FAILURE);
2179  }
2180 
2181  appl_args->output_map[appl_args->num_om] = strdup(token);
2182 
2183  if (appl_args->output_map[appl_args->num_om] == NULL) {
2184  ODPH_ERR("Output map element duplication failed\n");
2185  exit(EXIT_FAILURE);
2186  }
2187 
2188  appl_args->num_om++;
2189  token = strtok(NULL, ",");
2190  }
2191 
2192  free(tmp_str);
2193  break;
2194  case 'd':
2195  appl_args->dst_change = atoi(optarg);
2196  break;
2197  case 's':
2198  appl_args->src_change = atoi(optarg);
2199  break;
2200  case 'e':
2201  appl_args->error_check = atoi(optarg);
2202  break;
2203  case 'k':
2204  appl_args->chksum = atoi(optarg);
2205  break;
2206  case 'g':
2207  appl_args->num_groups = atoi(optarg);
2208  break;
2209  case 'G':
2210  appl_args->group_mode = atoi(optarg);
2211  break;
2212  case 'I':
2213  len = strlen(optarg);
2214  if (len == 0) {
2215  ODPH_ERR("Bad priority list\n");
2216  exit(EXIT_FAILURE);
2217  }
2218 
2219  str_len = len + 1;
2220 
2221  tmp_str = malloc(str_len);
2222  if (tmp_str == NULL) {
2223  ODPH_ERR("Priority list malloc() failed\n");
2224  exit(EXIT_FAILURE);
2225  }
2226 
2227  memcpy(tmp_str, optarg, str_len);
2228  token = strtok(tmp_str, ",");
2229 
2230  for (i = 0; token != NULL; token = strtok(NULL, ","), i++) {
2231  if (i >= MAX_PKTIOS) {
2232  ODPH_ERR("Too many priorities\n");
2233  exit(EXIT_FAILURE);
2234  }
2235 
2236  appl_args->prio[i] = atoi(token);
2237  appl_args->num_prio++;
2238  }
2239 
2240  if (appl_args->num_prio == 0) {
2241  ODPH_ERR("Bad priority list\n");
2242  exit(EXIT_FAILURE);
2243  }
2244 
2245  free(tmp_str);
2246  break;
2247  case 'b':
2248  appl_args->burst_rx = atoi(optarg);
2249  break;
2250  case 'q':
2251  appl_args->rx_queues = atoi(optarg);
2252  break;
2253  case 'p':
2254  appl_args->packet_copy = atoi(optarg);
2255  break;
2256  case 'R':
2257  appl_args->data_rd = atoi(optarg);
2258  break;
2259  case 'y':
2260  appl_args->pool_per_if = atoi(optarg);
2261  break;
2262  case 'n':
2263  appl_args->num_pkt = atoi(optarg);
2264  break;
2265  case 'l':
2266  appl_args->packet_len = atoi(optarg);
2267  break;
2268  case 'L':
2269  appl_args->seg_len = atoi(optarg);
2270  break;
2271  case 'M':
2272  appl_args->mtu = atoi(optarg);
2273  break;
2274  case 'P':
2275  appl_args->promisc_mode = 1;
2276  break;
2277  case 'u':
2278  appl_args->vector_mode = 1;
2279  break;
2280  case 'w':
2281  appl_args->num_vec = atoi(optarg);
2282  break;
2283  case 'x':
2284  appl_args->vec_size = atoi(optarg);
2285  break;
2286  case 'X':
2287  appl_args->flow_control = atoi(optarg);
2288  if (appl_args->flow_control == 1 || appl_args->flow_control == 3)
2289  appl_args->pause_rx = true;
2290  if (appl_args->flow_control == 2 || appl_args->flow_control == 3)
2291  appl_args->pause_tx = true;
2292  break;
2293  case 'z':
2294  appl_args->vec_tmo_ns = atoi(optarg);
2295  break;
2296  case 'F':
2297  appl_args->prefetch = atoi(optarg);
2298  break;
2299  case 'f':
2300  appl_args->flow_aware = 1;
2301  break;
2302  case 'T':
2303  appl_args->input_ts = 1;
2304  break;
2305  case 'C':
2306  if (strlen(optarg) == 0) {
2307  ODPH_ERR("Bad transmit completion parameter string\n");
2308  exit(EXIT_FAILURE);
2309  }
2310 
2311  tmp_str = strdup(optarg);
2312 
2313  if (tmp_str == NULL) {
2314  ODPH_ERR("Transmit completion parameter string duplication"
2315  " failed\n");
2316  exit(EXIT_FAILURE);
2317  }
2318 
2319  tmp = strtok(tmp_str, ",");
2320 
2321  if (tmp == NULL) {
2322  ODPH_ERR("Invalid transmit completion parameter format\n");
2323  exit(EXIT_FAILURE);
2324  }
2325 
2326  i = atoi(tmp);
2327 
2328  if (i == 0)
2329  appl_args->tx_compl.mode = ODP_PACKET_TX_COMPL_EVENT;
2330  else if (i == 1)
2331  appl_args->tx_compl.mode = ODP_PACKET_TX_COMPL_POLL;
2332 
2333  tmp = strtok(NULL, ",");
2334 
2335  if (tmp == NULL) {
2336  ODPH_ERR("Invalid transmit completion parameter format\n");
2337  exit(EXIT_FAILURE);
2338  }
2339 
2340  appl_args->tx_compl.nth = atoi(tmp);
2341 
2342  if (appl_args->tx_compl.mode == ODP_PACKET_TX_COMPL_POLL) {
2343  tmp = strtok(NULL, ",");
2344 
2345  if (tmp == NULL) {
2346  ODPH_ERR("Invalid transmit completion parameter format\n");
2347  exit(EXIT_FAILURE);
2348  }
2349 
2350  appl_args->tx_compl.thr_compl_id = atoi(tmp);
2351  }
2352 
2353  free(tmp_str);
2354  break;
2355  case 'v':
2356  appl_args->verbose = 1;
2357  break;
2358  case 'V':
2359  appl_args->verbose_pkt = 1;
2360  break;
2361  case 'h':
2362  usage(argv[0]);
2363  exit(EXIT_SUCCESS);
2364  break;
2365  default:
2366  break;
2367  }
2368  }
2369 
2370  if (appl_args->if_count == 0) {
2371  ODPH_ERR("No pktio interfaces\n");
2372  exit(EXIT_FAILURE);
2373  }
2374 
2375  if (appl_args->num_om && appl_args->num_om != appl_args->if_count) {
2376  ODPH_ERR("Different number of output mappings and pktio interfaces\n");
2377  exit(EXIT_FAILURE);
2378  }
2379 
2380  if (appl_args->num_prio && appl_args->num_prio != appl_args->if_count) {
2381  ODPH_ERR("Different number of priorities and pktio interfaces\n");
2382  exit(EXIT_FAILURE);
2383  }
2384 
2385  if (appl_args->addr_count != 0 && appl_args->addr_count != appl_args->if_count) {
2386  ODPH_ERR("Number of dest addresses differs from number of interfaces\n");
2387  exit(EXIT_FAILURE);
2388  }
2389 
2390  if (appl_args->burst_rx > MAX_PKT_BURST) {
2391  ODPH_ERR("Burst size (%i) too large. Maximum is %i.\n",
2392  appl_args->burst_rx, MAX_PKT_BURST);
2393  exit(EXIT_FAILURE);
2394  }
2395 
2396  if (appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED &&
2397  appl_args->tx_compl.nth == 0) {
2398  ODPH_ERR("Invalid packet interval for transmit completion: %u\n",
2399  appl_args->tx_compl.nth);
2400  exit(EXIT_FAILURE);
2401  }
2402 
2403  if (appl_args->tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT &&
2404  (appl_args->in_mode == PLAIN_QUEUE || appl_args->in_mode == DIRECT_RECV)) {
2405  ODPH_ERR("Transmit event completion mode not supported with plain queue or direct "
2406  "input modes\n");
2407  exit(EXIT_FAILURE);
2408  }
2409 
2410  appl_args->tx_compl.tot_compl_id = (appl_args->tx_compl.thr_compl_id + 1) *
2411  appl_args->cpu_count - 1;
2412 
2413  if (appl_args->burst_rx == 0)
2414  appl_args->burst_rx = MAX_PKT_BURST;
2415 
2416  appl_args->extra_feat = 0;
2417  if (appl_args->error_check || appl_args->chksum ||
2418  appl_args->packet_copy || appl_args->data_rd || appl_args->verbose_pkt)
2419  appl_args->extra_feat = 1;
2420 
2421  appl_args->has_state = 0;
2422  if (appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED)
2423  appl_args->has_state = 1;
2424 
2425  optind = 1; /* reset 'extern optind' from the getopt lib */
2426 }
2427 
2428 static void print_options(void)
2429 {
2430  int i;
2431  appl_args_t *appl_args = &gbl_args->appl;
2432 
2433  printf("\n"
2434  "odp_l2fwd options\n"
2435  "-----------------\n"
2436  "IF-count: %i\n"
2437  "Using IFs: ", appl_args->if_count);
2438 
2439  for (i = 0; i < appl_args->if_count; ++i)
2440  printf(" %s", appl_args->if_names[i]);
2441  printf("\n"
2442  "Mode: ");
2443  if (appl_args->in_mode == DIRECT_RECV)
2444  printf("PKTIN_DIRECT, ");
2445  else if (appl_args->in_mode == PLAIN_QUEUE)
2446  printf("PKTIN_QUEUE, ");
2447  else if (appl_args->in_mode == SCHED_PARALLEL)
2448  printf("PKTIN_SCHED_PARALLEL, ");
2449  else if (appl_args->in_mode == SCHED_ATOMIC)
2450  printf("PKTIN_SCHED_ATOMIC, ");
2451  else if (appl_args->in_mode == SCHED_ORDERED)
2452  printf("PKTIN_SCHED_ORDERED, ");
2453 
2454  if (appl_args->out_mode)
2455  printf("PKTOUT_QUEUE\n");
2456  else
2457  printf("PKTOUT_DIRECT\n");
2458 
2459  if (appl_args->num_om > 0) {
2460  printf("Output mappings: ");
2461 
2462  for (i = 0; i < appl_args->num_om; ++i)
2463  printf(" %s", appl_args->output_map[i]);
2464 
2465  printf("\n");
2466  }
2467 
2468  printf("MTU: ");
2469  if (appl_args->mtu)
2470  printf("%i bytes\n", appl_args->mtu);
2471  else
2472  printf("interface default\n");
2473  printf("Promisc mode: %s\n", appl_args->promisc_mode ?
2474  "enabled" : "disabled");
2475  if (appl_args->flow_control)
2476  printf("Flow control: %s%s\n",
2477  appl_args->pause_rx ? "rx " : "",
2478  appl_args->pause_tx ? "tx" : "");
2479  printf("Flow aware: %s\n", appl_args->flow_aware ?
2480  "yes" : "no");
2481  printf("Input TS: %s\n", appl_args->input_ts ? "yes" : "no");
2482  printf("Burst size: %i\n", appl_args->burst_rx);
2483  printf("RX queues per IF: %i\n", appl_args->rx_queues);
2484  printf("Number of pools: %i\n", appl_args->pool_per_if ?
2485  appl_args->if_count : 1);
2486 
2487  if (appl_args->extra_feat || appl_args->has_state) {
2488  printf("Extra features: %s%s%s%s%s%s\n",
2489  appl_args->error_check ? "error_check " : "",
2490  appl_args->chksum ? "chksum " : "",
2491  appl_args->packet_copy ? "packet_copy " : "",
2492  appl_args->data_rd ? "data_rd" : "",
2493  appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED ? "tx_compl" : "",
2494  appl_args->verbose_pkt ? "verbose_pkt" : "");
2495  }
2496 
2497  printf("Num worker threads: %i\n", appl_args->num_workers);
2498  printf("CPU mask: %s\n", gbl_args->cpumaskstr);
2499 
2500  if (appl_args->num_groups > 0)
2501  printf("num groups: %i\n", appl_args->num_groups);
2502  else if (appl_args->num_groups == 0)
2503  printf("group: ODP_SCHED_GROUP_ALL\n");
2504  else
2505  printf("group: ODP_SCHED_GROUP_WORKER\n");
2506 
2507  printf("Packets per pool: %u\n", appl_args->num_pkt);
2508  printf("Packet length: %u\n", appl_args->packet_len);
2509  printf("Segment length: %u\n", appl_args->seg_len == UINT32_MAX ? 0 :
2510  appl_args->seg_len);
2511  printf("Read data: %u bytes\n", appl_args->data_rd * 8);
2512  printf("Prefetch data %u bytes\n", appl_args->prefetch * 64);
2513  printf("Vectors per pool: %u\n", appl_args->num_vec);
2514  printf("Vector size: %u\n", appl_args->vec_size);
2515  printf("Priority per IF: ");
2516 
2517  for (i = 0; i < appl_args->if_count; i++)
2518  printf(" %i", appl_args->prio[i]);
2519 
2520  printf("\n\n");
2521 }
2522 
2523 static void gbl_args_init(args_t *args)
2524 {
2525  int pktio, queue;
2526 
2527  memset(args, 0, sizeof(args_t));
2528  odp_atomic_init_u32(&args->exit_threads, 0);
2529 
2530  for (pktio = 0; pktio < MAX_PKTIOS; pktio++) {
2531  args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
2532 
2533  for (queue = 0; queue < MAX_QUEUES; queue++)
2534  args->pktios[pktio].rx_q[queue] = ODP_QUEUE_INVALID;
2535 
2536  args->pktios[pktio].compl_q = ODP_QUEUE_INVALID;
2537  }
2538 
2539  args->appl.tx_compl.mode = ODP_PACKET_TX_COMPL_DISABLED;
2540 }
2541 
2542 static void create_groups(int num, odp_schedule_group_t *group)
2543 {
2544  int i;
2545  odp_thrmask_t zero;
2546 
2547  odp_thrmask_zero(&zero);
2548 
2549  /* Create groups */
2550  for (i = 0; i < num; i++) {
2551  group[i] = odp_schedule_group_create(NULL, &zero);
2552 
2553  if (group[i] == ODP_SCHED_GROUP_INVALID) {
2554  ODPH_ERR("Group create failed\n");
2555  exit(EXIT_FAILURE);
2556  }
2557  }
2558 }
2559 
2560 static int set_vector_pool_params(odp_pool_param_t *params, const odp_pool_capability_t *pool_capa)
2561 {
2562  uint32_t num_vec, vec_size;
2563 
2564  if (gbl_args->appl.vec_size == 0)
2565  vec_size = DEFAULT_VEC_SIZE;
2566  else
2567  vec_size = gbl_args->appl.vec_size;
2568 
2569  ODPH_ASSERT(pool_capa->vector.max_size > 0);
2570  if (vec_size > pool_capa->vector.max_size) {
2571  if (gbl_args->appl.vec_size == 0) {
2572  vec_size = pool_capa->vector.max_size;
2573  printf("\nWarning: Vector size reduced to %u\n\n", vec_size);
2574  } else {
2575  ODPH_ERR("Vector size too big %u. Maximum is %u.\n",
2576  vec_size, pool_capa->vector.max_size);
2577  return -1;
2578  }
2579  }
2580 
2581  if (gbl_args->appl.num_vec == 0) {
2582  uint32_t num_pkt = gbl_args->appl.num_pkt ?
2583  gbl_args->appl.num_pkt : DEFAULT_NUM_PKT;
2584 
2585  num_vec = (num_pkt + vec_size - 1) / vec_size;
2586  } else {
2587  num_vec = gbl_args->appl.num_vec;
2588  }
2589 
2590  if (pool_capa->vector.max_num && num_vec > pool_capa->vector.max_num) {
2591  if (gbl_args->appl.num_vec == 0) {
2592  num_vec = pool_capa->vector.max_num;
2593  printf("\nWarning: number of vectors reduced to %u\n\n", num_vec);
2594  } else {
2595  ODPH_ERR("Too many vectors (%u) per pool. Maximum is %u.\n",
2596  num_vec, pool_capa->vector.max_num);
2597  return -1;
2598  }
2599  }
2600 
2601  params->vector.num = num_vec;
2602  params->vector.max_size = vec_size;
2603  params->type = ODP_POOL_VECTOR;
2604 
2605  return 0;
2606 }
2607 
2608 /*
2609  * L2 forwarding main function
2610  */
2611 int main(int argc, char *argv[])
2612 {
2613  odph_helper_options_t helper_options;
2614  odph_thread_param_t thr_param[MAX_WORKERS];
2615  odph_thread_common_param_t thr_common;
2616  int i;
2617  int num_workers, num_thr;
2618  odp_shm_t shm;
2619  odp_cpumask_t cpumask;
2620  odph_ethaddr_t new_addr;
2621  odp_pool_param_t params;
2622  int ret;
2623  stats_t *stats[MAX_WORKERS];
2624  int if_count, num_pools, num_vec_pools;
2625  int (*thr_run_func)(void *);
2626  odp_instance_t instance;
2627  int num_groups, max_groups;
2628  odp_schedule_group_t group[MAX_GROUPS];
2629  odp_pool_t pool_tbl[MAX_PKTIOS], vec_pool_tbl[MAX_PKTIOS];
2630  odp_pool_t pool, vec_pool;
2631  odp_init_t init;
2632  odp_pool_capability_t pool_capa;
2633  odp_schedule_config_t sched_config;
2634  odp_schedule_capability_t sched_capa;
2635  uint32_t pkt_len, num_pkt, seg_len;
2636 
2637  /* Let helper collect its own arguments (e.g. --odph_proc) */
2638  argc = odph_parse_options(argc, argv);
2639  if (odph_options(&helper_options)) {
2640  ODPH_ERR("Reading ODP helper options failed.\n");
2641  exit(EXIT_FAILURE);
2642  }
2643 
2644  odp_init_param_init(&init);
2645 
2646  /* List features not to be used (may optimize performance) */
2647  init.not_used.feat.cls = 1;
2648  init.not_used.feat.compress = 1;
2649  init.not_used.feat.crypto = 1;
2650  init.not_used.feat.ipsec = 1;
2651  init.not_used.feat.timer = 1;
2652  init.not_used.feat.tm = 1;
2653 
2654  init.mem_model = helper_options.mem_model;
2655 
2656  if (setup_sig_handler()) {
2657  ODPH_ERR("Signal handler setup failed\n");
2658  exit(EXIT_FAILURE);
2659  }
2660 
2661  /* Init ODP before calling anything else */
2662  if (odp_init_global(&instance, &init, NULL)) {
2663  ODPH_ERR("ODP global init failed.\n");
2664  exit(EXIT_FAILURE);
2665  }
2666 
2667  /* Init this thread */
2668  if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
2669  ODPH_ERR("ODP local init failed.\n");
2670  exit(EXIT_FAILURE);
2671  }
2672 
2673  /* Reserve memory for args from shared mem */
2674  shm = odp_shm_reserve("shm_args", sizeof(args_t),
2675  ODP_CACHE_LINE_SIZE, 0);
2676 
2677  if (shm == ODP_SHM_INVALID) {
2678  ODPH_ERR("Shared mem reserve failed.\n");
2679  exit(EXIT_FAILURE);
2680  }
2681 
2682  gbl_args = odp_shm_addr(shm);
2683 
2684  if (gbl_args == NULL) {
2685  ODPH_ERR("Shared mem addr failed.\n");
2686  exit(EXIT_FAILURE);
2687  }
2688  gbl_args_init(gbl_args);
2689 
2690  /* Parse and store the application arguments */
2691  parse_args(argc, argv, &gbl_args->appl);
2692 
2694 
2695  if (sched_mode(gbl_args->appl.in_mode))
2696  gbl_args->appl.sched_mode = 1;
2697 
2698  num_workers = MAX_WORKERS;
2699  if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
2700  num_workers = gbl_args->appl.cpu_count;
2701 
2702  /* Get default worker cpumask */
2703  num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
2704  (void)odp_cpumask_to_str(&cpumask, gbl_args->cpumaskstr, sizeof(gbl_args->cpumaskstr));
2705 
2706  gbl_args->appl.num_workers = num_workers;
2707 
2708  print_options();
2709 
2710  for (i = 0; i < num_workers; i++)
2711  gbl_args->thread_args[i].thr_idx = i;
2712 
2713  if_count = gbl_args->appl.if_count;
2714 
2715  num_pools = 1;
2716  if (gbl_args->appl.pool_per_if)
2717  num_pools = if_count;
2718 
2719  if (odp_pool_capability(&pool_capa)) {
2720  ODPH_ERR("Pool capability failed\n");
2721  return -1;
2722  }
2723 
2724  if (num_pools > (int)pool_capa.pkt.max_pools) {
2725  ODPH_ERR("Too many pools %i\n", num_pools);
2726  return -1;
2727  }
2728 
2729  pkt_len = gbl_args->appl.packet_len;
2730 
2731  if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len) {
2732  pkt_len = pool_capa.pkt.max_len;
2733  printf("\nWarning: packet length reduced to %u\n\n", pkt_len);
2734  }
2735 
2736  if (gbl_args->appl.seg_len == UINT32_MAX)
2737  seg_len = gbl_args->appl.packet_len;
2738  else
2739  seg_len = gbl_args->appl.seg_len;
2740 
2741  /* Check whether we have sufficient segments to support requested packet
2742  * length, if not adjust to bigger segment size */
2743  if (seg_len < (pkt_len / pool_capa.pkt.max_segs_per_pkt))
2744  seg_len = pkt_len / pool_capa.pkt.max_segs_per_pkt;
2745 
2746  if (pool_capa.pkt.min_seg_len && seg_len < pool_capa.pkt.min_seg_len)
2747  seg_len = pool_capa.pkt.min_seg_len;
2748 
2749  if (pool_capa.pkt.max_seg_len && seg_len > pool_capa.pkt.max_seg_len)
2750  seg_len = pool_capa.pkt.max_seg_len;
2751 
2752  if ((gbl_args->appl.seg_len != UINT32_MAX) && (seg_len != gbl_args->appl.seg_len))
2753  printf("\nWarning: Segment length requested %d configured %d\n",
2754  gbl_args->appl.seg_len, seg_len);
2755 
2756  if (seg_len < gbl_args->appl.data_rd * 8) {
2757  ODPH_ERR("Requested data read length %u exceeds maximum segment length %u\n",
2758  gbl_args->appl.data_rd * 8, seg_len);
2759  return -1;
2760  }
2761 
2762  /* zero means default number of packets */
2763  if (gbl_args->appl.num_pkt == 0)
2764  num_pkt = DEFAULT_NUM_PKT;
2765  else
2766  num_pkt = gbl_args->appl.num_pkt;
2767 
2768  if (pool_capa.pkt.max_num && num_pkt > pool_capa.pkt.max_num) {
2769  if (gbl_args->appl.num_pkt == 0) {
2770  num_pkt = pool_capa.pkt.max_num;
2771  printf("\nWarning: number of packets reduced to %u\n\n",
2772  num_pkt);
2773  } else {
2774  ODPH_ERR("Too many packets %u. Maximum is %u.\n",
2775  num_pkt, pool_capa.pkt.max_num);
2776  return -1;
2777  }
2778  }
2779 
2780  gbl_args->num_pkt = num_pkt;
2781  gbl_args->pkt_len = pkt_len;
2782  gbl_args->seg_len = seg_len;
2783 
2784  printf("Resulting pool parameter values:\n");
2785  printf("Packets per pool: %u\n", num_pkt);
2786  printf("Packet length: %u\n", pkt_len);
2787  printf("Segment length: %u\n", seg_len);
2788 
2789  /* Create packet pool */
2790  odp_pool_param_init(&params);
2791  params.pkt.seg_len = seg_len;
2792  params.pkt.len = pkt_len;
2793  params.pkt.num = num_pkt;
2794  params.type = ODP_POOL_PACKET;
2795 
2796  for (i = 0; i < num_pools; i++) {
2797  pool_tbl[i] = odp_pool_create("packet pool", &params);
2798 
2799  if (pool_tbl[i] == ODP_POOL_INVALID) {
2800  ODPH_ERR("Pool create failed %i\n", i);
2801  exit(EXIT_FAILURE);
2802  }
2803 
2804  if (gbl_args->appl.verbose)
2805  odp_pool_print(pool_tbl[i]);
2806  }
2807 
2808  /* Create vector pool */
2809  num_vec_pools = 0;
2810  if (gbl_args->appl.vector_mode) {
2811  if (!sched_mode(gbl_args->appl.in_mode)) {
2812  ODPH_ERR("Vector mode only supports scheduler pktin modes (1-3)\n");
2813  return -1;
2814  }
2815 
2816  num_vec_pools = gbl_args->appl.pool_per_if ? if_count : 1;
2817  if (num_vec_pools > (int)pool_capa.vector.max_pools) {
2818  ODPH_ERR("Too many vector pools %i\n", num_vec_pools);
2819  return -1;
2820  }
2821 
2822  odp_pool_param_init(&params);
2823  if (set_vector_pool_params(&params, &pool_capa))
2824  return -1;
2825 
2826  gbl_args->vector_num = params.vector.num;
2827  gbl_args->vector_max_size = params.vector.max_size;
2828 
2829  /* Print resulting values */
2830  printf("Vectors per pool: %u\n", gbl_args->vector_num);
2831  printf("Vector size: %u\n", gbl_args->vector_max_size);
2832 
2833  for (i = 0; i < num_vec_pools; i++) {
2834  vec_pool_tbl[i] = odp_pool_create("vector pool", &params);
2835 
2836  if (vec_pool_tbl[i] == ODP_POOL_INVALID) {
2837  ODPH_ERR("Vector pool create failed %i\n", i);
2838  exit(EXIT_FAILURE);
2839  }
2840 
2841  if (gbl_args->appl.verbose)
2842  odp_pool_print(vec_pool_tbl[i]);
2843  }
2844  }
2845 
2846  printf("\n");
2847 
2848  bind_workers();
2849 
2850  odp_schedule_config_init(&sched_config);
2851 
2852  if (odp_schedule_capability(&sched_capa)) {
2853  ODPH_ERR("Schedule capability failed\n");
2854  exit(EXIT_FAILURE);
2855  }
2856 
2857  if (gbl_args->appl.flow_aware) {
2858  if (sched_capa.max_flow_id) {
2859  sched_config.max_flow_id = sched_capa.max_flow_id;
2860  } else {
2861  ODPH_ERR("Flow aware mode not supported\n");
2862  exit(EXIT_FAILURE);
2863  }
2864  }
2865 
2866  num_groups = gbl_args->appl.num_groups;
2867  /* Predefined groups are enabled by default */
2868  max_groups = sched_capa.max_groups - 3;
2869  if (max_groups > MAX_GROUPS)
2870  max_groups = MAX_GROUPS;
2871 
2872  if (num_groups > max_groups) {
2873  ODPH_ERR("Too many groups. Maximum is %i.\n", max_groups);
2874  exit(EXIT_FAILURE);
2875  }
2876 
2877  odp_schedule_config(&sched_config);
2878 
2879  /* Default */
2880  if (num_groups == 0) {
2881  group[0] = ODP_SCHED_GROUP_ALL;
2882  num_groups = 1;
2883  } else if (num_groups == -1) {
2884  group[0] = ODP_SCHED_GROUP_WORKER;
2885  num_groups = 1;
2886  } else {
2887  create_groups(num_groups, group);
2888  }
2889 
2890  pool = pool_tbl[0];
2891  vec_pool = vec_pool_tbl[0];
2892 
2893  printf("\nInterfaces\n----------\n");
2894 
2895  for (i = 0; i < if_count; ++i) {
2896  const char *dev = gbl_args->appl.if_names[i];
2897  int num_rx, num_tx;
2899 
2900  /* A queue per worker in scheduled mode */
2901  num_rx = gbl_args->appl.rx_queues > 0 ? gbl_args->appl.rx_queues : num_workers;
2902  num_tx = num_workers;
2903 
2904  if (!gbl_args->appl.sched_mode) {
2905  /* A queue per assigned worker */
2906  num_rx = gbl_args->pktios[i].num_rx_thr;
2907  num_tx = gbl_args->pktios[i].num_tx_thr;
2908  }
2909 
2910  /* Round robin pktios to groups */
2911  grp = group[i % num_groups];
2912 
2913  if (gbl_args->appl.pool_per_if) {
2914  pool = pool_tbl[i];
2915  vec_pool = vec_pool_tbl[i];
2916  }
2917 
2918  if (create_pktio(dev, i, num_rx, num_tx, pool, vec_pool, grp))
2919  exit(EXIT_FAILURE);
2920 
2921  /* Save destination eth address */
2922  if (gbl_args->appl.dst_change) {
2923  /* 02:00:00:00:00:XX */
2924  memset(&new_addr, 0, sizeof(odph_ethaddr_t));
2925  if (gbl_args->appl.addr_count) {
2926  memcpy(&new_addr, &gbl_args->appl.addrs[i],
2927  sizeof(odph_ethaddr_t));
2928  } else {
2929  new_addr.addr[0] = 0x02;
2930  new_addr.addr[5] = i;
2931  }
2932  gbl_args->dst_eth_addr[i] = new_addr;
2933  }
2934  }
2935 
2936  gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
2937 
2938  bind_queues();
2939 
2940  init_port_lookup_tbl();
2941 
2942  if (!gbl_args->appl.sched_mode)
2943  print_port_mapping();
2944 
2945  odp_barrier_init(&gbl_args->init_barrier, num_workers + 1);
2946  odp_barrier_init(&gbl_args->term_barrier, num_workers + 1);
2947 
2948  if (gbl_args->appl.in_mode == DIRECT_RECV)
2949  thr_run_func = run_worker_direct_mode;
2950  else if (gbl_args->appl.in_mode == PLAIN_QUEUE)
2951  thr_run_func = run_worker_plain_queue_mode;
2952  else /* SCHED_PARALLEL / SCHED_ATOMIC / SCHED_ORDERED */
2953  thr_run_func = gbl_args->appl.vector_mode ?
2954  run_worker_sched_mode_vector : run_worker_sched_mode;
2955 
2956  /* Create worker threads */
2957  odph_thread_common_param_init(&thr_common);
2958 
2959  thr_common.instance = instance;
2960  thr_common.cpumask = &cpumask;
2961  /* Synchronize thread start up. Test runs are more repeatable when
2962  * thread / thread ID / CPU ID mapping stays constant. */
2963  thr_common.sync = 1;
2964 
2965  for (i = 0; i < num_workers; ++i) {
2966  int j;
2967  int num_join;
2968  int mode = gbl_args->appl.group_mode;
2969 
2970  init_state(&gbl_args->appl, &gbl_args->thread_args[i].state, i);
2971  odph_thread_param_init(&thr_param[i]);
2972  thr_param[i].start = thr_run_func;
2973  thr_param[i].arg = &gbl_args->thread_args[i];
2974  thr_param[i].thr_type = ODP_THREAD_WORKER;
2975 
2976  gbl_args->thread_args[i].num_grp_join = 0;
2977 
2978  /* Fill in list of groups to join */
2979  if (gbl_args->appl.num_groups > 0) {
2980  num_join = if_count < num_groups ? if_count : num_groups;
2981 
2982  if (mode == 0 || mode == 1) {
2983  /* All threads join all groups */
2984  if (mode == 0)
2985  num_join = num_groups;
2986 
2987  gbl_args->thread_args[i].num_grp_join = num_join;
2988 
2989  for (j = 0; j < num_join; j++)
2990  gbl_args->thread_args[i].group[j] = group[j];
2991  } else {
2992  /* Thread joins first groups in round robin */
2993  if (num_workers >= num_join) {
2994  gbl_args->thread_args[i].num_grp_join = 1;
2995  gbl_args->thread_args[i].group[0] = group[i % num_join];
2996  } else {
2997  int cnt = 0;
2998 
2999  for (j = 0; i + j < num_join; j += num_workers) {
3000  gbl_args->thread_args[i].group[cnt] = group[i + j];
3001  cnt++;
3002  }
3003 
3004  gbl_args->thread_args[i].num_grp_join = cnt;
3005  }
3006  }
3007  }
3008 
3009  stats[i] = &gbl_args->thread_args[i].stats;
3010  }
3011 
3012  num_thr = odph_thread_create(gbl_args->thread_tbl, &thr_common,
3013  thr_param, num_workers);
3014 
3015  if (num_thr != num_workers) {
3016  ODPH_ERR("Worker create failed: %i\n", num_thr);
3017  exit(EXIT_FAILURE);
3018  }
3019 
3020  if (gbl_args->appl.verbose)
3022 
3023  /* Start packet receive and transmit */
3024  for (i = 0; i < if_count; ++i) {
3025  odp_pktio_t pktio;
3026 
3027  pktio = gbl_args->pktios[i].pktio;
3028  ret = odp_pktio_start(pktio);
3029  if (ret) {
3030  ODPH_ERR("Pktio start failed: %s\n", gbl_args->appl.if_names[i]);
3031  exit(EXIT_FAILURE);
3032  }
3033  }
3034 
3035  ret = print_speed_stats(num_workers, stats, gbl_args->appl.time,
3036  gbl_args->appl.accuracy);
3037 
3038  for (i = 0; i < if_count; ++i) {
3039  if (odp_pktio_stop(gbl_args->pktios[i].pktio)) {
3040  ODPH_ERR("Pktio stop failed: %s\n", gbl_args->appl.if_names[i]);
3041  exit(EXIT_FAILURE);
3042  }
3043  }
3044 
3045  odp_atomic_store_u32(&gbl_args->exit_threads, 1);
3046  if (gbl_args->appl.in_mode != DIRECT_RECV)
3047  odp_barrier_wait(&gbl_args->term_barrier);
3048 
3049  odph_thread_join_result_t res[num_workers];
3050 
3051  /* Master thread waits for other threads to exit */
3052  if (odph_thread_join_result(gbl_args->thread_tbl, res, num_workers) != num_workers) {
3053  ODPH_ERR("Worker join failed\n");
3054  exit(EXIT_FAILURE);
3055  }
3056 
3057  for (i = 0; i < num_workers; i++) {
3058  if (res[i].is_sig || res[i].ret != 0) {
3059  ODPH_ERR("Worker thread failure%s: %d\n", res[i].is_sig ?
3060  " (signaled)" : "", res[i].ret);
3061  exit(EXIT_FAILURE);
3062  }
3063  }
3064 
3065  for (i = 0; i < if_count; ++i) {
3066  odp_pktio_t pktio = gbl_args->pktios[i].pktio;
3067 
3068  if (gbl_args->appl.verbose && odp_pktio_extra_stat_info(pktio, NULL, 0) > 0) {
3069  printf("Pktio %s extra statistics:\n", gbl_args->appl.if_names[i]);
3071  }
3072 
3073  if (gbl_args->pktios[i].compl_q != ODP_QUEUE_INVALID)
3074  (void)odp_queue_destroy(gbl_args->pktios[i].compl_q);
3075 
3076  if (odp_pktio_close(pktio)) {
3077  ODPH_ERR("Pktio close failed: %s\n", gbl_args->appl.if_names[i]);
3078  exit(EXIT_FAILURE);
3079  }
3080  }
3081 
3082  free(gbl_args->appl.if_names);
3083  free(gbl_args->appl.if_str);
3084 
3085  for (i = 0; i < gbl_args->appl.num_om; i++)
3086  free(gbl_args->appl.output_map[i]);
3087 
3088  gbl_args = NULL;
3089  odp_mb_full();
3090 
3091  for (i = 0; i < num_pools; i++) {
3092  if (odp_pool_destroy(pool_tbl[i])) {
3093  ODPH_ERR("Pool destroy failed: %i\n", i);
3094  exit(EXIT_FAILURE);
3095  }
3096  }
3097 
3098  for (i = 0; i < num_vec_pools; i++) {
3099  if (odp_pool_destroy(vec_pool_tbl[i])) {
3100  ODPH_ERR("Vector pool destroy failed: %i\n", i);
3101  exit(EXIT_FAILURE);
3102  }
3103  }
3104 
3105  if (odp_shm_free(shm)) {
3106  ODPH_ERR("Shm free failed\n");
3107  exit(EXIT_FAILURE);
3108  }
3109 
3110  if (odp_term_local()) {
3111  ODPH_ERR("Term local failed\n");
3112  exit(EXIT_FAILURE);
3113  }
3114 
3115  if (odp_term_global(instance)) {
3116  ODPH_ERR("Term global failed\n");
3117  exit(EXIT_FAILURE);
3118  }
3119 
3120  return ret;
3121 }
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_mb_full(void)
Full memory barrier.
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
#define odp_likely(x)
Branch likely taken.
Definition: spec/hints.h:59
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
void odp_event_free_multi(const odp_event_t event[], int num)
Free multiple events.
void odp_event_free(odp_event_t event)
Free event.
odp_event_type_t odp_event_type(odp_event_t event)
Event type of an event.
#define ODP_EVENT_INVALID
Invalid event.
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.
int odp_pktio_info(odp_pktio_t pktio, odp_pktio_info_t *info)
Retrieve information about a pktio.
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_extra_stats_print(odp_pktio_t pktio)
Print extra statistics for a packet IO interface.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
int odp_pktin_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
Event queues for packet input.
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.
void odp_pktio_print(odp_pktio_t pktio)
Print pktio info to the console.
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_pktout_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
Event queues for packet output.
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.
odp_pktio_op_mode_t
Packet IO operation mode.
int odp_pktio_extra_stat_info(odp_pktio_t pktio, odp_pktio_extra_stat_info_t info[], int num)
Get extra statistics counter information for a packet IO interface.
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_QUEUE
Packet output through event queues.
@ ODP_PKTOUT_MODE_DISABLED
Application will never send to this interface.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIO_OP_MT
Multithread safe operation.
@ ODP_PKTIO_LINK_PAUSE_ON
Pause frame flow control enabled.
@ ODP_PKTIN_MODE_QUEUE
Packet input through plain event queues.
@ ODP_PKTIN_MODE_DISABLED
Application will never receive from this 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_tx_compl_request(odp_packet_t pkt, const odp_packet_tx_compl_opt_t *opt)
Request packet transmit completion.
int odp_packet_input_index(odp_packet_t pkt)
Packet input interface index.
int odp_packet_tx_compl_done(odp_pktio_t pktio, uint32_t compl_id)
Check packet transmit completion.
void odp_packet_to_event_multi(const odp_packet_t pkt[], odp_event_t ev[], int num)
Convert multiple packet handles to events.
uint32_t odp_packet_seg_len(odp_packet_t pkt)
Packet data length following the data pointer.
uint32_t odp_packet_headroom(odp_packet_t pkt)
Packet headroom length.
void odp_packet_prefetch(odp_packet_t pkt, uint32_t offset, uint32_t len)
Packet data prefetch.
odp_packet_vector_t odp_packet_vector_from_event(odp_event_t ev)
Get packet vector handle from event.
int odp_packet_num_segs(odp_packet_t pkt)
Number of segments.
odp_packet_t odp_packet_copy(odp_packet_t pkt, odp_pool_t pool)
Full copy of a packet.
uint32_t odp_packet_user_area_size(odp_packet_t pkt)
User area size.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
odp_packet_tx_compl_mode_t
Packet transmit completion mode.
uint32_t odp_packet_len(odp_packet_t pkt)
Packet data length.
int odp_packet_has_error(odp_packet_t pkt)
Check for all parse errors in packet.
int odp_packet_has_tx_compl_request(odp_packet_t pkt)
Check if packet transmit completion is requested.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
void odp_packet_free(odp_packet_t pkt)
Free packet.
void odp_packet_vector_free(odp_packet_vector_t pktv)
Free packet vector.
void odp_packet_l4_chksum_insert(odp_packet_t pkt, int insert)
Layer 4 checksum insertion override.
#define ODP_PACKET_INVALID
Invalid packet.
uint32_t odp_packet_vector_tbl(odp_packet_vector_t pktv, odp_packet_t **pkt_tbl)
Get packet vector table.
odp_pool_t odp_packet_pool(odp_packet_t pkt)
Packet pool.
#define ODP_PACKET_VECTOR_INVALID
Invalid packet vector.
void odp_packet_l3_chksum_insert(odp_packet_t pkt, int insert)
Layer 3 checksum insertion override.
@ ODP_PROTO_LAYER_ALL
All layers.
@ ODP_PROTO_LAYER_NONE
No layers.
@ ODP_PACKET_TX_COMPL_POLL
Enable packet transmit completion check through polling.
@ ODP_PACKET_TX_COMPL_DISABLED
Disable packet transmit completion.
@ ODP_PACKET_TX_COMPL_EVENT
Enable packet transmit completion event.
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()
void odp_pool_print(odp_pool_t pool)
Print pool info.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_VECTOR
Vector event pool.
@ ODP_POOL_PACKET
Packet pool.
int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num)
Enqueue multiple events to a queue.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_event_t odp_queue_deq(odp_queue_t queue)
Dequeue an event from a queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
int odp_queue_deq_multi(odp_queue_t queue, odp_event_t events[], int num)
Dequeue multiple events from a queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
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.
int odp_schedule_prio_t
Scheduling priority level.
int odp_schedule_group_t
Scheduler thread group.
void odp_schedule_config_init(odp_schedule_config_t *config)
Initialize schedule configuration options.
int odp_schedule_group_join(odp_schedule_group_t group, const odp_thrmask_t *mask)
Join a schedule group.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
#define ODP_SCHED_GROUP_WORKER
Group of all worker threads.
#define ODP_SCHED_GROUP_INVALID
Invalid scheduler group.
#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.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
int odp_schedule_capability(odp_schedule_capability_t *capa)
Query scheduler capabilities.
odp_schedule_group_t odp_schedule_group_create(const char *name, const odp_thrmask_t *mask)
Schedule group create.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
void odp_schedule_resume(void)
Resume scheduling.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
void odp_shm_print_all(void)
Print all shared memory blocks.
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.
void odp_sys_info_print(void)
Print system info.
void odp_thrmask_set(odp_thrmask_t *mask, int thr)
Add thread to mask.
int odp_thread_id(void)
Get thread identifier.
void odp_thrmask_zero(odp_thrmask_t *mask)
Clear entire thread mask.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
uint64_t odp_time_to_ns(odp_time_t time)
Convert time to nanoseconds.
odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
Time difference.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
odp_time_t odp_time_local(void)
Current local time.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
Packet transmit completion request options.
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_pktin_vector_config_t vector
Packet input vector configuration.
uint64_t max_tmo_ns
Maximum timeout in nanoseconds for the producer to wait for the vector of packets.
uint64_t min_tmo_ns
Minimum value allowed to be configured to odp_pktin_vector_config_t::max_tmo_ns.
uint32_t min_size
Minimum value allowed to be configured to odp_pktin_vector_config_t::max_size.
uint32_t max_size
Maximum number of packets that can be accumulated into a packet vector by a producer.
odp_support_t supported
Packet input vector availability.
odp_bool_t enable
Enable packet input vector.
uint32_t max_size
Maximum number of packets in a vector.
uint64_t max_tmo_ns
Maximum time to wait for packets.
odp_bool_t queue_type_sched
Scheduled queue support.
uint32_t mode_poll
Packet transmit completion mode ODP_PACKET_TX_COMPL_POLL support.
odp_pktio_set_op_t set_op
Supported set operations.
uint32_t max_output
Maximum valid value for 'maxlen_output'.
odp_pktin_vector_capability_t vector
Packet input vector capability.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_input
Maximum valid value for 'maxlen_input'.
struct odp_pktio_capability_t::@110 flow_control
Supported flow control modes.
odp_pktio_config_t config
Supported pktio configuration options.
uint32_t min_output
Minimum valid value for 'maxlen_output'.
uint32_t max_output_queues
Maximum number of output queues.
uint32_t max_compl_id
Maximum supported completion ID value.
struct odp_pktio_capability_t::@108 tx_compl
Supported packet Tx completion options.
uint32_t min_input
Minimum valid value for 'maxlen_input'.
uint32_t mode_event
Packet transmit completion mode ODP_PACKET_TX_COMPL_EVENT support.
struct odp_pktio_capability_t::@107 maxlen
Supported frame lengths for odp_pktio_maxlen_set()
uint32_t pause_tx
Generation of traditional Ethernet pause frames.
uint32_t pause_rx
Reception of traditional Ethernet pause frames.
Packet IO configuration options.
uint32_t max_compl_id
Maximum completion index.
uint32_t mode_event
Enable packet transmit completion events.
odp_pktio_link_pause_t pause_tx
Transmission of flow control frames.
odp_pktout_config_opt_t pktout
Packet output configuration options bit field.
struct odp_pktio_config_t::@102 flow_control
Link flow control configuration.
uint32_t mode_poll
Enable packet transmit completion check through polling.
odp_pktio_link_pause_t pause_rx
Reception of flow control frames.
odp_pktio_parser_config_t parser
Packet input parser configuration.
struct odp_pktio_config_t::@103 tx_compl
Packet transmit completion configuration.
odp_pktin_config_opt_t pktin
Packet input configuration options bit field.
Packet IO information.
const char * drv_name
Packet IO driver name (implementation specific)
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_segs_per_pkt
Maximum number of segments per packet.
struct odp_pool_capability_t::@124 vector
Vector pool capabilities.
uint32_t max_size
Maximum buffer data size in bytes.
uint32_t min_seg_len
Minimum packet segment data length in bytes.
uint32_t max_pools
Maximum number of pools of any type (odp_pool_type_t)
uint32_t max_seg_len
Maximum packet segment data length in bytes.
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 max_size
Maximum number of handles (such as odp_packet_t) in a vector.
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.
struct odp_pool_param_t::@128 vector
Parameters for vector pools.
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
uint32_t max_flow_id
Maximum flow ID per queue.
uint32_t max_groups
Maximum number of scheduling groups.
Schedule configuration.
uint32_t max_flow_id
Maximum flow ID per queue.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
struct odp_feature_t::@148 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()
uint64_t ts_all
Timestamp all packets on packet input.
struct odp_pktin_config_opt_t::@100 bit
Option flags.
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.
struct odp_pktout_config_opt_t::@101 bit
Option flags for packet output.
uint64_t no_packet_refs
Packet references not used on packet output.
uint64_t ipv4_chksum_ena
Enable IPv4 header checksum insertion.
uint64_t tcp_chksum_ena
Enable TCP checksum insertion.
uint64_t udp_chksum_ena
Enable UDP checksum insertion.