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