API Reference Manual  1.46.0
odp_l3fwd.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2016-2018 Linaro Limited
3  */
4 
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <getopt.h>
17 #include <unistd.h>
18 #include <inttypes.h>
19 
20 #include <odp_api.h>
21 #include <odp/helper/odph_api.h>
22 
23 #include "odp_l3fwd_db.h"
24 #include "odp_l3fwd_lpm.h"
25 
26 #define POOL_NUM_PKT 8192
27 #define POOL_SEG_LEN 1856
28 #define MAX_PKT_BURST 32
29 
30 #define MAX_NB_WORKER (ODP_THREAD_COUNT_MAX - 1)
31 #define MAX_NB_PKTIO 32
32 #define MAX_NB_QUEUE 32
33 #define MAX_NB_QCONFS 1024
34 #define MAX_NB_ROUTE 32
35 
36 #define INVALID_ID (-1)
37 #define PRINT_INTERVAL 1 /* interval seconds of printing stats */
38 
40 #define NO_PATH(file_name) (strrchr((file_name), '/') ? \
41  strrchr((file_name), '/') + 1 : (file_name))
42 
43 struct l3fwd_pktio_s {
44  odp_pktio_t pktio;
45  odph_ethaddr_t mac_addr;
46  odp_pktin_queue_t ifin[MAX_NB_QUEUE];
47  odp_pktout_queue_t ifout[MAX_NB_QUEUE];
48  int nb_rxq; /* capa max */
49  int nb_txq; /* capa max */
50  int rxq_idx; /* requested, maybe greater than nb_rxq */
51  int txq_idx; /* requested, maybe greater than nb_txq */
52 };
53 
54 struct l3fwd_qconf_s {
55  uint8_t if_idx; /* port index */
56  uint8_t rxq_idx; /* recv queue index in a port */
57  uint8_t core_idx; /* this core should handle traffic */
58 };
59 
60 struct thread_arg_s {
61  uint64_t packets;
62  uint64_t rx_drops;
63  uint64_t tx_drops;
64  struct {
65  int if_idx; /* interface index */
66  int nb_rxq; /* number of rxq this thread will access */
67  int rxq[MAX_NB_QUEUE]; /* rxq[i] is index in pktio.ifin[] */
68  int txq_idx; /* index in pktio.ifout[] */
69  } pktio[MAX_NB_PKTIO];
70  int nb_pktio;
71  int thr_idx;
72 };
73 
74 typedef struct {
75  char *if_names_buf; /* memory buffer for all if_names */
76  char *if_names[MAX_NB_PKTIO]; /* pointers to name strings stored in if_names_buf */
77  int if_count;
78  char *route_str[MAX_NB_ROUTE];
79  unsigned int worker_count;
80  struct l3fwd_qconf_s qconf_config[MAX_NB_QCONFS];
81  unsigned int qconf_count;
82  uint32_t duration; /* seconds to run */
83  uint8_t hash_mode; /* 1:hash, 0:lpm */
84  uint8_t dest_mac_changed[MAX_NB_PKTIO]; /* 1: dest mac from cmdline */
85  int error_check; /* Check packets for errors */
86 } app_args_t;
87 
88 typedef struct {
89  app_args_t cmd_args;
90  struct l3fwd_pktio_s l3fwd_pktios[MAX_NB_PKTIO];
91  struct thread_arg_s worker_args[MAX_NB_WORKER];
92  odph_ethaddr_t eth_dest_mac[MAX_NB_PKTIO];
94  odp_barrier_t barrier;
96  odp_shm_t shm;
98  odp_atomic_u32_t exit_threads;
99 
100  /* forward func, hash or lpm */
101  int (*fwd_func)(odp_packet_t pkt, int sif);
102 } global_data_t;
103 
104 static global_data_t *global;
105 
106 static int create_pktio(const char *name, odp_pool_t pool,
107  struct l3fwd_pktio_s *fwd_pktio)
108 {
109  odp_pktio_param_t pktio_param;
110  odp_pktio_t pktio;
112  odp_pktio_config_t config;
113  int rc;
114 
115  odp_pktio_param_init(&pktio_param);
116 
117  pktio = odp_pktio_open(name, pool, &pktio_param);
118  if (pktio == ODP_PKTIO_INVALID) {
119  printf("Failed to open %s\n", name);
120  return -1;
121  }
122  fwd_pktio->pktio = pktio;
123 
124  rc = odp_pktio_capability(pktio, &capa);
125  if (rc) {
126  printf("Error: pktio %s: unable to read capabilities!\n",
127  name);
128 
129  return -1;
130  }
131 
132  odp_pktio_config_init(&config);
133  config.parser.layer = global->cmd_args.error_check ?
136 
137  /* Provide hint to pktio that packet references are not used */
138  config.pktout.bit.no_packet_refs = 1;
139 
140  odp_pktio_config(pktio, &config);
141 
142  fwd_pktio->nb_rxq = (int)capa.max_input_queues;
143  fwd_pktio->nb_txq = (int)capa.max_output_queues;
144 
145  if (fwd_pktio->nb_rxq > MAX_NB_QUEUE)
146  fwd_pktio->nb_rxq = MAX_NB_QUEUE;
147 
148  if (fwd_pktio->nb_txq > MAX_NB_QUEUE)
149  fwd_pktio->nb_txq = MAX_NB_QUEUE;
150 
151  return 0;
152 }
153 
154 static void setup_fwd_db(void)
155 {
156  fwd_db_entry_t *entry;
157  int if_idx;
158  app_args_t *args;
159 
160  args = &global->cmd_args;
161  if (args->hash_mode)
162  init_fwd_hash_cache();
163  else
164  fib_tbl_init();
165 
166  for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
167  if_idx = entry->oif_id;
168  if (!args->hash_mode)
169  fib_tbl_insert(entry->subnet.addr, if_idx,
170  entry->subnet.depth);
171  if (args->dest_mac_changed[if_idx])
172  global->eth_dest_mac[if_idx] = entry->dst_mac;
173  else
174  entry->dst_mac = global->eth_dest_mac[if_idx];
175  }
176 }
177 
183 static inline void ipv4_dec_ttl_csum_update(odph_ipv4hdr_t *ip)
184 {
185  uint16_t a = ~odp_cpu_to_be_16(1 << 8);
186 
187  ip->ttl--;
188  if (ip->chksum >= a)
189  ip->chksum -= a;
190  else
191  ip->chksum += odp_cpu_to_be_16(1 << 8);
192 }
193 
194 static inline int l3fwd_pkt_hash(odp_packet_t pkt, int sif)
195 {
196  fwd_db_entry_t *entry;
197  ipv4_tuple5_t key;
198  odph_ethhdr_t *eth;
199  odph_udphdr_t *udp;
200  odph_ipv4hdr_t *ip;
201  int dif;
202 
203  ip = odp_packet_l3_ptr(pkt, NULL);
204  key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);
205  key.src_ip = odp_be_to_cpu_32(ip->src_addr);
206  key.proto = ip->proto;
207 
208  if (odp_packet_has_udp(pkt) ||
209  odp_packet_has_tcp(pkt)) {
210  /* UDP or TCP*/
211  void *ptr = odp_packet_l4_ptr(pkt, NULL);
212 
213  udp = (odph_udphdr_t *)ptr;
214  key.src_port = odp_be_to_cpu_16(udp->src_port);
215  key.dst_port = odp_be_to_cpu_16(udp->dst_port);
216  } else {
217  key.src_port = 0;
218  key.dst_port = 0;
219  }
220  entry = find_fwd_db_entry(&key);
221  ipv4_dec_ttl_csum_update(ip);
222  eth = odp_packet_l2_ptr(pkt, NULL);
223  if (entry) {
224  eth->src = entry->src_mac;
225  eth->dst = entry->dst_mac;
226  dif = entry->oif_id;
227  } else {
228  /* no route, send by src port */
229  eth->dst = eth->src;
230  dif = sif;
231  }
232 
233  return dif;
234 }
235 
236 static inline int l3fwd_pkt_lpm(odp_packet_t pkt, int sif)
237 {
238  odph_ipv4hdr_t *ip;
239  odph_ethhdr_t *eth;
240  int dif;
241  int ret;
242 
243  ip = odp_packet_l3_ptr(pkt, NULL);
244  ipv4_dec_ttl_csum_update(ip);
245  eth = odp_packet_l2_ptr(pkt, NULL);
246 
247  /* network byte order maybe different from host */
248  ret = fib_tbl_lookup(odp_be_to_cpu_32(ip->dst_addr), &dif);
249  if (ret)
250  dif = sif;
251 
252  eth->dst = global->eth_dest_mac[dif];
253  eth->src = global->l3fwd_pktios[dif].mac_addr;
254 
255  return dif;
256 }
257 
269 static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)
270 {
271  odp_packet_t pkt;
272  unsigned dropped = 0;
273  unsigned i, j;
274  int err;
275 
276  for (i = 0, j = 0; i < num; ++i) {
277  pkt = pkt_tbl[i];
278  err = 0;
279 
280  if (global->cmd_args.error_check)
281  err = odp_packet_has_error(pkt);
282 
283  if (odp_unlikely(err || !odp_packet_has_ipv4(pkt))) {
284  odp_packet_free(pkt);
285  dropped++;
286  } else if (odp_unlikely(i != j++)) {
287  pkt_tbl[j - 1] = pkt;
288  }
289  }
290 
291  return dropped;
292 }
293 
294 static int run_worker(void *arg)
295 {
296  int if_idx;
297  struct thread_arg_s *thr_arg = arg;
298  odp_pktin_queue_t inq;
299  int input_ifs[thr_arg->nb_pktio];
300  odp_pktin_queue_t input_queues[thr_arg->nb_pktio];
301  odp_pktout_queue_t output_queues[global->cmd_args.if_count];
302  odp_packet_t pkt_tbl[MAX_PKT_BURST];
303  odp_packet_t *tbl;
304  int pkts, drop, sent;
305  int dst_port, dif;
306  int i, j;
307  int pktio = 0;
308  int num_pktio = 0;
309 
310  /* Copy all required handles to local memory */
311  for (i = 0; i < global->cmd_args.if_count; i++) {
312  int txq_idx = thr_arg->pktio[i].txq_idx;
313 
314  output_queues[i] = global->l3fwd_pktios[i].ifout[txq_idx];
315 
316  if_idx = thr_arg->pktio[i].if_idx;
317  for (j = 0; j < thr_arg->pktio[i].nb_rxq; j++) {
318  int rxq_idx = thr_arg->pktio[i].rxq[j];
319 
320  inq = global->l3fwd_pktios[if_idx].ifin[rxq_idx];
321  input_ifs[num_pktio] = if_idx;
322  input_queues[num_pktio] = inq;
323  num_pktio++;
324  }
325  }
326 
327  if (num_pktio == 0)
328  ODPH_ABORT("No pktio devices found\n");
329 
330  if_idx = input_ifs[pktio];
331  inq = input_queues[pktio];
332 
333  odp_barrier_wait(&global->barrier);
334 
335  while (!odp_atomic_load_u32(&global->exit_threads)) {
336  if (num_pktio > 1) {
337  if_idx = input_ifs[pktio];
338  inq = input_queues[pktio];
339  pktio++;
340  if (pktio == num_pktio)
341  pktio = 0;
342  }
343 
344  pkts = odp_pktin_recv(inq, pkt_tbl, MAX_PKT_BURST);
345  if (pkts < 1)
346  continue;
347 
348  thr_arg->packets += pkts;
349  drop = drop_err_pkts(pkt_tbl, pkts);
350  pkts -= drop;
351  thr_arg->rx_drops += drop;
352  if (odp_unlikely(pkts < 1))
353  continue;
354 
355  dif = global->fwd_func(pkt_tbl[0], if_idx);
356  tbl = &pkt_tbl[0];
357  while (pkts) {
358  dst_port = dif;
359  for (i = 1; i < pkts; i++) {
360  dif = global->fwd_func(tbl[i], if_idx);
361  if (dif != dst_port)
362  break;
363  }
364  sent = odp_pktout_send(output_queues[dst_port], tbl, i);
365  if (odp_unlikely(sent < i)) {
366  sent = sent < 0 ? 0 : sent;
367  odp_packet_free_multi(&tbl[sent], i - sent);
368  thr_arg->tx_drops += i - sent;
369  }
370 
371  if (i < pkts)
372  tbl += i;
373 
374  pkts -= i;
375  }
376  }
377 
378  /* Make sure that latest stat writes are visible to other threads */
379  odp_mb_full();
380 
381  return 0;
382 }
383 
384 static int find_port_id_by_name(char *name, app_args_t *args)
385 {
386  int i;
387 
388  if (!name)
389  return -1;
390 
391  for (i = 0; i < args->if_count; i++) {
392  if (!strcmp(name, args->if_names[i]))
393  return i;
394  }
395 
396  return -1;
397 }
398 
399 /* split string into tokens */
400 static int split_string(char *str, int stringlen,
401  char **tokens, int maxtokens, char delim)
402 {
403  int i, tok = 0;
404  int tokstart = 1; /* first token is right at start of string */
405 
406  if (str == NULL || tokens == NULL)
407  goto einval_error;
408 
409  for (i = 0; i < stringlen; i++) {
410  if (str[i] == '\0' || tok >= maxtokens)
411  break;
412  if (tokstart) {
413  tokstart = 0;
414  tokens[tok++] = &str[i];
415  }
416  if (str[i] == delim) {
417  str[i] = '\0';
418  tokstart = 1;
419  }
420  }
421  return tok;
422 
423 einval_error:
424  errno = EINVAL;
425  return -1;
426 }
427 
428 static int parse_config(char *cfg_str, app_args_t *args)
429 {
430  char s[256];
431  const char *p, *p0 = cfg_str;
432  char *end;
433  enum fieldnames {
434  FLD_PORT = 0,
435  FLD_QUEUE,
436  FLD_LCORE,
437  FLD_LAST
438  };
439  unsigned long int_fld[FLD_LAST];
440  char *str_fld[FLD_LAST];
441  int i;
442  unsigned size;
443  int nb_qconfs = 0;
444  struct l3fwd_qconf_s *qconf_array = &args->qconf_config[0];
445 
446  p = strchr(p0, '(');
447  while (p != NULL) {
448  ++p;
449  p0 = strchr(p, ')');
450  if (p0 == NULL)
451  return -1;
452 
453  size = p0 - p;
454  if (size >= sizeof(s))
455  return -1;
456 
457  snprintf(s, sizeof(s), "%.*s", size, p);
458  i = split_string(s, sizeof(s), str_fld, FLD_LAST, ',');
459  if (i != FLD_LAST)
460  return -1;
461  for (i = 0; i < FLD_LAST; i++) {
462  errno = 0;
463  int_fld[i] = strtoul(str_fld[i], &end, 0);
464  if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
465  return -1;
466  }
467  if (nb_qconfs >= MAX_NB_QCONFS) {
468  printf("exceeded max number of queue params: %d\n",
469  nb_qconfs);
470  return -1;
471  }
472  qconf_array[nb_qconfs].if_idx = (uint8_t)int_fld[FLD_PORT];
473  qconf_array[nb_qconfs].rxq_idx = (uint8_t)int_fld[FLD_QUEUE];
474  qconf_array[nb_qconfs].core_idx = (uint8_t)int_fld[FLD_LCORE];
475  ++nb_qconfs;
476 
477  p = strchr(p0, '(');
478  }
479  args->qconf_count = nb_qconfs;
480 
481  return 0;
482 }
483 
484 static void print_usage(char *progname)
485 {
486  printf("\n"
487  "ODP L3 forwarding application.\n"
488  "\n"
489  "Usage: %s OPTIONS\n"
490  " E.g. %s -i eth0,eth1 -r 1.1.1.0/24,eth0 -r 2.2.2.0/24,eth1\n"
491  " In the above example,\n"
492  " eth0 will send pkts to eth1 and vice versa\n"
493  "\n"
494  "Mandatory OPTIONS:\n"
495  " -i, --interface eth interfaces (comma-separated, no spaces)\n"
496  " -r, --route SubNet,Intf[,NextHopMAC]\n"
497  " NextHopMAC can be optional\n"
498  "\n"
499  "Optional OPTIONS:\n"
500  " -s, --style [lpm|hash], ip lookup method\n"
501  " optional, default as lpm\n"
502  " -d, --duration Seconds to run and print stats\n"
503  " optional, default as 0, run forever\n"
504  " -t, --thread Number of threads to do forwarding\n"
505  " 0=all available, default=1\n"
506  " -q, --queue Configure rx queue(s) for port\n"
507  " optional, format: [(port, queue, thread),...]\n"
508  " for example: -q '(0, 0, 1),(1,0,2)'\n"
509  " -e, --error_check 0: Don't check packet errors (default)\n"
510  " 1: Check packet errors\n"
511  " -h, --help Display help and exit.\n\n"
512  "\n", NO_PATH(progname), NO_PATH(progname)
513  );
514 }
515 
516 static void parse_cmdline_args(int argc, char *argv[], app_args_t *args)
517 {
518  int opt;
519  char *token, *local;
520  size_t len, route_index = 0;
521  int mem_failure = 0;
522  unsigned int i;
523 
524  static struct option longopts[] = {
525  {"interface", required_argument, NULL, 'i'}, /* return 'i' */
526  {"route", required_argument, NULL, 'r'}, /* return 'r' */
527  {"style", required_argument, NULL, 's'}, /* return 's' */
528  {"duration", required_argument, NULL, 'd'}, /* return 'd' */
529  {"thread", required_argument, NULL, 't'}, /* return 't' */
530  {"queue", required_argument, NULL, 'q'}, /* return 'q' */
531  {"error_check", required_argument, NULL, 'e'},
532  {"help", no_argument, NULL, 'h'}, /* return 'h' */
533  {NULL, 0, NULL, 0}
534  };
535 
536  args->worker_count = 1; /* use one worker by default */
537 
538  while (1) {
539  opt = getopt_long(argc, argv, "+s:t:d:i:r:q:e:h",
540  longopts, NULL);
541 
542  if (opt == -1)
543  break; /* No more options */
544 
545  switch (opt) {
546  /* parse ip lookup method */
547  case 's':
548  if (!strcmp(optarg, "hash"))
549  args->hash_mode = 1;
550  break;
551  /* parse number of worker threads to be run*/
552  case 't':
553  i = odp_cpu_count();
554  args->worker_count = atoi(optarg);
555  if (args->worker_count > i) {
556  printf("Too many threads,"
557  "truncate to cpu count: %d\n", i);
558  args->worker_count = i;
559  }
560 
561  break;
562 
563  /* parse seconds to run */
564  case 'd':
565  args->duration = atoi(optarg);
566  break;
567 
568  /* parse packet-io interface names */
569  case 'i':
570  len = strlen(optarg);
571  if (len == 0) {
572  print_usage(argv[0]);
573  exit(EXIT_FAILURE);
574  }
575  len += 1; /* add room for '\0' */
576 
577  local = malloc(len);
578  if (!local) {
579  print_usage(argv[0]);
580  exit(EXIT_FAILURE);
581  }
582  args->if_names_buf = local;
583 
584  /* store the if names (reset names string) */
585  strcpy(local, optarg);
586  for (token = strtok(local, ","), i = 0;
587  token != NULL; token = strtok(NULL, ","), i++) {
588  if (i >= MAX_NB_PKTIO) {
589  printf("too many ports specified, "
590  "truncated to %d", MAX_NB_PKTIO);
591  break; /* for */
592  }
593  args->if_names[i] = token;
594  }
595  if (i == 0) {
596  print_usage(argv[0]);
597  free(local);
598  exit(EXIT_FAILURE);
599  }
600  args->if_count = i;
601  break;
602 
603  /*Configure Route in forwarding database*/
604  case 'r':
605  if (route_index >= MAX_NB_ROUTE) {
606  printf("No more routes can be added\n");
607  break;
608  }
609  local = calloc(1, strlen(optarg) + 1);
610  if (!local) {
611  mem_failure = 1;
612  break;
613  }
614  memcpy(local, optarg, strlen(optarg));
615  local[strlen(optarg)] = '\0';
616  args->route_str[route_index++] = local;
617  break;
618 
619  case 'e':
620  args->error_check = atoi(optarg);
621  break;
622 
623  case 'h':
624  print_usage(argv[0]);
625  exit(EXIT_SUCCESS);
626  break;
627 
628  case 'q':
629  parse_config(optarg, args);
630  break;
631 
632  default:
633  break;
634  }
635  }
636 
637  /* checking arguments */
638  if (args->if_count == 0) {
639  printf("\nNo option -i specified.\n");
640  goto out;
641  }
642 
643  if (args->route_str[0] == NULL) {
644  printf("\nNo option -r specified.\n");
645  goto out;
646  }
647 
648  if (mem_failure == 1) {
649  printf("\nAllocate memory failure.\n");
650  goto out;
651  }
652  optind = 1; /* reset 'extern optind' from the getopt lib */
653  return;
654 
655 out:
656  print_usage(argv[0]);
657  exit(EXIT_FAILURE);
658 }
659 
660 static void print_info(char *progname, app_args_t *args)
661 {
662  int i;
663 
665 
666  printf("Running ODP appl: \"%s\"\n"
667  "-----------------\n"
668  "IP Lookup: %s\n"
669  "IF Count: %i\n"
670  "Using IFs: ",
671  progname,
672  args->hash_mode ? "hash" : "lpm",
673  args->if_count);
674 
675  for (i = 0; i < args->if_count; ++i)
676  printf(" %s", args->if_names[i]);
677 
678  printf("\n\n");
679  fflush(NULL);
680 }
681 
689 static void setup_worker_qconf(app_args_t *args)
690 {
691  int j, rxq_idx, pktio;
692  unsigned int i, nb_worker, if_count;
693  struct thread_arg_s *arg;
694  struct l3fwd_pktio_s *port;
695  uint8_t queue_mask[MAX_NB_PKTIO][MAX_NB_QUEUE];
696 
697  nb_worker = args->worker_count;
698  if_count = args->if_count;
699 
700  /* distribute rx queues among threads as round-robin */
701  if (!args->qconf_count) {
702  if (nb_worker > if_count) {
703  for (i = 0; i < nb_worker; i++) {
704  arg = &global->worker_args[i];
705  arg->thr_idx = i;
706  j = i % if_count;
707  port = &global->l3fwd_pktios[j];
708  arg->pktio[0].rxq[0] =
709  port->rxq_idx % port->nb_rxq;
710  arg->pktio[0].nb_rxq = 1;
711  arg->pktio[0].if_idx = j;
712  arg->nb_pktio = 1;
713  port->rxq_idx++;
714  }
715  } else {
716  for (i = 0; i < if_count; i++) {
717  j = i % nb_worker;
718  arg = &global->worker_args[j];
719  arg->thr_idx = j;
720  port = &global->l3fwd_pktios[i];
721  rxq_idx = arg->pktio[i].nb_rxq;
722  pktio = arg->nb_pktio;
723  arg->pktio[pktio].rxq[rxq_idx] =
724  port->rxq_idx % port->nb_rxq;
725  arg->pktio[pktio].nb_rxq++;
726  arg->pktio[pktio].if_idx = i;
727  arg->nb_pktio++;
728  port->rxq_idx++;
729  }
730  }
731  }
732 
733  /* distribute rx queues among threads as q argument */
734  memset(queue_mask, 0, sizeof(queue_mask));
735  for (i = 0; i < args->qconf_count; i++) {
736  struct l3fwd_qconf_s *q;
737 
738  q = &args->qconf_config[i];
739  if (q->core_idx >= nb_worker || q->if_idx >= if_count)
740  ODPH_ABORT("Error queue (%d, %d, %d), max port: %d, "
741  "max core: %d\n", q->if_idx, q->rxq_idx,
742  q->core_idx, args->if_count - 1,
743  args->worker_count - 1);
744 
745  /* check if one queue is configured twice or more */
746  if (queue_mask[q->if_idx][q->rxq_idx])
747  ODPH_ABORT("Error queue (%d, %d, %d), reconfig queue\n",
748  q->if_idx, q->rxq_idx, q->core_idx);
749  queue_mask[q->if_idx][q->rxq_idx] = 1;
750 
751  port = &global->l3fwd_pktios[q->if_idx];
752  if (port->rxq_idx < q->rxq_idx)
753  ODPH_ABORT("Error queue (%d, %d, %d), queue should be "
754  "in sequence and start from 0, queue %d\n",
755  q->if_idx, q->rxq_idx, q->core_idx,
756  q->rxq_idx);
757 
758  if (q->rxq_idx > port->nb_rxq) {
759  ODPH_ABORT("Error queue (%d, %d, %d), max queue %d\n",
760  q->if_idx, q->rxq_idx, q->core_idx,
761  port->nb_rxq - 1);
762  }
763  port->rxq_idx = q->rxq_idx + 1;
764 
765  /* put the queue into worker_args */
766  arg = &global->worker_args[q->core_idx];
767 
768  /* Check if interface already has queues configured */
769  for (j = 0; j < args->if_count; j++) {
770  if (arg->pktio[j].if_idx == q->if_idx)
771  break;
772  }
773  if (j == args->if_count)
774  j = arg->nb_pktio++;
775 
776  rxq_idx = arg->pktio[j].nb_rxq;
777  arg->pktio[j].rxq[rxq_idx] = q->rxq_idx;
778  arg->pktio[j].nb_rxq++;
779  arg->pktio[j].if_idx = q->if_idx;
780  arg->thr_idx = q->core_idx;
781  }
782  /* distribute tx queues among threads */
783  for (i = 0; i < args->worker_count; i++) {
784  arg = &global->worker_args[i];
785  for (j = 0; j < args->if_count; j++) {
786  port = &global->l3fwd_pktios[j];
787  arg->pktio[j].txq_idx =
788  port->txq_idx % port->nb_txq;
789  port->txq_idx++;
790  }
791  }
792 
793  /* config and initialize rx and tx queues. */
794  for (i = 0; i < if_count; i++) {
795  odp_pktin_queue_param_t in_queue_param;
796  odp_pktout_queue_param_t out_queue_param;
797  odp_pktin_queue_t *inq;
798  odp_pktout_queue_t *outq;
799  const char *name;
800  int nb_rxq, nb_txq;
801 
802  port = &global->l3fwd_pktios[i];
803  name = args->if_names[i];
804  odp_pktin_queue_param_init(&in_queue_param);
805  odp_pktout_queue_param_init(&out_queue_param);
806 
807  in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
808  out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
809 
810  in_queue_param.num_queues = port->rxq_idx;
811  if (port->rxq_idx > port->nb_rxq) {
812  in_queue_param.num_queues = port->nb_rxq;
813  in_queue_param.op_mode = ODP_PKTIO_OP_MT;
814  }
815 
816  in_queue_param.hash_enable = (in_queue_param.num_queues > 1) ?
817  1 : 0;
818  in_queue_param.hash_proto.proto.ipv4 = 1;
819  in_queue_param.hash_proto.proto.ipv4_tcp = 1;
820  in_queue_param.hash_proto.proto.ipv4_udp = 1;
821 
822  if (odp_pktin_queue_config(port->pktio, &in_queue_param))
823  ODPH_ABORT("Fail to config input queue for port %s\n",
824  name);
825 
826  out_queue_param.num_queues = port->txq_idx;
827  if (port->txq_idx > port->nb_txq) {
828  out_queue_param.num_queues = port->nb_txq;
829  out_queue_param.op_mode = ODP_PKTIO_OP_MT;
830  }
831  if (odp_pktout_queue_config(port->pktio, &out_queue_param))
832  ODPH_ABORT("Fail to config output queue for port %s\n",
833  name);
834 
835  inq = port->ifin;
836  nb_rxq = in_queue_param.num_queues;
837  if (odp_pktin_queue(port->pktio, inq, nb_rxq) != nb_rxq)
838  ODPH_ABORT("Fail to set pktin queue for port %s\n",
839  name);
840 
841  outq = port->ifout;
842  nb_txq = out_queue_param.num_queues;
843  if (odp_pktout_queue(port->pktio, outq, nb_txq) != nb_txq)
844  ODPH_ABORT("Fail to set pktout queue for port %s\n",
845  name);
846  }
847 }
848 
849 static void print_qconf_table(app_args_t *args)
850 {
851  unsigned int i;
852  int j, k, qid, if_idx;
853  char buf[32];
854  struct thread_arg_s *thr_arg;
855 
856  printf("Rx Queue table\n"
857  "-----------------\n"
858  "%-32s%-16s%-16s\n",
859  "port/id", "rxq", "thread");
860 
861  for (i = 0; i < args->worker_count; i++) {
862  thr_arg = &global->worker_args[i];
863  for (j = 0; j < args->if_count; j++) {
864  if (!thr_arg->pktio[j].nb_rxq)
865  continue;
866 
867  if_idx = thr_arg->pktio[j].if_idx;
868  snprintf(buf, 32, "%s/%d", args->if_names[if_idx],
869  if_idx);
870  for (k = 0; k < MAX_NB_QUEUE; k++) {
871  qid = thr_arg->pktio[j].rxq[k];
872  if (qid != INVALID_ID)
873  printf("%-32s%-16d%-16d\n", buf, qid,
874  thr_arg->thr_idx);
875  }
876  }
877  }
878  printf("\n");
879  fflush(NULL);
880 }
881 
890 static int print_speed_stats(int num_workers, int duration, int timeout)
891 {
892  uint64_t pkts = 0;
893  uint64_t pkts_prev = 0;
894  uint64_t pps;
895  uint64_t rx_drops, tx_drops;
896  uint64_t maximum_pps = 0;
897  int i;
898  int elapsed = 0;
899  int stats_enabled = 1;
900  int loop_forever = (duration == 0);
901 
902  if (timeout <= 0) {
903  stats_enabled = 0;
904  timeout = 1;
905  }
906  /* Wait for all threads to be ready*/
907  odp_barrier_wait(&global->barrier);
908 
909  do {
910  pkts = 0;
911  rx_drops = 0;
912  tx_drops = 0;
913  sleep(timeout);
914 
915  for (i = 0; i < num_workers; i++) {
916  pkts += global->worker_args[i].packets;
917  rx_drops += global->worker_args[i].rx_drops;
918  tx_drops += global->worker_args[i].tx_drops;
919  }
920  if (stats_enabled) {
921  pps = (pkts - pkts_prev) / timeout;
922  if (pps > maximum_pps)
923  maximum_pps = pps;
924  printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps,
925  maximum_pps);
926 
927  printf(" %" PRIu64 " rx drops, %" PRIu64 " tx drops\n",
928  rx_drops, tx_drops);
929 
930  pkts_prev = pkts;
931  }
932  elapsed += timeout;
933  } while (loop_forever || (elapsed < duration));
934 
935  if (stats_enabled)
936  printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
937  maximum_pps);
938 
939  return pkts > 100 ? 0 : -1;
940 }
941 
942 int main(int argc, char **argv)
943 {
944  odph_thread_t thread_tbl[MAX_NB_WORKER];
945  odph_thread_common_param_t thr_common;
946  odph_thread_param_t thr_param[MAX_NB_WORKER];
947  odp_pool_t pool;
948  odp_pool_param_t params;
949  odp_shm_t shm;
950  odp_instance_t instance;
951  odp_cpumask_t cpumask;
952  int i, j, nb_worker;
953  uint8_t mac[ODPH_ETHADDR_LEN];
954  uint8_t *dst_mac;
955  app_args_t *args;
956  struct thread_arg_s *thr_arg;
957  char *oif;
958 
959  if (odp_init_global(&instance, NULL, NULL)) {
960  printf("Error: ODP global init failed.\n");
961  exit(1);
962  }
963 
964  if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
965  printf("Error: ODP local init failed.\n");
966  exit(1);
967  }
968 
969  /* Reserve memory for args from shared mem */
970  shm = odp_shm_reserve("_appl_global_data", sizeof(global_data_t),
971  ODP_CACHE_LINE_SIZE, 0);
972  if (shm == ODP_SHM_INVALID) {
973  printf("Error: shared mem reserve failed.\n");
974  exit(EXIT_FAILURE);
975  }
976 
977  global = odp_shm_addr(shm);
978  if (global == NULL) {
979  printf("Error: shared mem alloc failed.\n");
980  exit(EXIT_FAILURE);
981  }
982 
983  memset(global, 0, sizeof(global_data_t));
984  odp_atomic_init_u32(&global->exit_threads, 0);
985  global->shm = shm;
986 
987  /* Initialize the dest mac as 2:0:0:0:0:x */
988  mac[0] = 2;
989  for (i = 0; i < MAX_NB_PKTIO; i++) {
990  mac[ODPH_ETHADDR_LEN - 1] = (uint8_t)i;
991  memcpy(global->eth_dest_mac[i].addr, mac, ODPH_ETHADDR_LEN);
992  }
993 
994  /* Initialize the thread arguments */
995  for (i = 0; i < MAX_NB_WORKER; i++) {
996  thr_arg = &global->worker_args[i];
997  for (j = 0; j < MAX_NB_PKTIO; j++) {
998  thr_arg->thr_idx = INVALID_ID;
999  thr_arg->pktio[j].txq_idx = INVALID_ID;
1000  thr_arg->pktio[j].if_idx = INVALID_ID;
1001  memset(thr_arg->pktio[j].rxq, INVALID_ID,
1002  sizeof(thr_arg->pktio[j].rxq));
1003  }
1004  }
1005 
1006  /* Parse cmdline arguments */
1007  args = &global->cmd_args;
1008  parse_cmdline_args(argc, argv, args);
1009 
1010  /* Init l3fwd table */
1011  init_fwd_db();
1012 
1013  /* Add route into table */
1014  for (i = 0; i < MAX_NB_ROUTE; i++) {
1015  if (args->route_str[i]) {
1016  create_fwd_db_entry(args->route_str[i], &oif, &dst_mac);
1017  if (oif == NULL) {
1018  printf("Error: fail to create route entry.\n");
1019  exit(1);
1020  }
1021 
1022  j = find_port_id_by_name(oif, args);
1023  if (j == -1) {
1024  printf("Error: port %s not used.\n", oif);
1025  exit(1);
1026  }
1027 
1028  if (dst_mac)
1029  args->dest_mac_changed[j] = 1;
1030  }
1031  }
1032 
1033  print_info(NO_PATH(argv[0]), args);
1034 
1035  /* Create packet pool */
1036  odp_pool_param_init(&params);
1037  params.pkt.seg_len = POOL_SEG_LEN;
1038  params.pkt.len = POOL_SEG_LEN;
1039  params.pkt.num = POOL_NUM_PKT;
1040  params.type = ODP_POOL_PACKET;
1041 
1042  pool = odp_pool_create("packet pool", &params);
1043 
1044  if (pool == ODP_POOL_INVALID) {
1045  printf("Error: packet pool create failed.\n");
1046  exit(1);
1047  }
1048 
1049  /* Resolve fwd db*/
1050  for (i = 0; i < args->if_count; i++) {
1051  struct l3fwd_pktio_s *port;
1052  char *if_name;
1053 
1054  if_name = args->if_names[i];
1055  port = &global->l3fwd_pktios[i];
1056  if (create_pktio(if_name, pool, port)) {
1057  printf("Error: create pktio %s\n", if_name);
1058  exit(1);
1059  }
1060  odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);
1061  resolve_fwd_db(if_name, i, mac);
1062  memcpy(port->mac_addr.addr, mac, ODPH_ETHADDR_LEN);
1063  }
1064  setup_fwd_db();
1065  dump_fwd_db();
1066 
1067  nb_worker = MAX_NB_WORKER;
1068  if (args->worker_count && args->worker_count < MAX_NB_WORKER)
1069  nb_worker = args->worker_count;
1070  nb_worker = odp_cpumask_default_worker(&cpumask, nb_worker);
1071  args->worker_count = nb_worker;
1072 
1073  /* Setup rx and tx queues for each port */
1074  setup_worker_qconf(args);
1075  print_qconf_table(args);
1076 
1077  /* Decide ip lookup method */
1078  if (args->hash_mode)
1079  global->fwd_func = l3fwd_pkt_hash;
1080  else
1081  global->fwd_func = l3fwd_pkt_lpm;
1082 
1083  /* Start all the available ports */
1084  for (i = 0; i < args->if_count; i++) {
1085  struct l3fwd_pktio_s *port;
1086  char *if_name;
1087  char buf[32];
1088 
1089  if_name = args->if_names[i];
1090  port = &global->l3fwd_pktios[i];
1091  /* start pktio */
1092  if (odp_pktio_start(port->pktio)) {
1093  printf("unable to start pktio: %s\n", if_name);
1094  exit(1);
1095  }
1096 
1097  sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
1098  port->mac_addr.addr[0],
1099  port->mac_addr.addr[1],
1100  port->mac_addr.addr[2],
1101  port->mac_addr.addr[3],
1102  port->mac_addr.addr[4],
1103  port->mac_addr.addr[5]);
1104  printf("start pktio: %s, mac %s\n", if_name, buf);
1105  }
1106 
1107  odp_barrier_init(&global->barrier, nb_worker + 1);
1108 
1109  odph_thread_common_param_init(&thr_common);
1110  thr_common.instance = instance;
1111  thr_common.cpumask = &cpumask;
1112 
1113  for (i = 0; i < nb_worker; i++) {
1114  odph_thread_param_init(&thr_param[i]);
1115  thr_param[i].start = run_worker;
1116  thr_param[i].arg = &global->worker_args[i];
1117  thr_param[i].thr_type = ODP_THREAD_WORKER;
1118  }
1119 
1120  memset(thread_tbl, 0, sizeof(thread_tbl));
1121  odph_thread_create(thread_tbl, &thr_common, thr_param, nb_worker);
1122 
1123  print_speed_stats(nb_worker, args->duration, PRINT_INTERVAL);
1124  odp_atomic_store_u32(&global->exit_threads, 1);
1125 
1126  /* wait for other threads to join */
1127  odph_thread_join(thread_tbl, nb_worker);
1128 
1129  /* Stop and close used pktio devices */
1130  for (i = 0; i < args->if_count; i++) {
1131  odp_pktio_t pktio = global->l3fwd_pktios[i].pktio;
1132 
1133  if (odp_pktio_stop(pktio) || odp_pktio_close(pktio)) {
1134  printf("Error: failed to close pktio\n");
1135  exit(EXIT_FAILURE);
1136  }
1137  }
1138 
1139  /* if_names share a single buffer */
1140  free(args->if_names_buf);
1141 
1142  for (i = 0; i < MAX_NB_ROUTE; i++)
1143  free(args->route_str[i]);
1144 
1145  shm = odp_shm_lookup("flow_table");
1146  if (shm != ODP_SHM_INVALID && odp_shm_free(shm) != 0) {
1147  printf("Error: shm free flow_table\n");
1148  exit(EXIT_FAILURE);
1149  }
1150  shm = odp_shm_lookup("shm_fwd_db");
1151  if (shm != ODP_SHM_INVALID && odp_shm_free(shm) != 0) {
1152  printf("Error: shm free shm_fwd_db\n");
1153  exit(EXIT_FAILURE);
1154  }
1155  shm = odp_shm_lookup("fib_lpm_sub");
1156  if (shm != ODP_SHM_INVALID && odp_shm_free(shm) != 0) {
1157  printf("Error: shm free fib_lpm_sub\n");
1158  exit(EXIT_FAILURE);
1159  }
1160 
1161  if (odp_pool_destroy(pool)) {
1162  printf("Error: pool destroy\n");
1163  exit(EXIT_FAILURE);
1164  }
1165 
1166  if (odp_shm_free(global->shm)) {
1167  printf("Error: shm free global data\n");
1168  exit(EXIT_FAILURE);
1169  }
1170 
1171  if (odp_term_local()) {
1172  printf("Error: term local\n");
1173  exit(EXIT_FAILURE);
1174  }
1175 
1176  if (odp_term_global(instance)) {
1177  printf("Error: term global\n");
1178  exit(EXIT_FAILURE);
1179  }
1180 
1181  return 0;
1182 }
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_unlikely(x)
Branch unlikely taken.
Definition: spec/hints.h:64
odp_u16be_t odp_cpu_to_be_16(uint16_t cpu16)
Convert cpu native uint16_t to 16bit big endian.
uint16_t odp_be_to_cpu_16(odp_u16be_t be16)
Convert 16bit big endian to cpu native uint16_t.
uint32_t odp_be_to_cpu_32(odp_u32be_t be32)
Convert 32bit big endian to cpu native uint32_t.
int odp_cpu_count(void)
CPU count.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
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_close(odp_pktio_t pktio)
Close a packet IO interface.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_config(odp_pktio_t pktio, const odp_pktio_config_t *config)
Configure packet IO interface options.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
Direct packet input queues.
void odp_pktout_queue_param_init(odp_pktout_queue_param_t *param)
Initialize packet output queue parameters.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
Receive packets directly from an interface input queue.
int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa)
Query packet IO interface capabilities.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
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_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIO_OP_MT
Multithread safe operation.
int odp_packet_has_ipv4(odp_packet_t pkt)
Check for IPv4.
int odp_packet_has_error(odp_packet_t pkt)
Check for all parse errors in packet.
void odp_packet_free(odp_packet_t pkt)
Free packet.
void * odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len)
Layer 2 start pointer.
void * odp_packet_l4_ptr(odp_packet_t pkt, uint32_t *len)
Layer 4 start pointer.
int odp_packet_has_tcp(odp_packet_t pkt)
Check for TCP.
void * odp_packet_l3_ptr(odp_packet_t pkt, uint32_t *len)
Layer 3 start pointer.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
int odp_packet_has_udp(odp_packet_t pkt)
Check for UDP.
@ ODP_PROTO_LAYER_ALL
All layers.
@ ODP_PROTO_LAYER_L4
Layer L4 protocols (UDP, TCP, SCTP)
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
odp_shm_t odp_shm_lookup(const char *name)
Lookup for a block of shared memory.
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.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
The OpenDataPlane API.
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_pktin_hash_proto_t hash_proto
Protocol field selection for hashing.
odp_bool_t hash_enable
Enable flow hashing.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_output_queues
Maximum number of output queues.
Packet IO configuration options.
odp_pktout_config_opt_t pktout
Packet output configuration options bit field.
odp_pktio_parser_config_t parser
Packet input parser configuration.
Packet IO parameters.
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.
Pool parameters.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
uint32_t ipv4_tcp
IPv4 addresses and TCP port numbers.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.
struct odp_pktin_hash_proto_t::@99 proto
Protocol header fields for hashing.
uint32_t ipv4
IPv4 addresses.
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.