27#include <odp/helper/odph_api.h>
28#include <pktio_common.h>
30#define PROG_NAME "odp_l2fwd_perf"
31#define PAIR_DELIMITER "@"
32#define IF_DELIMITER ","
37#define MAX_WORKERS ((uint32_t)(ODP_THREAD_COUNT_MAX - 1))
46#define DEF_MODE DIRECT
53#define GIGAS 1000000000U
74typedef struct pktio_s pktio_t;
76typedef struct pktio_s {
81 odph_ethaddr_t src_mac;
82 odph_ethaddr_t dst_mac;
98typedef struct prog_config_s prog_config_t;
101 worker_pktio_t pktios[MAX_IFS];
103 prog_config_t *prog_config;
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;
121 uint32_t orig_in_mtu;
122 uint32_t orig_out_mtu;
125 uint32_t num_workers;
129 uint8_t orig_is_promisc;
138 odph_ethaddr_t src_mac;
139 odph_ethaddr_t dst_mac;
146 odph_ethaddr_t src_mac;
147 odph_ethaddr_t dst_mac;
156typedef int (*run_func_t)(
void *arg);
158static prog_config_t *prog_conf;
165static void init_config(prog_config_t *config)
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;
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;
186 for (uint32_t i = 0U; i < MAX_IFS; ++i) {
187 pktio = &config->pktios[i];
191 pktio->dst_mac.addr[0U] = MAC_COMMON;
192 pktio->dst_mac.addr[5U] = i + 1U;
196static void parse_interfaces(prog_config_t *config,
const char *optarg)
198 char *tmp_str = strdup(optarg), *tmp;
201 ODPH_ABORT(
"Out of memory\n");
203 tmp = strtok(tmp_str, IF_DELIMITER);
205 while (tmp && config->num_ifs < MAX_IFS) {
209 ODPH_ABORT(
"Out of memory\n");
211 config->pktios[config->num_ifs].name = tmp;
213 tmp = strtok(NULL, IF_DELIMITER);
219static odp_bool_t parse_mac_and_port(pktio_t *pktio,
const char *pair)
221 const char *pos = strstr(pair, PAIR_DELIMITER);
227 size = pos - pair + 1U;
228 pktio->dst_name = strdup(pair + size);
230 if (pktio->dst_name == NULL)
231 ODPH_ABORT(
"Out of memory\n");
235 odph_strcpy(mac, pair, size);
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;
248static void parse_portmap(prog_config_t *config,
const char *optarg)
250 char *tmp_str = strdup(optarg);
255 ODPH_ABORT(
"Out of memory\n");
257 tmp = strtok(tmp_str, IF_DELIMITER);
259 while (tmp && i < MAX_IFS) {
260 if (parse_mac_and_port(&config->pktios[i], tmp))
263 tmp = strtok(NULL, IF_DELIMITER);
269static void print_usage(
const dynamic_defs_t *dyn_defs)
272 "Simple L2 forwarder.\n"
274 "Usage: %s [OPTIONS]\n", PROG_NAME);
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);
280 "Mandatory OPTIONS:\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"
287 "Optional OPTIONS:\n"
289 " -m, --mode Packet input mode. %u by default.\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"
309 " -l, --pkt_len Maximum size of packet buffers in packet I/O pool. %u by\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"
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);
329static parse_result_t check_options(prog_config_t *config)
332 uint32_t max_workers;
334 if (config->num_ifs == 0U) {
335 ODPH_ERR(
"Invalid number of interfaces: %u (min: 1, max: %u)\n", config->num_ifs,
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);
346 if (config->burst_size == 0U) {
347 ODPH_ERR(
"Invalid burst size: %u (min: 1)\n", config->burst_size);
352 ODPH_ERR(
"Error querying pool capabilities\n");
356 if (config->num_pkts == 0U ||
358 ODPH_ERR(
"Invalid pool packet count: %u (min: 1, max: %u)\n", config->num_pkts,
363 if (config->pkt_len == 0U ||
365 ODPH_ERR(
"Invalid pool packet length: %u (min: 1, max: %u)\n", config->pkt_len,
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,
383static parse_result_t parse_options(
int argc,
char **argv, prog_config_t *config)
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' },
404 static const char *shortopts =
"i:m:p:b:n:l:M:Pt:sc:h";
409 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
416 parse_interfaces(config, optarg);
419 config->mode = atoi(optarg);
422 parse_portmap(config, optarg);
425 config->burst_size = atoi(optarg);
428 config->num_pkts = atoi(optarg);
431 config->pkt_len = atoi(optarg);
434 config->mtu = atoi(optarg);
437 config->is_promisc = 1U;
440 config->runtime = atoi(optarg);
443 config->is_mac_mod =
false;
446 config->wait_sec = atoi(optarg);
449 config->num_workers = atoi(optarg);
452 print_usage(&config->dyn_defs);
456 print_usage(&config->dyn_defs);
461 return check_options(config);
464static parse_result_t setup_program(
int argc,
char **argv, prog_config_t *config)
466 struct sigaction action = { .sa_handler = terminate };
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");
478 return parse_options(argc, argv, config);
481static void bind_destinations(pktio_t *pktios, uint8_t num)
483 pktio_t *pktio_src, *pktio_dst;
485 for (uint8_t i = 0U; i < num; ++i) {
486 pktio_src = &pktios[i];
488 for (uint8_t j = 0U; j < num; ++j) {
489 pktio_dst = &pktios[j];
491 if (pktio_src->dst_name != NULL &&
492 strcmp(pktio_src->dst_name, pktio_dst->name) == 0) {
493 pktio_src->dst = pktio_dst;
496 pktio_dst->dst_mac = pktio_src->src_mac;
503static worker_pktio_t *get_destination(
const pktio_t *pktio, worker_pktio_t *pktios, uint8_t num)
505 worker_pktio_t *w_pktio;
507 for (uint8_t i = 0U; i < num; ++i) {
508 w_pktio = &pktios[i];
510 if (pktio == w_pktio->pktio->dst)
517static void bind_workers(prog_config_t *config)
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;
525 if (num_workers >= num_ifs) {
539 for (; i < max; ++i) {
543 worker = &config->worker_config[*work_idx_ptr];
544 worker->pktios[worker->num_ifs++].pktio = &config->pktios[*pktio_idx_ptr];
550 for (i = 0U; i < num_workers; ++i) {
551 worker = &config->worker_config[i];
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;
562 for (i = 0U; i < num_ifs; ++i) {
563 pktio = &config->pktios[i];
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);
569 if (w_pktio != NULL) {
571 w_pktio->tx_poll = pktio->num_out_qs;
577static odp_bool_t setup_config(prog_config_t *config)
579 if (config->mode != DIRECT) {
581 ODPH_ERR(
"Error initializing scheduler\n");
586 bind_destinations(config->pktios, config->num_ifs);
587 bind_workers(config);
589 for (uint32_t i = 0U; i < config->num_workers; ++i)
590 config->worker_config[i].prog_config = config;
609static odp_bool_t setup_pktios(prog_config_t *config)
616 uint32_t num_input_qs, num_output_qs;
620 const char *names[MAX_IFS];
625 pool_param.
pkt.
len = config->pkt_len;
626 pool_param.
pkt.
num = config->num_pkts;
631 ODPH_ERR(
"Error creating packet I/O pool\n");
635 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
636 pktio = &config->pktios[i];
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);
645 ODPH_ERR(
"Error opening packet I/O (%s)\n", pktio->name);
653 ODPH_ERR(
"Error configuring packet I/O (%s)\n", pktio->name);
658 ODPH_ERR(
"Error querying packet I/O capabilities (%s)\n", pktio->name);
663 num_input_qs = config->mode == DIRECT ? pktio->num_in_qs : config->num_workers;
665 num_input_qs = ODPH_MIN(num_input_qs, MAX_QS);
667 if (num_input_qs > 1) {
672 if (config->mode == DIRECT) {
673 if (num_input_qs == pktio->num_in_qs)
676 ODPH_ERR(
"Warning: not enough input queues supported for MT "
677 "unsafe operation (%s)\n", pktio->name);
683 pktio->num_in_qs = num_input_qs;
686 ODPH_ERR(
"Error configuring packet I/O input queues (%s)\n", pktio->name);
690 if (config->mode == DIRECT &&
693 ODPH_ERR(
"Error querying packet I/O input queues (%s)\n", pktio->name);
697 num_output_qs = pktio->num_out_qs;
699 if (num_output_qs > 0U) {
701 num_output_qs = config->mode == DIRECT ?
702 num_output_qs : config->num_workers;
704 num_output_qs = ODPH_MIN(num_output_qs, MAX_QS);
706 if (num_output_qs >= pktio->num_out_qs)
709 ODPH_ERR(
"Warning: not enough output queues supported for MT "
710 "unsafe operation (%s)\n", pktio->name);
713 pktio->num_out_qs = num_output_qs;
716 ODPH_ERR(
"Error configuring packet I/O output queues (%s)\n",
722 (
int)num_output_qs) {
723 ODPH_ERR(
"Error querying packet I/O output queues (%s)\n",
729 if (config->mtu > 0U) {
732 ODPH_ERR(
"MTU setting not supported (%s)\n", pktio->name);
741 ODPH_ERR(
"Invalid MTU requested: %u (input min: %u, input max: %u,"
742 " output min: %u, output max: %u, %s)\n",
754 ODPH_ERR(
"Error setting MTU (%s)\n", pktio->name);
759 if (config->is_promisc) {
761 config->is_promisc = 0U;
762 ODPH_ERR(
"Promiscuous mode setting not supported (%s)\n",
770 config->is_promisc = 0U;
771 ODPH_ERR(
"Error setting promiscuous mode (%s)\n", pktio->name);
775 config->orig_is_promisc = ret < 0 ? 1U : (uint8_t)ret;
779 ODPH_ERR(
"Error querying MAC address (%s)\n", pktio->name);
784 ODPH_ERR(
"Error starting packet I/O (%s)\n", pktio->name);
788 pktios[i] = pktio->handle;
789 names[i] = pktio->name;
793 if (config->wait_sec && pktio_common_check_link_status_wait(pktios, names, config->num_ifs,
794 config->wait_sec) == -1)
797 pktio_common_print_link_info_multi(pktios, names, config->num_ifs);
802static void print_mac(odph_ethaddr_t mac)
804 for (
int i = 0; i < ODPH_ETHADDR_LEN; ++i)
805 printf(
"%02x%s", mac.addr[i], i < ODPH_ETHADDR_LEN - 1 ?
":" :
"");
810static void print_rx_workers(pktio_t *pktio, prog_config_t *config)
812 worker_config_t *worker;
813 const uint32_t num_workers = config->num_workers;
814 uint32_t ids[num_workers], num_ids = 0U;
816 if (config->mode != DIRECT) {
817 printf(
"(scheduled)\n");
821 for (uint32_t i = 0U; i < num_workers; ++i) {
822 worker = &config->worker_config[i];
824 for (uint8_t j = 0U; j < worker->num_ifs; ++j) {
825 if (pktio == worker->pktios[j].pktio) {
832 for (uint32_t i = 0U; i < num_ids; ++i)
833 printf(
"%u%s", ids[i], i < num_ids - 1U ?
", " :
"");
838static void print_tx_workers(pktio_t *pktio, prog_config_t *config)
840 worker_config_t *worker;
841 const uint32_t num_workers = config->num_workers;
842 uint32_t ids[num_workers], num_ids = 0U;
844 if (config->mode != DIRECT) {
845 printf(
"(scheduled)\n");
849 for (uint32_t i = 0U; i < num_workers; ++i) {
850 worker = &config->worker_config[i];
852 if (get_destination(pktio, worker->pktios, worker->num_ifs) != NULL)
859 for (uint32_t i = 0U; i < num_ids; ++i)
860 printf(
"%u%s", ids[i], i < num_ids - 1U ?
", " :
"");
865static odp_bool_t print_summary(prog_config_t *config)
869 printf(
"\nprogram options:\n================\n\n"
872 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
873 pktio = &config->pktios[i];
875 " handle: 0x%" PRIx64
"\n"
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,
884 printf(
" rx worker IDs: ");
885 print_rx_workers(pktio, config);
886 printf(
" tx worker IDs: ");
887 print_tx_workers(pktio, config);
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);
900 if (config->mtu > 0U)
901 printf(
" MTU: %u\n", config->mtu);
903 printf(
" promiscuous mode: %s\n", config->is_promisc ?
"enabled" :
"disabled");
905 if (config->runtime > 0U)
906 printf(
" runtime: %u sec\n", config->runtime);
908 printf(
" runtime: infinite\n");
910 printf(
" MAC modification: %s\n"
911 " workers: %u\n\n", config->is_mac_mod ?
"enabled" :
"disabled",
912 config->num_workers);
917static dir_fwd_tbl_t build_dir_fwd_tbl(worker_pktio_t *pktios, uint8_t num_ifs)
919 worker_pktio_t *w_pktio;
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;
935static int run_direct_single_if(
void *args)
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);
945 int num_recv, num_sent, diff;
947 uint64_t tx = 0U, tx_drops = 0U;
948 stats_t *stats = &config->stats;
962 num_sent = num_sent < 0 ? 0 : num_sent;
963 diff = num_recv - num_sent;
973 stats->tx_drops = tx_drops;
979static int run_direct_single_if_mac_mod(
void *args)
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);
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;
993 uint64_t tx = 0U, tx_drops = 0U;
994 stats_t *stats = &config->stats;
1005 for (
int i = 0U; i < num_recv; ++i) {
1014 num_sent = num_sent < 0 ? 0 : num_sent;
1015 diff = num_recv - num_sent;
1025 stats->tx_drops = tx_drops;
1031static int run_direct_multi_if(
void *args)
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);
1043 int num_recv, num_sent, diff;
1045 uint64_t tx = 0U, tx_drops = 0U;
1046 stats_t *stats = &config->stats;
1052 pktin = tbl.tbl[i].pktin;
1053 pktout = tbl.tbl[i].pktout;
1066 num_sent = num_sent < 0 ? 0 : num_sent;
1067 diff = num_recv - num_sent;
1077 stats->tx_drops = tx_drops;
1083static int run_direct_multi_if_mac_mod(
void *args)
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);
1095 int num_recv, num_sent, diff;
1096 odph_ethaddr_t src_mac, dst_mac;
1099 uint64_t tx = 0U, tx_drops = 0U;
1100 stats_t *stats = &config->stats;
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;
1119 for (
int j = 0U; j < num_recv; ++j) {
1128 num_sent = num_sent < 0 ? 0 : num_sent;
1129 diff = num_recv - num_sent;
1139 stats->tx_drops = tx_drops;
1145static scd_fwd_tbl_t build_scd_fwd_tbl(
const pktio_t *pktios, uint8_t num_ifs,
int thread_idx)
1147 const pktio_t *pktio;
1151 for (uint8_t i = 0U; i < num_ifs; ++i) {
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;
1162static void drain_events(
void)
1176static int run_scheduled(
void *args)
1178 worker_config_t *config = args;
1179 prog_config_t *prog_config = config->prog_config;
1182 const uint8_t num_ifs = prog_config->num_ifs;
1183 const uint32_t burst_size = prog_config->burst_size;
1185 int num_recv, num_sent, diff;
1187 const scd_fwd_tbl_t tbl = build_scd_fwd_tbl(prog_config->pktios, num_ifs,
odp_thread_id());
1189 uint64_t tx = 0U, tx_drops = 0U;
1190 stats_t *stats = &config->stats;
1206 num_sent = num_sent < 0 ? 0 : num_sent;
1207 diff = num_recv - num_sent;
1217 stats->tx_drops = tx_drops;
1230static int run_scheduled_mac_mod(
void *args)
1232 worker_config_t *config = args;
1233 prog_config_t *prog_config = config->prog_config;
1236 const uint8_t num_ifs = prog_config->num_ifs;
1237 const uint32_t burst_size = prog_config->burst_size;
1239 int num_recv, num_sent, idx, diff;
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;
1245 uint64_t tx = 0U, tx_drops = 0U;
1246 stats_t *stats = &config->stats;
1259 src_mac = tbl.tbl[idx].src_mac;
1260 dst_mac = tbl.tbl[idx].dst_mac;
1261 pktout = tbl.tbl[idx].pktout;
1263 for (
int i = 0U; i < num_recv; ++i) {
1272 num_sent = num_sent < 0 ? 0 : num_sent;
1273 diff = num_recv - num_sent;
1283 stats->tx_drops = tx_drops;
1296static run_func_t get_run_func(
const prog_config_t *config,
const worker_config_t *worker)
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;
1303 return worker->num_ifs == 1U ?
1304 run_direct_single_if : run_direct_multi_if;
1306 return config->is_mac_mod ? run_scheduled_mac_mod : run_scheduled;
1310static odp_bool_t setup_workers(prog_config_t *config)
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;
1318 odph_thread_common_param_init(&thr_common);
1319 thr_common.instance = config->odp_instance;
1320 thr_common.cpumask = &config->worker_mask;
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);
1328 thr_param->arg = worker;
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");
1342static odp_bool_t setup_test(prog_config_t *config)
1344 return setup_config(config) && setup_pktios(config) && print_summary(config) &&
1345 setup_workers(config);
1348static void run_control(prog_config_t *config)
1350 if (config->runtime > 0U) {
1351 sleep(config->runtime);
1359static void stop_test(prog_config_t *config)
1361 const pktio_t *pktio;
1363 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
1364 pktio = &config->pktios[i];
1371 (void)odph_thread_join(config->thread_tbl, config->num_workers);
1374static void print_humanised(uint64_t value)
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);
1383 printf(
"(%" PRIu64
" pps)", value);
1386static void print_stats(
const prog_config_t *config)
1388 const stats_t *stats;
1389 uint64_t tot_tx = 0U, tot_tx_drops = 0U, pps, tot_pps = 0U;
1391 printf(
"L2 forwarding done:\n"
1392 "===================\n\n");
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;
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,
1406 print_humanised(pps);
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);
1419static void teardown(
const prog_config_t *config)
1421 const pktio_t *pktio;
1423 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
1424 pktio = &config->pktios[i];
1427 if (config->is_promisc != config->orig_is_promisc)
1429 config->orig_is_promisc);
1431 if (config->mtu > 0U && config->orig_in_mtu > 0U &&
1432 config->orig_out_mtu > 0U)
1434 config->orig_out_mtu);
1440 free(pktio->dst_name);
1447int main(
int argc,
char **argv)
1449 odph_helper_options_t odph_opts;
1453 int ret = EXIT_SUCCESS;
1454 parse_result_t parse_res;
1456 argc = odph_parse_options(argc, argv);
1458 if (odph_options(&odph_opts) == -1) {
1459 ODPH_ERR(
"Error while reading ODP helper options, exiting\n");
1464 init_param.
mem_model = odph_opts.mem_model;
1467 ODPH_ERR(
"ODP global init failed, exiting\n");
1472 ODPH_ERR(
"ODP local init failed, exiting\n");
1476 shm_cfg =
odp_shm_reserve(PROG_NAME
"_cfg",
sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
1480 ODPH_ERR(
"Error reserving shared memory\n");
1487 if (prog_conf == NULL) {
1488 ODPH_ERR(
"Error resolving shared memory address\n");
1493 memset(prog_conf, 0,
sizeof(*prog_conf));
1495 parse_res = setup_program(argc, argv, prog_conf);
1497 if (parse_res == PRS_NOK) {
1502 if (parse_res == PRS_TERM) {
1507 if (!setup_test(prog_conf)) {
1512 run_control(prog_conf);
1513 stop_test(prog_conf);
1514 print_stats(prog_conf);
1517 teardown(prog_conf);
1523 ODPH_ERR(
"ODP local terminate failed, exiting\n");
1528 ODPH_ERR(
"ODP global terminate failed, exiting\n");
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.
#define ODP_UNUSED
Intentionally unused variables of functions.
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.
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.
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.