API Reference Manual 1.51.0
Loading...
Searching...
No Matches
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
43struct 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
54struct 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
60struct 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
74typedef 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
88typedef 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
104static global_data_t *global;
105
106static 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
154static 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
183static 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
194static 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
236static 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
269static 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
294static int run_worker(void *arg)
295{
296 int if_idx;
297 struct thread_arg_s *thr_arg = arg;
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
384static 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 */
400static 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
423einval_error:
424 errno = EINVAL;
425 return -1;
426}
427
428static 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
484static 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
516static 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
655out:
656 print_usage(argv[0]);
657 exit(EXIT_FAILURE);
658}
659
660static 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
689static 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;
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
849static 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
890static 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
942int 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_l3_ptr(odp_packet_t pkt, uint32_t *len)
Layer 3 start pointer.
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.
int odp_packet_has_tcp(odp_packet_t pkt)
Check for TCP.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
void * odp_packet_l4_ptr(odp_packet_t pkt, uint32_t *len)
Layer 4 start pointer.
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.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
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.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
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...
uint32_t ipv4_tcp
IPv4 addresses and TCP port numbers.
struct odp_pktin_hash_proto_t::@107 proto
Protocol header fields for hashing.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.
uint32_t ipv4
IPv4 addresses.
struct odp_pktout_config_opt_t::@109 bit
Option flags for packet output.
uint64_t no_packet_refs
Packet references not used on packet output.