27 #include <odp/helper/odph_api.h>
29 #define PROG_NAME "odp_l2fwd_perf"
30 #define PAIR_DELIMITER "@"
31 #define IF_DELIMITER ","
32 #define MAC_COMMON 0x2
36 #define MAX_WORKERS ((uint32_t)(ODP_THREAD_COUNT_MAX - 1))
45 #define DEF_MODE DIRECT
47 #define DEF_CNT 32768U
49 #define DEF_RUNTIME 0U
50 #define DEF_WORKERS 1U
52 #define GIGAS 1000000000U
53 #define MEGAS 1000000U
73 typedef struct pktio_s pktio_t;
75 typedef struct pktio_s {
80 odph_ethaddr_t src_mac;
81 odph_ethaddr_t dst_mac;
97 typedef struct prog_config_s prog_config_t;
100 worker_pktio_t pktios[MAX_IFS];
102 prog_config_t *prog_config;
106 typedef struct prog_config_s {
107 odph_thread_t thread_tbl[MAX_WORKERS];
108 worker_config_t worker_config[MAX_WORKERS];
109 pktio_t pktios[MAX_IFS];
110 dynamic_defs_t dyn_defs;
120 uint32_t orig_in_mtu;
121 uint32_t orig_out_mtu;
124 uint32_t num_workers;
127 uint8_t orig_is_promisc;
136 odph_ethaddr_t src_mac;
137 odph_ethaddr_t dst_mac;
144 odph_ethaddr_t src_mac;
145 odph_ethaddr_t dst_mac;
149 typedef int (*run_func_t)(
void *arg);
151 static prog_config_t *prog_conf;
158 static void init_config(prog_config_t *config)
164 config->dyn_defs.num_pkts = pool_capa.
pkt.
max_num > 0U ?
165 ODPH_MIN(pool_capa.
pkt.
max_num, DEF_CNT) : DEF_CNT;
166 config->dyn_defs.pkt_len = pool_capa.
pkt.
max_len > 0U ?
167 ODPH_MIN(pool_capa.
pkt.
max_len, DEF_LEN) : DEF_LEN;
171 config->burst_size = DEF_BURST;
172 config->num_pkts = config->dyn_defs.num_pkts;
173 config->pkt_len = config->dyn_defs.pkt_len;
174 config->runtime = DEF_RUNTIME;
175 config->num_workers = DEF_WORKERS;
176 config->mode = DEF_MODE;
177 config->is_mac_mod = 1U;
179 for (uint32_t i = 0U; i < MAX_IFS; ++i) {
180 pktio = &config->pktios[i];
184 pktio->dst_mac.addr[0U] = MAC_COMMON;
185 pktio->dst_mac.addr[5U] = i + 1U;
189 static void parse_interfaces(prog_config_t *config,
const char *optarg)
191 char *tmp_str = strdup(optarg), *tmp;
194 ODPH_ABORT(
"Out of memory\n");
196 tmp = strtok(tmp_str, IF_DELIMITER);
198 while (tmp && config->num_ifs < MAX_IFS) {
202 ODPH_ABORT(
"Out of memory\n");
204 config->pktios[config->num_ifs].name = tmp;
206 tmp = strtok(NULL, IF_DELIMITER);
212 static odp_bool_t parse_mac_and_port(pktio_t *pktio,
const char *pair)
214 const char *pos = strstr(pair, PAIR_DELIMITER);
220 size = pos - pair + 1U;
221 pktio->dst_name = strdup(pair + size);
223 if (pktio->dst_name == NULL)
224 ODPH_ABORT(
"Out of memory\n");
228 odph_strcpy(mac, pair, size);
231 if (odph_eth_addr_parse(&pktio->src_mac, mac) < 0) {
232 ODPH_ERR(
"Warning: unable to parse portmap entry (%s)\n", pktio->dst_name);
233 free(pktio->dst_name);
234 pktio->dst_name = NULL;
241 static void parse_portmap(prog_config_t *config,
const char *optarg)
243 char *tmp_str = strdup(optarg);
248 ODPH_ABORT(
"Out of memory\n");
250 tmp = strtok(tmp_str, IF_DELIMITER);
252 while (tmp && i < MAX_IFS) {
253 if (parse_mac_and_port(&config->pktios[i], tmp))
256 tmp = strtok(NULL, IF_DELIMITER);
262 static void print_usage(
const dynamic_defs_t *dyn_defs)
265 "Simple L2 forwarder.\n"
267 "Usage: %s [OPTIONS]\n", PROG_NAME);
270 " %s -i eth0,eth1 -p 11:22:33:44:55:66@eth1,66:55:44:33:22:11@eth0 -b 16\n",
271 PROG_NAME, PROG_NAME);
273 "Mandatory OPTIONS:\n"
275 " -i, --interfaces Ethernet interfaces for packet I/O, '%s'-separated, no\n"
276 " whitespaces anywhere. By default, packets are looped back.\n"
277 " This can be overridden with '--portmap'. Maximum\n"
278 " interface count is %u.\n"
280 "Optional OPTIONS:\n"
282 " -m, --mode Packet input mode. %u by default.\n"
284 " 1: scheduled with parallel queues\n"
285 " 2: scheduled with atomic queues\n"
286 " 3: scheduled with ordered queues\n"
287 " Queue per direction per worker is always attempted. Output\n"
288 " is always in direct mode.\n"
289 " -p, --portmap List of destination MAC address and port pairs for passed\n"
290 " interfaces, MAC address and the port should be\n"
291 " '%s'-separated, the pairs '%s'-separated, no whitespaces\n"
292 " anywhere. Ordering follows the '--interface' option, e.g.\n"
293 " passing '-i eth0%seth1' and\n"
294 " '-p 11:22:33:44:55:66%seth1%s66:55:44:33:22:11%seth0' would\n"
295 " result in eth0 sending packets to eth1 with source MAC of\n"
296 " port eth0 and destination MAC of '11:22:33:44:55:66' and\n"
297 " eth1 to eth0 with source MAC of eth1 and destination MAC of\n"
298 " '66:55:44:33:22:11'.\n"
299 " -b, --burst_rx Receive burst size. %u by default.\n"
300 " -n, --num_pkts Number of packet buffers allocated for packet I/O pool.\n"
302 " -l, --pkt_len Maximum size of packet buffers in packet I/O pool. %u by\n"
304 " -M, --mtu Interface MTU in bytes. Interface specific by default.\n"
305 " -P, --promisc_mode Enable promiscuous mode.\n"
306 " -t, --time Time in seconds to run. 0 means infinite. %u by default.\n"
307 " -s, --no_mac_mod Disable source and destination MAC address modification.\n"
308 " -c, --worker_count Number of workers. Workers are assigned to handle\n"
309 " interfaces in round-robin fashion. E.g. with two interfaces\n"
310 " eth0 and eth1 and with 5 workers, eth0 would be handled by\n"
311 " worker indexes 0, 2 and 4 and eth1 by worker indexes 1 and\n"
312 " 3. Assignment may be affected by '--portmap'. %u workers by\n"
314 " -h, --help This help.\n"
315 "\n", IF_DELIMITER, MAX_IFS, DEF_MODE, PAIR_DELIMITER, IF_DELIMITER, IF_DELIMITER,
316 PAIR_DELIMITER, IF_DELIMITER, PAIR_DELIMITER, DEF_BURST, dyn_defs->num_pkts,
317 dyn_defs->pkt_len, DEF_RUNTIME, DEF_WORKERS);
320 static parse_result_t check_options(prog_config_t *config)
323 uint32_t max_workers;
325 if (config->num_ifs == 0U) {
326 ODPH_ERR(
"Invalid number of interfaces: %u (min: 1, max: %u)\n", config->num_ifs,
331 if (config->mode != DIRECT && config->mode != SCHED_PARALLEL &&
332 config->mode != SCHED_ATOMIC && config->mode != SCHED_ORDERED) {
333 ODPH_ERR(
"Invalid packet input mode: %u\n", config->mode);
337 if (config->burst_size == 0U) {
338 ODPH_ERR(
"Invalid burst size: %u (min: 1)\n", config->burst_size);
343 ODPH_ERR(
"Error querying pool capabilities\n");
347 if (config->num_pkts == 0U ||
349 ODPH_ERR(
"Invalid pool packet count: %u (min: 1, max: %u)\n", config->num_pkts,
354 if (config->pkt_len == 0U ||
356 ODPH_ERR(
"Invalid pool packet length: %u (min: 1, max: %u)\n", config->pkt_len,
363 if (config->num_workers == 0U || config->num_workers > max_workers) {
364 ODPH_ERR(
"Invalid worker count: %u (min: 1, max: %u)\n", config->num_workers,
374 static parse_result_t parse_options(
int argc,
char **argv, prog_config_t *config)
378 static const struct option longopts[] = {
379 {
"interfaces", required_argument, NULL,
'i' },
380 {
"mode", required_argument, NULL,
'm' },
381 {
"portmap", required_argument, NULL,
'p' },
382 {
"burst_rx", required_argument, NULL,
'b' },
383 {
"num_pkts", required_argument, NULL,
'n' },
384 {
"pkt_len", required_argument, NULL,
'l' },
385 {
"mtu", required_argument, NULL,
'M' },
386 {
"promisc_mode", no_argument, NULL,
'P' },
387 {
"time", required_argument, NULL,
't' },
388 {
"no_mac_mod", no_argument, NULL,
's' },
389 {
"worker_count", required_argument, NULL,
'c' },
390 {
"help", no_argument, NULL,
'h' },
394 static const char *shortopts =
"i:m:p:b:n:l:M:Pt:sc:h";
399 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
406 parse_interfaces(config, optarg);
409 config->mode = atoi(optarg);
412 parse_portmap(config, optarg);
415 config->burst_size = atoi(optarg);
418 config->num_pkts = atoi(optarg);
421 config->pkt_len = atoi(optarg);
424 config->mtu = atoi(optarg);
427 config->is_promisc = 1U;
430 config->runtime = atoi(optarg);
433 config->is_mac_mod =
false;
436 config->num_workers = atoi(optarg);
439 print_usage(&config->dyn_defs);
443 print_usage(&config->dyn_defs);
448 return check_options(config);
451 static parse_result_t setup_program(
int argc,
char **argv, prog_config_t *config)
453 struct sigaction action = { .sa_handler = terminate };
457 if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
458 sigaddset(&action.sa_mask, SIGTERM) == -1 ||
459 sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
460 sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
461 ODPH_ERR(
"Error installing signal handler\n");
465 return parse_options(argc, argv, config);
468 static void bind_destinations(pktio_t *pktios, uint8_t num)
470 pktio_t *pktio_src, *pktio_dst;
472 for (uint8_t i = 0U; i < num; ++i) {
473 pktio_src = &pktios[i];
475 for (uint8_t j = 0U; j < num; ++j) {
476 pktio_dst = &pktios[j];
478 if (pktio_src->dst_name != NULL &&
479 strcmp(pktio_src->dst_name, pktio_dst->name) == 0) {
480 pktio_src->dst = pktio_dst;
483 pktio_dst->dst_mac = pktio_src->src_mac;
490 static worker_pktio_t *get_destination(
const pktio_t *pktio, worker_pktio_t *pktios, uint8_t num)
492 worker_pktio_t *w_pktio;
494 for (uint8_t i = 0U; i < num; ++i) {
495 w_pktio = &pktios[i];
497 if (pktio == w_pktio->pktio->dst)
504 static void bind_workers(prog_config_t *config)
506 const uint32_t num_workers = config->num_workers, num_ifs = config->num_ifs;
507 uint32_t max, min, i = 0U, j = 0U, *work_idx_ptr, *pktio_idx_ptr;
508 worker_config_t *worker;
509 worker_pktio_t *w_pktio;
512 if (num_workers >= num_ifs) {
526 for (; i < max; ++i) {
530 worker = &config->worker_config[*work_idx_ptr];
531 worker->pktios[worker->num_ifs++].pktio = &config->pktios[*pktio_idx_ptr];
537 for (i = 0U; i < num_workers; ++i) {
538 worker = &config->worker_config[i];
540 for (j = 0U; j < worker->num_ifs; ++j) {
541 w_pktio = &worker->pktios[j];
542 ++w_pktio->pktio->num_in_qs;
543 w_pktio->rx_poll = w_pktio->pktio->num_in_qs;
549 for (i = 0U; i < num_ifs; ++i) {
550 pktio = &config->pktios[i];
552 for (j = 0U; j < num_workers; ++j) {
553 worker = &config->worker_config[j];
554 w_pktio = get_destination(pktio, worker->pktios, worker->num_ifs);
556 if (w_pktio != NULL) {
558 w_pktio->tx_poll = pktio->num_out_qs;
564 static odp_bool_t setup_config(prog_config_t *config)
566 if (config->mode != DIRECT) {
568 ODPH_ERR(
"Error initializing scheduler\n");
573 bind_destinations(config->pktios, config->num_ifs);
574 bind_workers(config);
576 for (uint32_t i = 0U; i < config->num_workers; ++i)
577 config->worker_config[i].prog_config = config;
596 static odp_bool_t setup_pktios(prog_config_t *config)
603 uint32_t num_input_qs, num_output_qs;
610 pool_param.
pkt.
len = config->pkt_len;
611 pool_param.
pkt.
num = config->num_pkts;
616 ODPH_ERR(
"Error creating packet I/O pool\n");
620 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
621 pktio = &config->pktios[i];
623 pktio_param.
in_mode = config->mode == DIRECT ?
625 pktio_param.
out_mode = pktio->num_out_qs > 0U ?
627 pktio->handle =
odp_pktio_open(pktio->name, config->pool, &pktio_param);
630 ODPH_ERR(
"Error opening packet I/O (%s)\n", pktio->name);
638 ODPH_ERR(
"Error configuring packet I/O (%s)\n", pktio->name);
643 ODPH_ERR(
"Error querying packet I/O capabilities (%s)\n", pktio->name);
648 num_input_qs = config->mode == DIRECT ? pktio->num_in_qs : config->num_workers;
650 num_input_qs = ODPH_MIN(num_input_qs, MAX_QS);
652 if (num_input_qs > 1) {
657 if (config->mode == DIRECT) {
658 if (num_input_qs == pktio->num_in_qs)
661 ODPH_ERR(
"Warning: not enough input queues supported for MT "
662 "unsafe operation (%s)\n", pktio->name);
668 pktio->num_in_qs = num_input_qs;
671 ODPH_ERR(
"Error configuring packet I/O input queues (%s)\n", pktio->name);
675 if (config->mode == DIRECT &&
678 ODPH_ERR(
"Error querying packet I/O input queues (%s)\n", pktio->name);
682 num_output_qs = pktio->num_out_qs;
684 if (num_output_qs > 0U) {
686 num_output_qs = config->mode == DIRECT ?
687 num_output_qs : config->num_workers;
689 num_output_qs = ODPH_MIN(num_output_qs, MAX_QS);
691 if (num_output_qs >= pktio->num_out_qs)
694 ODPH_ERR(
"Warning: not enough output queues supported for MT "
695 "unsafe operation (%s)\n", pktio->name);
698 pktio->num_out_qs = num_output_qs;
701 ODPH_ERR(
"Error configuring packet I/O output queues (%s)\n",
707 (
int)num_output_qs) {
708 ODPH_ERR(
"Error querying packet I/O output queues (%s)\n",
714 if (config->mtu > 0U) {
717 ODPH_ERR(
"MTU setting not supported (%s)\n", pktio->name);
726 ODPH_ERR(
"Invalid MTU requested: %u (input min: %u, input max: %u,"
727 " output min: %u, output max: %u, %s)\n",
739 ODPH_ERR(
"Error setting promiscuous mode (%s)\n", pktio->name);
744 if (config->is_promisc) {
746 config->is_promisc = 0U;
747 ODPH_ERR(
"Promiscuous mode setting not supported (%s)\n",
755 config->is_promisc = 0U;
756 ODPH_ERR(
"Error setting promiscuous mode (%s)\n", pktio->name);
760 config->orig_is_promisc = ret < 0 ? 1U : (uint8_t)ret;
764 ODPH_ERR(
"Error querying MAC address (%s)\n", pktio->name);
769 ODPH_ERR(
"Error starting packet I/O (%s)\n", pktio->name);
777 static void print_mac(odph_ethaddr_t mac)
779 for (
int i = 0; i < ODPH_ETHADDR_LEN; ++i)
780 printf(
"%02x%s", mac.addr[i], i < ODPH_ETHADDR_LEN - 1 ?
":" :
"");
785 static void print_rx_workers(pktio_t *pktio, prog_config_t *config)
787 worker_config_t *worker;
788 const uint32_t num_workers = config->num_workers;
789 uint32_t ids[num_workers], num_ids = 0U;
791 if (config->mode != DIRECT) {
792 printf(
"(scheduled)\n");
796 for (uint32_t i = 0U; i < num_workers; ++i) {
797 worker = &config->worker_config[i];
799 for (uint8_t j = 0U; j < worker->num_ifs; ++j) {
800 if (pktio == worker->pktios[j].pktio) {
807 for (uint32_t i = 0U; i < num_ids; ++i)
808 printf(
"%u%s", ids[i], i < num_ids - 1U ?
", " :
"");
813 static void print_tx_workers(pktio_t *pktio, prog_config_t *config)
815 worker_config_t *worker;
816 const uint32_t num_workers = config->num_workers;
817 uint32_t ids[num_workers], num_ids = 0U;
819 if (config->mode != DIRECT) {
820 printf(
"(scheduled)\n");
824 for (uint32_t i = 0U; i < num_workers; ++i) {
825 worker = &config->worker_config[i];
827 if (get_destination(pktio, worker->pktios, worker->num_ifs) != NULL)
834 for (uint32_t i = 0U; i < num_ids; ++i)
835 printf(
"%u%s", ids[i], i < num_ids - 1U ?
", " :
"");
840 static odp_bool_t print_summary(prog_config_t *config)
844 printf(
"\nprogram options:\n================\n\n"
847 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
848 pktio = &config->pktios[i];
850 " handle: 0x%" PRIx64
"\n"
852 print_mac(pktio->src_mac);
853 printf(
" dst MAC: ");
854 print_mac(pktio->dst_mac);
855 printf(
" dst name: %s\n"
856 " input queues: %u\n"
857 " output queues: %u\n", pktio->dst->name, pktio->num_in_qs,
859 printf(
" rx worker IDs: ");
860 print_rx_workers(pktio, config);
861 printf(
" tx worker IDs: ");
862 print_tx_workers(pktio, config);
869 " packet length: %u\n", config->mode == DIRECT ?
870 "direct" : config->mode == SCHED_PARALLEL ?
871 "scheduled-parallel" : config->mode == SCHED_ATOMIC ?
872 "scheduled-atomic" :
"scheduled-ordered",
873 config->burst_size, config->num_pkts, config->pkt_len);
875 if (config->mtu > 0U)
876 printf(
" MTU: %u\n", config->mtu);
878 printf(
" promiscuous mode: %s\n", config->is_promisc ?
"enabled" :
"disabled");
880 if (config->runtime > 0U)
881 printf(
" runtime: %u sec\n", config->runtime);
883 printf(
" runtime: infinite\n");
885 printf(
" MAC modification: %s\n"
886 " workers: %u\n\n", config->is_mac_mod ?
"enabled" :
"disabled",
887 config->num_workers);
892 static dir_fwd_tbl_t build_dir_fwd_tbl(worker_pktio_t *pktios, uint8_t num_ifs)
894 worker_pktio_t *w_pktio;
898 for (uint8_t i = 0U; i < num_ifs; ++i) {
899 w_pktio = &pktios[i];
900 pktio = w_pktio->pktio;
901 tbl.tbl[i].pktin = pktio->in_qs[w_pktio->rx_poll % pktio->num_in_qs];
902 tbl.tbl[i].pktout = pktio->dst->out_qs[w_pktio->tx_poll % pktio->dst->num_out_qs];
903 tbl.tbl[i].src_mac = pktio->dst->src_mac;
904 tbl.tbl[i].dst_mac = pktio->dst->dst_mac;
910 static int run_direct_single_if(
void *args)
912 worker_config_t *config = args;
913 prog_config_t *prog_config = config->prog_config;
914 const uint32_t burst_size = prog_config->burst_size;
915 const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, config->num_ifs);
920 int num_recv, num_sent, diff;
922 uint64_t tx = 0U, tx_drops = 0U;
923 stats_t *stats = &config->stats;
937 num_sent = num_sent < 0 ? 0 : num_sent;
938 diff = num_recv - num_sent;
948 stats->tx_drops = tx_drops;
954 static int run_direct_single_if_mac_mod(
void *args)
956 worker_config_t *config = args;
957 prog_config_t *prog_config = config->prog_config;
958 const uint32_t burst_size = prog_config->burst_size;
959 const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, config->num_ifs);
964 int num_recv, num_sent, diff;
965 const odph_ethaddr_t src_mac = tbl.tbl[0U].src_mac, dst_mac = tbl.tbl[0U].dst_mac;
968 uint64_t tx = 0U, tx_drops = 0U;
969 stats_t *stats = &config->stats;
980 for (
int i = 0U; i < num_recv; ++i) {
989 num_sent = num_sent < 0 ? 0 : num_sent;
990 diff = num_recv - num_sent;
1000 stats->tx_drops = tx_drops;
1006 static int run_direct_multi_if(
void *args)
1008 worker_config_t *config = args;
1009 prog_config_t *prog_config = config->prog_config;
1010 const uint8_t num_ifs = config->num_ifs;
1011 const uint32_t burst_size = prog_config->burst_size;
1012 const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, num_ifs);
1018 int num_recv, num_sent, diff;
1020 uint64_t tx = 0U, tx_drops = 0U;
1021 stats_t *stats = &config->stats;
1027 pktin = tbl.tbl[i].pktin;
1028 pktout = tbl.tbl[i].pktout;
1041 num_sent = num_sent < 0 ? 0 : num_sent;
1042 diff = num_recv - num_sent;
1052 stats->tx_drops = tx_drops;
1058 static int run_direct_multi_if_mac_mod(
void *args)
1060 worker_config_t *config = args;
1061 prog_config_t *prog_config = config->prog_config;
1062 const uint8_t num_ifs = config->num_ifs;
1063 const uint32_t burst_size = prog_config->burst_size;
1064 const dir_fwd_tbl_t tbl = build_dir_fwd_tbl(config->pktios, num_ifs);
1070 int num_recv, num_sent, diff;
1071 odph_ethaddr_t src_mac, dst_mac;
1074 uint64_t tx = 0U, tx_drops = 0U;
1075 stats_t *stats = &config->stats;
1081 pktin = tbl.tbl[i].pktin;
1082 pktout = tbl.tbl[i].pktout;
1083 src_mac = tbl.tbl[i].src_mac;
1084 dst_mac = tbl.tbl[i].dst_mac;
1094 for (
int j = 0U; j < num_recv; ++j) {
1103 num_sent = num_sent < 0 ? 0 : num_sent;
1104 diff = num_recv - num_sent;
1114 stats->tx_drops = tx_drops;
1120 static scd_fwd_tbl_t build_scd_fwd_tbl(
const pktio_t *pktios, uint8_t num_ifs,
int thread_idx)
1122 const pktio_t *pktio;
1126 for (uint8_t i = 0U; i < num_ifs; ++i) {
1129 tbl.tbl[idx].pktout = pktio->dst->out_qs[thread_idx % pktio->dst->num_out_qs];
1130 tbl.tbl[idx].src_mac = pktio->dst->src_mac;
1131 tbl.tbl[idx].dst_mac = pktio->dst->dst_mac;
1137 static void drain_events(
void)
1151 static int run_scheduled(
void *args)
1153 worker_config_t *config = args;
1154 prog_config_t *prog_config = config->prog_config;
1157 const uint8_t num_ifs = prog_config->num_ifs;
1158 const uint32_t burst_size = prog_config->burst_size;
1160 int num_recv, num_sent, diff;
1162 const scd_fwd_tbl_t tbl = build_scd_fwd_tbl(prog_config->pktios, num_ifs,
odp_thread_id());
1164 uint64_t tx = 0U, tx_drops = 0U;
1165 stats_t *stats = &config->stats;
1181 num_sent = num_sent < 0 ? 0 : num_sent;
1182 diff = num_recv - num_sent;
1192 stats->tx_drops = tx_drops;
1205 static int run_scheduled_mac_mod(
void *args)
1207 worker_config_t *config = args;
1208 prog_config_t *prog_config = config->prog_config;
1211 const uint8_t num_ifs = prog_config->num_ifs;
1212 const uint32_t burst_size = prog_config->burst_size;
1214 int num_recv, num_sent, idx, diff;
1216 const scd_fwd_tbl_t tbl = build_scd_fwd_tbl(prog_config->pktios, num_ifs,
odp_thread_id());
1217 odph_ethaddr_t src_mac, dst_mac;
1220 uint64_t tx = 0U, tx_drops = 0U;
1221 stats_t *stats = &config->stats;
1234 src_mac = tbl.tbl[idx].src_mac;
1235 dst_mac = tbl.tbl[idx].dst_mac;
1236 pktout = tbl.tbl[idx].pktout;
1238 for (
int i = 0U; i < num_recv; ++i) {
1247 num_sent = num_sent < 0 ? 0 : num_sent;
1248 diff = num_recv - num_sent;
1258 stats->tx_drops = tx_drops;
1271 static run_func_t get_run_func(
const prog_config_t *config,
const worker_config_t *worker)
1273 if (config->mode == DIRECT) {
1274 if (config->is_mac_mod)
1275 return worker->num_ifs == 1U ?
1276 run_direct_single_if_mac_mod : run_direct_multi_if_mac_mod;
1278 return worker->num_ifs == 1U ?
1279 run_direct_single_if : run_direct_multi_if;
1281 return config->is_mac_mod ? run_scheduled_mac_mod : run_scheduled;
1285 static odp_bool_t setup_workers(prog_config_t *config)
1287 odph_thread_common_param_t thr_common;
1288 odph_thread_param_t thr_params[config->num_workers], *thr_param;
1289 worker_config_t *worker;
1293 odph_thread_common_param_init(&thr_common);
1294 thr_common.instance = config->odp_instance;
1295 thr_common.cpumask = &config->worker_mask;
1297 for (uint32_t i = 0; i < config->num_workers; ++i) {
1298 thr_param = &thr_params[i];
1299 worker = &config->worker_config[i];
1300 odph_thread_param_init(thr_param);
1301 thr_param->start = get_run_func(config, worker);
1303 thr_param->arg = worker;
1306 if ((uint32_t)odph_thread_create(config->thread_tbl, &thr_common, thr_params,
1307 config->num_workers) != config->num_workers) {
1308 ODPH_ERR(
"Error configuring worker threads\n");
1317 static odp_bool_t setup_test(prog_config_t *config)
1319 return setup_config(config) && setup_pktios(config) && print_summary(config) &&
1320 setup_workers(config);
1323 static void run_control(prog_config_t *config)
1325 if (config->runtime > 0U) {
1326 sleep(config->runtime);
1334 static void stop_test(prog_config_t *config)
1336 const pktio_t *pktio;
1338 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
1339 pktio = &config->pktios[i];
1346 (void)odph_thread_join(config->thread_tbl, config->num_workers);
1349 static void print_humanised(uint64_t value)
1352 printf(
"(%.2f Gpps)", (
double)value / GIGAS);
1353 else if (value > MEGAS)
1354 printf(
"(%.2f Mpps)", (
double)value / MEGAS);
1355 else if (value > KILOS)
1356 printf(
"(%.2f kpps)", (
double)value / KILOS);
1358 printf(
"(%" PRIu64
" pps)", value);
1361 static void print_stats(
const prog_config_t *config)
1363 const stats_t *stats;
1364 uint64_t tot_tx = 0U, tot_tx_drops = 0U, pps, tot_pps = 0U;
1366 printf(
"L2 forwarding done:\n"
1367 "===================\n\n");
1369 for (uint32_t i = 0U; i < config->num_workers; ++i) {
1370 stats = &config->worker_config[i].stats;
1371 tot_tx += stats->tx;
1372 tot_tx_drops += stats->tx_drops;
1376 printf(
" worker %u:\n\n"
1377 " packets sent: %" PRIu64
"\n"
1378 " packets dropped: %" PRIu64
"\n"
1379 " packets per second: %" PRIu64
" ", i, stats->tx, stats->tx_drops,
1381 print_humanised(pps);
1385 printf(
" total packets sent: %" PRIu64
"\n"
1386 " total packets dropped: %" PRIu64
"\n"
1387 " total packets per second: %" PRIu64
" ", tot_tx, tot_tx_drops, tot_pps);
1388 print_humanised(tot_pps);
1394 static void teardown(
const prog_config_t *config)
1396 const pktio_t *pktio;
1398 for (uint8_t i = 0U; i < config->num_ifs; ++i) {
1399 pktio = &config->pktios[i];
1402 if (config->is_promisc != config->orig_is_promisc)
1404 config->orig_is_promisc);
1406 if (config->mtu > 0U && config->orig_in_mtu > 0U &&
1407 config->orig_out_mtu > 0U)
1409 config->orig_out_mtu);
1415 free(pktio->dst_name);
1422 int main(
int argc,
char **argv)
1424 odph_helper_options_t odph_opts;
1428 int ret = EXIT_SUCCESS;
1429 parse_result_t parse_res;
1431 argc = odph_parse_options(argc, argv);
1433 if (odph_options(&odph_opts) == -1) {
1434 ODPH_ERR(
"Error while reading ODP helper options, exiting\n");
1439 init_param.
mem_model = odph_opts.mem_model;
1442 ODPH_ERR(
"ODP global init failed, exiting\n");
1447 ODPH_ERR(
"ODP local init failed, exiting\n");
1451 shm_cfg =
odp_shm_reserve(PROG_NAME
"_cfg",
sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
1455 ODPH_ERR(
"Error reserving shared memory\n");
1462 if (prog_conf == NULL) {
1463 ODPH_ERR(
"Error resolving shared memory address\n");
1468 memset(prog_conf, 0,
sizeof(*prog_conf));
1470 parse_res = setup_program(argc, argv, prog_conf);
1472 if (parse_res == PRS_NOK) {
1477 if (parse_res == PRS_TERM) {
1482 if (!setup_test(prog_conf)) {
1487 run_control(prog_conf);
1488 stop_test(prog_conf);
1489 print_stats(prog_conf);
1492 teardown(prog_conf);
1498 ODPH_ERR(
"ODP local terminate failed, exiting\n");
1503 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.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
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'.
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'.
struct odp_pktio_capability_t::@107 maxlen
Supported frame lengths for odp_pktio_maxlen_set()
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.
struct odp_pool_capability_t::@122 pkt
Packet pool capabilities
uint32_t max_num
Maximum number of buffers of any size.
uint32_t max_len
Maximum packet data length in bytes.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
odp_schedule_param_t sched
Scheduler parameters.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.
struct odp_pktin_hash_proto_t::@99 proto
Protocol header fields for hashing.
struct odp_pktio_set_op_t::@104 op
Operation flags.
uint32_t maxlen
Maximum frame length.
uint32_t promisc_mode
Promiscuous mode.