API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_l2fwd.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2014-2018 Linaro Limited
3 * Copyright (c) 2019-2025 Nokia
4 * Copyright (c) 2020-2021 Marvell
5 */
6
21/* enable strtok */
22#ifndef _GNU_SOURCE
23#define _GNU_SOURCE
24#endif
25
26#include <stdlib.h>
27#include <getopt.h>
28#include <unistd.h>
29#include <errno.h>
30#include <inttypes.h>
31#include <signal.h>
32
33#include <odp_api.h>
34#include <odp/helper/odph_api.h>
35#include <pktio_common.h>
36
37/* Maximum number of worker threads */
38#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
39
40/* Default number of packets per pool */
41#define DEFAULT_NUM_PKT (16 * 1024)
42
43/* Packet length to pool create */
44#define POOL_PKT_LEN 1536
45
46/* Maximum number of packet in a burst */
47#define MAX_PKT_BURST 32
48
49/* Maximum number of pktio queues per interface */
50#define MAX_QUEUES 2048
51
52/* Maximum number of schedule groups */
53#define MAX_GROUPS 32
54
55/* Maximum number of pktio interfaces */
56#define MAX_PKTIOS 8
57
58/* Default vector size */
59#define DEFAULT_VEC_SIZE MAX_PKT_BURST
60
61/* Max event vector size */
62#define MAX_EVENT_VEC_SIZE (4U * MAX_PKT_BURST)
63
64/* Default vector timeout */
65#define DEFAULT_VEC_TMO ODP_TIME_MSEC_IN_NS
66
67/* Maximum thread info string length */
68#define EXTRA_STR_LEN 32
69
70/* Packet input mode */
71typedef enum pktin_mode_t {
72 DIRECT_RECV,
73 PLAIN_QUEUE,
74 SCHED_PARALLEL,
75 SCHED_ATOMIC,
76 SCHED_ORDERED,
77} pktin_mode_t;
78
79/* Packet output modes */
80typedef enum pktout_mode_t {
81 PKTOUT_DIRECT,
82 PKTOUT_QUEUE
83} pktout_mode_t;
84
85/* Vector mode */
86typedef enum vector_mode_t {
87 VECTOR_MODE_DISABLED = 0,
88 VECTOR_MODE_EVENT = 1,
89 VECTOR_MODE_PACKET = 2
90} vector_mode_t;
91
92static inline int sched_mode(pktin_mode_t in_mode)
93{
94 return (in_mode == SCHED_PARALLEL) ||
95 (in_mode == SCHED_ATOMIC) ||
96 (in_mode == SCHED_ORDERED);
97}
98
99/* Get rid of path in filename - only for unix-type paths using '/' */
100#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
101 strrchr((file_name), '/') + 1 : (file_name))
102/*
103 * Parsed command line application arguments
104 */
105typedef struct {
106 /* Some extra features (e.g. error checks) have been enabled */
107 uint8_t extra_feat;
108
109 /* Has some state that needs to be maintained across tx and/or rx */
110 uint8_t has_state;
111
112 /* Prefetch packet data */
113 uint8_t prefetch;
114
115 /* Change destination eth addresses */
116 uint8_t dst_change;
117
118 /* Change source eth addresses */
119 uint8_t src_change;
120
121 /* Read packet data in uint64_t words */
122 uint16_t data_rd;
123
124 /* Check packet errors */
125 uint8_t error_check;
126
127 /* Packet copy */
128 uint8_t packet_copy;
129
130 /* Checksum offload */
131 uint8_t chksum;
132
133 /* Print debug info on every packet */
134 uint8_t verbose_pkt;
135
136 unsigned int cpu_count;
137 int if_count; /* Number of interfaces to be used */
138 int addr_count; /* Number of dst addresses to be used */
139 int num_workers; /* Number of worker threads */
140 char **if_names; /* Array of pointers to interface names */
141 odph_ethaddr_t addrs[MAX_PKTIOS]; /* Array of dst addresses */
142 pktin_mode_t in_mode; /* Packet input mode */
143 pktout_mode_t out_mode; /* Packet output mode */
144 int time; /* Time in seconds to run. */
145 int accuracy; /* Number of seconds to get and print stats */
146 char *if_str; /* Storage for interface names */
147 int sched_mode; /* Scheduler mode */
148 int num_groups; /* Number of scheduling groups */
149 int group_mode; /* How threads join groups */
150 int burst_rx; /* Receive burst size */
151 int rx_queues; /* RX queues per interface */
152 int pool_per_if; /* Create pool per interface */
153 uint32_t num_pkt; /* Number of packets per pool */
154 int flow_control; /* Flow control mode */
155 bool pause_rx; /* Reception of pause frames enabled */
156 bool pause_tx; /* Transmission of pause frames enabled */
157 vector_mode_t vector_mode; /* Vector mode */
158 uint32_t num_vec; /* Number of vectors per pool */
159 int64_t vec_tmo_ns; /* Vector formation timeout in ns */
160 uint32_t vec_size; /* Vector size */
161 uint64_t wait_ns; /* Extra wait in ns */
162 uint64_t memcpy_bytes; /* Extra memcpy bytes */
163 int verbose; /* Verbose output */
164 uint32_t packet_len; /* Maximum packet length supported */
165 uint32_t seg_len; /* Pool segment length */
166 int promisc_mode; /* Promiscuous mode enabled */
167 int flow_aware; /* Flow aware scheduling enabled */
168 uint8_t input_ts; /* Packet input timestamping enabled */
169 int mtu; /* Interface MTU */
170 int num_om;
171 int num_prio;
172 uint32_t sched_prefetch; /* Number of events to prefetch */
173 odp_cache_stash_config_t cache_stash_config; /* Cache stash configuration */
174 uint32_t wait_sec;
175
176 struct {
178 uint32_t nth;
179 uint32_t thr_compl_id;
180 uint32_t tot_compl_id;
181 } tx_compl;
182
183 char *output_map[MAX_PKTIOS]; /* Destination port mappings for interfaces */
184 odp_schedule_prio_t prio[MAX_PKTIOS]; /* Priority of input queues of an interface */
185
186} appl_args_t;
187
188/* Statistics */
189typedef union ODP_ALIGNED_CACHE {
190 struct {
191 /* Number of forwarded packets */
192 uint64_t packets;
193 /* Packets dropped due to receive error */
194 uint64_t rx_drops;
195 /* Packets dropped due to transmit error */
196 uint64_t tx_drops;
197 /* Number of transmit completion start misses (previous incomplete) */
198 uint64_t tx_c_misses;
199 /* Number of transmit completion start failures */
200 uint64_t tx_c_fails;
201 /* Number of failed packet copies */
202 uint64_t copy_fails;
203 /* Dummy sum of packet data */
204 uint64_t dummy_sum;
205 } s;
206
207 uint8_t padding[ODP_CACHE_LINE_SIZE];
208} stats_t;
209
210/* Transmit completion specific state data */
211typedef struct {
212 /* Options that are passed to transmit completion requests */
214 /* Thread specific initial value for transmit completion IDs */
215 uint32_t init;
216 /* Thread specific maximum value for transmit completion IDs */
217 uint32_t max;
218 /* Next free completion ID to be used for a transmit completion request */
219 uint32_t free_head;
220 /* Next completion ID to be polled for transmit completion readiness */
221 uint32_t poll_head;
222 /* Number of active requests */
223 uint32_t num_act;
224 /* Maximum number of active requests */
225 uint32_t max_act;
226 /* Transmit completion request interval for packets */
227 int interval;
228 /* Next packet in a send burst for which to request transmit completion */
229 int next_req;
230} tx_compl_t;
231
232/* Thread specific state data */
233typedef struct {
234 tx_compl_t tx_compl;
235} state_t;
236
237/* Thread specific data */
238typedef struct thread_args_t {
239 stats_t stats;
240 state_t state;
241
242 struct {
243 odp_pktin_queue_t pktin;
244 odp_pktout_queue_t pktout;
245 odp_queue_t rx_queue;
246 odp_queue_t tx_queue;
247 int rx_idx;
248 int tx_idx;
249 int rx_queue_idx;
250 int tx_queue_idx;
251 } pktio[MAX_PKTIOS];
252
253 /* Groups to join */
254 odp_schedule_group_t group[MAX_GROUPS];
255
256 int thr_idx;
257 int num_pktio;
258 int num_grp_join;
259
260} thread_args_t;
261
262/*
263 * Grouping of all global data
264 */
265typedef struct {
266 /* Thread table */
267 odph_thread_t thread_tbl[MAX_WORKERS];
268 /* Thread specific arguments */
269 thread_args_t thread_args[MAX_WORKERS];
270 /* Barriers to synchronize main and workers */
271 odp_barrier_t init_barrier;
272 odp_barrier_t term_barrier;
273 /* Application (parsed) arguments */
274 appl_args_t appl;
275 /* Table of port ethernet addresses */
276 odph_ethaddr_t port_eth_addr[MAX_PKTIOS];
277 /* Table of dst ethernet addresses */
278 odph_ethaddr_t dst_eth_addr[MAX_PKTIOS];
279 /* Table of dst ports. This is used by non-sched modes. */
280 int dst_port[MAX_PKTIOS];
281 /* Table of pktio handles */
282 struct {
283 odp_pktio_t pktio;
284 odp_pktin_queue_t pktin[MAX_QUEUES];
285 odp_pktout_queue_t pktout[MAX_QUEUES];
286 odp_queue_t rx_q[MAX_QUEUES];
287 odp_queue_t tx_q[MAX_QUEUES];
288 odp_queue_t compl_q;
289 int num_rx_thr;
290 int num_tx_thr;
291 int num_rx_queue;
292 int num_tx_queue;
293 int next_rx_queue;
294 int next_tx_queue;
295 } pktios[MAX_PKTIOS];
296
297 /* Destination port lookup table.
298 * Table index is pktio_index of the API. This is used by the sched
299 * mode. */
300 uint8_t dst_port_from_idx[ODP_PKTIO_MAX_INDEX + 1];
301 /* Break workers loop if set to 1 */
302 odp_atomic_u32_t exit_threads;
303
304 uint32_t pkt_len;
305 uint32_t num_pkt;
306 uint32_t seg_len;
307 uint32_t vector_num;
308 uint32_t vector_max_size;
309 char cpumaskstr[ODP_CPUMASK_STR_SIZE];
310 odp_shm_t memcpy_shm; /* Shared memory block for memcpy */
311 uint8_t *memcpy_data; /* Data for memcpy */
312
313} args_t;
314
316enum longopt_only {
317 OPT_WAIT_LINK = 256,
318 OPT_SCHED_PREFETCH = 257,
319 OPT_CACHE_STASH = 258
320};
321
322/* Global pointer to args */
323static args_t *gbl_args;
324
325static void sig_handler(int signo ODP_UNUSED)
326{
327 if (gbl_args == NULL)
328 return;
329 odp_atomic_store_u32(&gbl_args->exit_threads, 1);
330}
331
332static int setup_sig_handler(void)
333{
334 struct sigaction action = { .sa_handler = sig_handler };
335
336 if (sigemptyset(&action.sa_mask) || sigaction(SIGINT, &action, NULL))
337 return -1;
338
339 return 0;
340}
341
342/*
343 * Drop packets which input parsing marked as containing errors.
344 *
345 * Frees packets with error and modifies pkt_tbl[] to only contain packets with
346 * no detected errors.
347 *
348 * pkt_tbl Array of packets
349 * num Number of packets in pkt_tbl[]
350 *
351 * Returns number of packets dropped
352 */
353static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num)
354{
355 odp_packet_t pkt;
356 unsigned dropped = 0;
357 unsigned i, j;
358
359 for (i = 0, j = 0; i < num; ++i) {
360 pkt = pkt_tbl[i];
361
363 odp_packet_free(pkt); /* Drop */
364 dropped++;
365 } else if (odp_unlikely(i != j++)) {
366 pkt_tbl[j - 1] = pkt;
367 }
368 }
369
370 return dropped;
371}
372
373static inline void prefetch_data(uint8_t prefetch, odp_packet_t pkt_tbl[], uint32_t num)
374{
375 if (prefetch == 0)
376 return;
377
378 for (uint32_t i = 0; i < num; i++)
379 odp_packet_prefetch(pkt_tbl[i], 0, prefetch * 64);
380}
381
382/*
383 * Fill packets' eth addresses according to the destination port
384 *
385 * pkt_tbl Array of packets
386 * num Number of packets in the array
387 * dst_port Destination port
388 */
389static inline void fill_eth_addrs(odp_packet_t pkt_tbl[],
390 unsigned num, int dst_port)
391{
392 odp_packet_t pkt;
393 odph_ethhdr_t *eth;
394 unsigned i;
395
396 if (!gbl_args->appl.dst_change && !gbl_args->appl.src_change)
397 return;
398
399 for (i = 0; i < num; ++i) {
400 pkt = pkt_tbl[i];
401 eth = odp_packet_data(pkt);
402
403 if (gbl_args->appl.src_change)
404 eth->src = gbl_args->port_eth_addr[dst_port];
405
406 if (gbl_args->appl.dst_change)
407 eth->dst = gbl_args->dst_eth_addr[dst_port];
408 }
409}
410
411static inline int event_queue_send(odp_queue_t queue, odp_packet_t *pkt_tbl,
412 unsigned pkts)
413{
414 int ret;
415 unsigned sent = 0;
416 odp_event_t ev_tbl[pkts];
417
418 odp_packet_to_event_multi(pkt_tbl, ev_tbl, pkts);
419
420 while (sent < pkts) {
421 ret = odp_queue_enq_multi(queue, &ev_tbl[sent], pkts - sent);
422
423 if (odp_unlikely(ret <= 0)) {
424 if (ret < 0 || odp_atomic_load_u32(&gbl_args->exit_threads))
425 break;
426 }
427
428 sent += ret;
429 }
430
431 return sent;
432}
433
434static inline void chksum_insert(odp_packet_t *pkt_tbl, int pkts)
435{
436 odp_packet_t pkt;
437 int i;
438
439 for (i = 0; i < pkts; i++) {
440 pkt = pkt_tbl[i];
443 }
444}
445
446static void print_packets(odp_packet_t *pkt_tbl, int num)
447{
448 odp_packet_t pkt;
449 uintptr_t data_ptr;
450 uint32_t bit, align;
451
452 for (int i = 0; i < num; i++) {
453 pkt = pkt_tbl[i];
454 data_ptr = (uintptr_t)odp_packet_data(pkt);
455
456 for (bit = 0, align = 1; bit < 32; bit++, align *= 2)
457 if (data_ptr & (0x1 << bit))
458 break;
459
460 printf(" Packet data: 0x%" PRIxPTR "\n"
461 " Packet len: %u\n"
462 " Packet seg len: %u\n"
463 " Data align: %u\n"
464 " Num segments: %i\n"
465 " Headroom size: %u\n"
466 " User area size: %u\n\n",
467 data_ptr, odp_packet_len(pkt), odp_packet_seg_len(pkt), align,
470 }
471}
472
473static inline void data_rd(odp_packet_t *pkt_tbl, int num, uint16_t rd_words, stats_t *stats)
474{
475 odp_packet_t pkt;
476 uint64_t *data;
477 int i;
478 uint32_t len, words, j;
479 uint64_t sum = 0;
480
481 for (i = 0; i < num; i++) {
482 pkt = pkt_tbl[i];
483 data = odp_packet_data(pkt);
484 len = odp_packet_seg_len(pkt);
485
486 words = rd_words;
487 if (rd_words * 8 > len)
488 words = len / 8;
489
490 for (j = 0; j < words; j++)
491 sum += data[j];
492 }
493
494 stats->s.dummy_sum += sum;
495}
496
497static inline int copy_packets(odp_packet_t *pkt_tbl, int pkts)
498{
499 odp_packet_t old_pkt, new_pkt;
500 odp_pool_t pool;
501 int i;
502 int copy_fails = 0;
503
504 for (i = 0; i < pkts; i++) {
505 old_pkt = pkt_tbl[i];
506 pool = odp_packet_pool(old_pkt);
507 new_pkt = odp_packet_copy(old_pkt, pool);
508 if (odp_likely(new_pkt != ODP_PACKET_INVALID)) {
509 pkt_tbl[i] = new_pkt;
510 odp_packet_free(old_pkt);
511 } else {
512 copy_fails++;
513 }
514 }
515
516 return copy_fails;
517}
518
519/*
520 * Return number of packets remaining in the pkt_tbl
521 */
522static inline int process_extra_features(const appl_args_t *appl_args, odp_packet_t *pkt_tbl,
523 int pkts, stats_t *stats, uint8_t *const memcpy_src)
524{
525 if (odp_unlikely(appl_args->extra_feat)) {
526 uint16_t rd_words = appl_args->data_rd;
527
528 if (appl_args->verbose_pkt)
529 print_packets(pkt_tbl, pkts);
530
531 if (rd_words)
532 data_rd(pkt_tbl, pkts, rd_words, stats);
533
534 if (appl_args->packet_copy) {
535 int fails;
536
537 fails = copy_packets(pkt_tbl, pkts);
538 stats->s.copy_fails += fails;
539 }
540
541 if (appl_args->chksum)
542 chksum_insert(pkt_tbl, pkts);
543
544 if (appl_args->error_check) {
545 int rx_drops;
546
547 /* Drop packets with errors */
548 rx_drops = drop_err_pkts(pkt_tbl, pkts);
549
550 if (odp_unlikely(rx_drops)) {
551 stats->s.rx_drops += rx_drops;
552 if (pkts == rx_drops)
553 return 0;
554
555 pkts -= rx_drops;
556 }
557 }
558
559 if (appl_args->wait_ns)
560 odp_time_wait_ns(appl_args->wait_ns);
561
562 if (appl_args->memcpy_bytes) {
563 const uint64_t bytes = appl_args->memcpy_bytes;
564 uint8_t *memcpy_dst = memcpy_src + bytes;
565
566 memcpy(memcpy_dst, memcpy_src, bytes);
567 }
568 }
569 return pkts;
570}
571
572static inline void handle_tx_event_compl(tx_compl_t *tx_c, odp_packet_t pkts[], int num,
573 int tx_idx, stats_t *stats)
574{
575 odp_packet_t pkt;
576 int next_req = tx_c->next_req;
577 const int interval = tx_c->interval;
578
579 tx_c->opt.queue = gbl_args->pktios[tx_idx].compl_q;
580
581 while (next_req <= num) {
582 pkt = pkts[next_req - 1];
583
584 if (odp_packet_tx_compl_request(pkt, &tx_c->opt) < 0) {
585 stats->s.tx_c_fails++;
586 /* Missed one, try requesting for the first packet of next burst. */
587 next_req = num + 1;
588 break;
589 }
590
591 next_req += interval;
592 }
593
594 tx_c->next_req = next_req - num;
595}
596
597static inline void handle_tx_poll_compl(tx_compl_t *tx_c, odp_packet_t pkts[], int num, int tx_idx,
598 stats_t *stats)
599{
600 uint32_t num_act = tx_c->num_act, poll_head = tx_c->poll_head, free_head = tx_c->free_head;
601 const uint32_t max = tx_c->max, init = tx_c->init, max_act = tx_c->max_act;
602 odp_pktio_t pktio = gbl_args->pktios[tx_idx].pktio;
603 int next_req = tx_c->next_req;
604 odp_packet_t pkt;
605 const int interval = tx_c->interval;
606
607 while (num_act > 0) {
608 if (odp_packet_tx_compl_done(pktio, poll_head) < 1)
609 break;
610
611 --num_act;
612
613 if (++poll_head > max)
614 poll_head = init;
615 }
616
617 while (next_req <= num) {
618 pkt = pkts[next_req - 1];
619
620 if (num_act == max_act) {
621 stats->s.tx_c_misses++;
622 /* Missed one, try requesting for the first packet of next burst. */
623 next_req = num + 1;
624 break;
625 }
626
627 tx_c->opt.compl_id = free_head;
628
629 if (odp_packet_tx_compl_request(pkt, &tx_c->opt) < 0) {
630 stats->s.tx_c_fails++;
631 /* Missed one, try requesting for the first packet of next burst. */
632 next_req = num + 1;
633 break;
634 }
635
636 if (++free_head > max)
637 free_head = init;
638
639 ++num_act;
640 next_req += interval;
641 }
642
643 tx_c->free_head = free_head;
644 tx_c->poll_head = poll_head;
645 tx_c->num_act = num_act;
646 tx_c->next_req = next_req - num;
647}
648
649static inline void handle_tx_state(state_t *state, odp_packet_t pkts[], int num, int tx_idx,
650 stats_t *stats)
651{
652 tx_compl_t *tx_c = &state->tx_compl;
653
654 if (tx_c->opt.mode == ODP_PACKET_TX_COMPL_EVENT)
655 handle_tx_event_compl(tx_c, pkts, num, tx_idx, stats);
656 else if (tx_c->opt.mode == ODP_PACKET_TX_COMPL_POLL)
657 handle_tx_poll_compl(tx_c, pkts, num, tx_idx, stats);
658}
659
660static inline void handle_state_failure(state_t *state, odp_packet_t packet)
661{
662 if (odp_packet_has_tx_compl_request(packet) != 0) {
663 --state->tx_compl.num_act;
664 --state->tx_compl.free_head;
665
666 if (state->tx_compl.free_head == UINT32_MAX ||
667 state->tx_compl.free_head < state->tx_compl.init)
668 state->tx_compl.free_head = state->tx_compl.max;
669 }
670}
671
672static inline void send_packets(odp_packet_t *pkt_tbl,
673 int pkts,
674 int use_event_queue,
675 int tx_idx,
676 odp_queue_t tx_queue,
677 odp_pktout_queue_t pktout_queue,
678 state_t *state,
679 stats_t *stats)
680{
681 int sent;
682 unsigned int tx_drops;
683 int i;
684 odp_packet_t pkt;
685
686 if (odp_unlikely(state != NULL))
687 handle_tx_state(state, pkt_tbl, pkts, tx_idx, stats);
688
689 if (odp_unlikely(use_event_queue))
690 sent = event_queue_send(tx_queue, pkt_tbl, pkts);
691 else
692 sent = odp_pktout_send(pktout_queue, pkt_tbl, pkts);
693
694 sent = odp_unlikely(sent < 0) ? 0 : sent;
695 tx_drops = pkts - sent;
696
697 if (odp_unlikely(tx_drops)) {
698 stats->s.tx_drops += tx_drops;
699
700 /* Drop rejected packets */
701 for (i = sent; i < pkts; i++) {
702 pkt = pkt_tbl[i];
703 handle_state_failure(state, pkt);
704 odp_packet_free(pkt);
705 }
706 }
707
708 stats->s.packets += pkts;
709}
710
711static int handle_rx_state(state_t *state, odp_event_t evs[], int num)
712{
713 if (state->tx_compl.opt.mode != ODP_PACKET_TX_COMPL_EVENT ||
714 odp_event_type(evs[0]) != ODP_EVENT_PACKET_TX_COMPL)
715 return num;
716
717 odp_event_free_multi(evs, num);
718
719 return 0;
720}
721
722/*
723 * Packet IO worker thread using scheduled queues and packet vector mode.
724 *
725 * arg thread arguments of type 'thread_args_t *'
726 */
727static int run_worker_sched_mode_vector(void *arg)
728{
729 const int thr = odp_thread_id();
730 int i;
731 int pktio, num_pktio;
732 uint16_t max_burst;
733 odp_thrmask_t mask;
734 odp_pktout_queue_t pktout[MAX_PKTIOS];
735 odp_queue_t tx_queue[MAX_PKTIOS];
736 thread_args_t *thr_args = arg;
737 const int thr_idx = thr_args->thr_idx;
738 stats_t *stats = &thr_args->stats;
739 const appl_args_t *appl_args = &gbl_args->appl;
740 uint8_t *const memcpy_src = appl_args->memcpy_bytes ?
741 &gbl_args->memcpy_data[thr_idx * 2 * appl_args->memcpy_bytes] : NULL;
742 state_t *state = appl_args->has_state ? &thr_args->state : NULL;
743 int use_event_queue = gbl_args->appl.out_mode;
744 pktin_mode_t in_mode = gbl_args->appl.in_mode;
745 const uint32_t sched_prefetch = appl_args->sched_prefetch;
746
747 max_burst = gbl_args->appl.burst_rx;
748
749 odp_thrmask_zero(&mask);
750 odp_thrmask_set(&mask, thr);
751
752 /* Join non-default groups */
753 for (i = 0; i < thr_args->num_grp_join; i++) {
754 if (odp_schedule_group_join(thr_args->group[i], &mask)) {
755 ODPH_ERR("Join failed: %i\n", i);
756 return -1;
757 }
758 }
759
760 num_pktio = thr_args->num_pktio;
761
762 if (num_pktio > MAX_PKTIOS) {
763 ODPH_ERR("Too many pktios %i\n", num_pktio);
764 return -1;
765 }
766
767 for (pktio = 0; pktio < num_pktio; pktio++) {
768 tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
769 pktout[pktio] = thr_args->pktio[pktio].pktout;
770 }
771
772 printf("[%02i] PKTIN_SCHED_%s_PACKET_VECTOR, %s\n", thr,
773 (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
774 ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
775 (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
776
777 odp_barrier_wait(&gbl_args->init_barrier);
778
779 /* Loop packets */
780 while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
781 odp_event_t ev_tbl[MAX_PKT_BURST];
782 int events;
783
784 events = odp_schedule_multi_no_wait(NULL, ev_tbl, max_burst);
785
786 if (events <= 0)
787 continue;
788
789 for (i = 0; i < events; i++) {
791 odp_packet_t *pkt_tbl = NULL;
792 odp_packet_t pkt;
793 int src_idx, dst_idx;
794 int pkts = 0;
795
796 if (odp_event_type(ev_tbl[i]) == ODP_EVENT_PACKET) {
797 pkt = odp_packet_from_event(ev_tbl[i]);
798 pkt_tbl = &pkt;
799 pkts = 1;
800 } else if (odp_event_type(ev_tbl[i]) == ODP_EVENT_PACKET_VECTOR) {
801 pkt_vec = odp_packet_vector_from_event(ev_tbl[i]);
802 pkts = odp_packet_vector_tbl(pkt_vec, &pkt_tbl);
803 } else if (state != NULL) {
804 pkts = handle_rx_state(state, ev_tbl, events);
805
806 if (pkts <= 0)
807 continue;
808 }
809
810 prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
811
812 pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats, memcpy_src);
813
814 if (odp_unlikely(pkts == 0)) {
815 if (pkt_vec != ODP_PACKET_VECTOR_INVALID)
816 odp_packet_vector_free(pkt_vec);
817 continue;
818 }
819
820 /* packets from the same queue are from the same interface */
821 src_idx = odp_packet_input_index(pkt_tbl[0]);
822 ODPH_ASSERT(src_idx >= 0);
823 dst_idx = gbl_args->dst_port_from_idx[src_idx];
824 fill_eth_addrs(pkt_tbl, pkts, dst_idx);
825
826 if (sched_prefetch)
827 odp_schedule_prefetch(sched_prefetch);
828
829 send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue[dst_idx],
830 pktout[dst_idx], state, stats);
831
832 if (pkt_vec != ODP_PACKET_VECTOR_INVALID)
833 odp_packet_vector_free(pkt_vec);
834 }
835 }
836
837 /*
838 * Free prefetched packets before entering the thread barrier.
839 * Such packets can block sending of later packets in other threads
840 * that then would never enter the thread barrier and we would
841 * end up in a dead-lock.
842 */
844 while (1) {
845 odp_event_t ev;
846
848 if (ev == ODP_EVENT_INVALID)
849 break;
850
851 odp_event_free(ev);
852 }
853
854 /* Make sure that latest stat writes are visible to other threads */
855 odp_mb_full();
856
857 /* Wait until pktio devices are stopped */
858 odp_barrier_wait(&gbl_args->term_barrier);
859
860 /* Free remaining events in queues */
862 while (1) {
863 odp_event_t ev;
864
865 ev = odp_schedule(NULL,
867
868 if (ev == ODP_EVENT_INVALID)
869 break;
870
871 odp_event_free(ev);
872 }
873
874 return 0;
875}
876
877/*
878 * Packet IO worker thread using scheduled queues and event vector mode.
879 *
880 * arg thread arguments of type 'thread_args_t *'
881 */
882static int run_worker_sched_mode_event_vector(void *arg)
883{
884 const int thr = odp_thread_id();
885 int i;
886 int pktio, num_pktio;
887 uint16_t max_burst;
888 odp_thrmask_t mask;
889 odp_pktout_queue_t pktout[MAX_PKTIOS];
890 odp_queue_t tx_queue[MAX_PKTIOS];
891 thread_args_t *thr_args = arg;
892 const int thr_idx = thr_args->thr_idx;
893 stats_t *stats = &thr_args->stats;
894 const appl_args_t *appl_args = &gbl_args->appl;
895 uint8_t *const memcpy_src = appl_args->memcpy_bytes ?
896 &gbl_args->memcpy_data[thr_idx * 2 * appl_args->memcpy_bytes] : NULL;
897 state_t *state = appl_args->has_state ? &thr_args->state : NULL;
898 int use_event_queue = gbl_args->appl.out_mode;
899 pktin_mode_t in_mode = gbl_args->appl.in_mode;
900 const uint32_t sched_prefetch = appl_args->sched_prefetch;
901
902 max_burst = gbl_args->appl.burst_rx;
903
904 odp_thrmask_zero(&mask);
905 odp_thrmask_set(&mask, thr);
906
907 /* Join non-default groups */
908 for (i = 0; i < thr_args->num_grp_join; i++) {
909 if (odp_schedule_group_join(thr_args->group[i], &mask)) {
910 ODPH_ERR("Join failed: %i\n", i);
911 return -1;
912 }
913 }
914
915 num_pktio = thr_args->num_pktio;
916
917 if (num_pktio > MAX_PKTIOS) {
918 ODPH_ERR("Too many pktios %i\n", num_pktio);
919 return -1;
920 }
921
922 for (pktio = 0; pktio < num_pktio; pktio++) {
923 tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
924 pktout[pktio] = thr_args->pktio[pktio].pktout;
925 }
926
927 printf("[%02i] PKTIN_SCHED_%s_EVENT_VECTOR, %s\n", thr,
928 (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
929 ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
930 (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
931
932 odp_barrier_wait(&gbl_args->init_barrier);
933
934 /* Loop packets */
935 while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
936 odp_event_t ev_tbl[MAX_PKT_BURST];
937 odp_packet_t pkt_tbl[MAX_EVENT_VEC_SIZE];
938 int events;
939
940 events = odp_schedule_multi_no_wait(NULL, ev_tbl, max_burst);
941
942 if (events <= 0)
943 continue;
944
945 for (i = 0; i < events; i++) {
946 odp_event_t *event_tbl = NULL;
947 odp_packet_t pkt;
948 int src_idx, dst_idx;
949 int pkts = 0;
950
951 if (odp_event_type(ev_tbl[i]) == ODP_EVENT_PACKET) {
952 pkt = odp_packet_from_event(ev_tbl[i]);
953 pkt_tbl[0] = pkt;
954 pkts = 1;
955 } else if (odp_event_type(ev_tbl[i]) == ODP_EVENT_VECTOR) {
957
958 pkts = odp_event_vector_tbl(evv, &event_tbl);
959 odp_packet_from_event_multi(pkt_tbl, event_tbl, pkts);
961 } else if (state != NULL) {
962 pkts = handle_rx_state(state, ev_tbl, events);
963
964 if (pkts <= 0)
965 continue;
966 }
967
968 prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
969
970 pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats, memcpy_src);
971
972 if (odp_unlikely(pkts == 0))
973 continue;
974
975 /* packets from the same queue are from the same interface */
976 src_idx = odp_packet_input_index(pkt_tbl[0]);
977 ODPH_ASSERT(src_idx >= 0);
978 dst_idx = gbl_args->dst_port_from_idx[src_idx];
979 fill_eth_addrs(pkt_tbl, pkts, dst_idx);
980
981 if (sched_prefetch)
982 odp_schedule_prefetch(sched_prefetch);
983
984 send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue[dst_idx],
985 pktout[dst_idx], state, stats);
986 }
987 }
988
989 /*
990 * Free prefetched packets before entering the thread barrier.
991 * Such packets can block sending of later packets in other threads
992 * that then would never enter the thread barrier and we would
993 * end up in a dead-lock.
994 */
996 while (1) {
997 odp_event_t ev;
998
1000 if (ev == ODP_EVENT_INVALID)
1001 break;
1002
1003 odp_event_free(ev);
1004 }
1005
1006 /* Make sure that latest stat writes are visible to other threads */
1007 odp_mb_full();
1008
1009 /* Wait until pktio devices are stopped */
1010 odp_barrier_wait(&gbl_args->term_barrier);
1011
1012 /* Free remaining events in queues */
1014 while (1) {
1015 odp_event_t ev;
1016
1018
1019 if (ev == ODP_EVENT_INVALID)
1020 break;
1021
1022 odp_event_free(ev);
1023 }
1024
1025 return 0;
1026}
1027
1028/*
1029 * Packet IO worker thread using scheduled queues
1030 *
1031 * arg thread arguments of type 'thread_args_t *'
1032 */
1033static int run_worker_sched_mode(void *arg)
1034{
1035 int pkts;
1036 const int thr = odp_thread_id();
1037 int dst_idx;
1038 int i;
1039 int pktio, num_pktio;
1040 uint16_t max_burst;
1041 odp_thrmask_t mask;
1042 odp_pktout_queue_t pktout[MAX_PKTIOS];
1043 odp_queue_t tx_queue[MAX_PKTIOS];
1044 char extra_str[EXTRA_STR_LEN];
1045 thread_args_t *thr_args = arg;
1046 const int thr_idx = thr_args->thr_idx;
1047 stats_t *stats = &thr_args->stats;
1048 const appl_args_t *appl_args = &gbl_args->appl;
1049 uint8_t *const memcpy_src = appl_args->memcpy_bytes ?
1050 &gbl_args->memcpy_data[thr_idx * 2 * appl_args->memcpy_bytes] : NULL;
1051 state_t *state = appl_args->has_state ? &thr_args->state : NULL;
1052 int use_event_queue = gbl_args->appl.out_mode;
1053 pktin_mode_t in_mode = gbl_args->appl.in_mode;
1054 const uint32_t sched_prefetch = appl_args->sched_prefetch;
1055
1056 max_burst = gbl_args->appl.burst_rx;
1057
1058 memset(extra_str, 0, EXTRA_STR_LEN);
1059 odp_thrmask_zero(&mask);
1060 odp_thrmask_set(&mask, thr);
1061
1062 /* Join non-default groups */
1063 for (i = 0; i < thr_args->num_grp_join; i++) {
1064 if (odp_schedule_group_join(thr_args->group[i], &mask)) {
1065 ODPH_ERR("Join failed: %i\n", i);
1066 return -1;
1067 }
1068
1069 if (gbl_args->appl.verbose) {
1070 uint64_t tmp = (uint64_t)(uintptr_t)thr_args->group[i];
1071
1072 printf("[%02i] Joined group 0x%" PRIx64 "\n", thr, tmp);
1073 }
1074 }
1075
1076 if (thr_args->num_grp_join)
1077 snprintf(extra_str, EXTRA_STR_LEN, ", joined %i groups", thr_args->num_grp_join);
1078 else if (gbl_args->appl.num_groups == 0)
1079 snprintf(extra_str, EXTRA_STR_LEN, ", GROUP_ALL");
1080 else if (gbl_args->appl.num_groups)
1081 snprintf(extra_str, EXTRA_STR_LEN, ", GROUP_WORKER");
1082
1083 num_pktio = thr_args->num_pktio;
1084
1085 if (num_pktio > MAX_PKTIOS) {
1086 ODPH_ERR("Too many pktios %i\n", num_pktio);
1087 return -1;
1088 }
1089
1090 for (pktio = 0; pktio < num_pktio; pktio++) {
1091 tx_queue[pktio] = thr_args->pktio[pktio].tx_queue;
1092 pktout[pktio] = thr_args->pktio[pktio].pktout;
1093 }
1094
1095 printf("[%02i] PKTIN_SCHED_%s, %s%s\n", thr,
1096 (in_mode == SCHED_PARALLEL) ? "PARALLEL" :
1097 ((in_mode == SCHED_ATOMIC) ? "ATOMIC" : "ORDERED"),
1098 (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT", extra_str);
1099
1100 odp_barrier_wait(&gbl_args->init_barrier);
1101
1102 /* Loop packets */
1103 while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
1104 odp_event_t ev_tbl[MAX_PKT_BURST];
1105 odp_packet_t pkt_tbl[MAX_PKT_BURST];
1106 int src_idx;
1107
1108 pkts = odp_schedule_multi_no_wait(NULL, ev_tbl, max_burst);
1109
1110 if (pkts <= 0)
1111 continue;
1112
1113 if (odp_unlikely(state != NULL)) {
1114 pkts = handle_rx_state(state, ev_tbl, pkts);
1115
1116 if (pkts <= 0)
1117 continue;
1118 }
1119
1120 odp_packet_from_event_multi(pkt_tbl, ev_tbl, pkts);
1121
1122 prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
1123
1124 pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats, memcpy_src);
1125
1126 if (odp_unlikely(pkts == 0))
1127 continue;
1128
1129 /* packets from the same queue are from the same interface */
1130 src_idx = odp_packet_input_index(pkt_tbl[0]);
1131 ODPH_ASSERT(src_idx >= 0);
1132 dst_idx = gbl_args->dst_port_from_idx[src_idx];
1133 fill_eth_addrs(pkt_tbl, pkts, dst_idx);
1134
1135 if (sched_prefetch)
1136 odp_schedule_prefetch(sched_prefetch);
1137
1138 send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue[dst_idx],
1139 pktout[dst_idx], state, stats);
1140 }
1141
1142 /*
1143 * Free prefetched packets before entering the thread barrier.
1144 * Such packets can block sending of later packets in other threads
1145 * that then would never enter the thread barrier and we would
1146 * end up in a dead-lock.
1147 */
1149 while (1) {
1150 odp_event_t ev;
1151
1152 ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
1153 if (ev == ODP_EVENT_INVALID)
1154 break;
1155 odp_event_free(ev);
1156 }
1157
1158 /* Make sure that latest stat writes are visible to other threads */
1159 odp_mb_full();
1160
1161 /* Wait until pktio devices are stopped */
1162 odp_barrier_wait(&gbl_args->term_barrier);
1163
1164 /* Free remaining events in queues */
1166 while (1) {
1167 odp_event_t ev;
1168
1169 ev = odp_schedule(NULL,
1171
1172 if (ev == ODP_EVENT_INVALID)
1173 break;
1174
1175 odp_event_free(ev);
1176 }
1177
1178 return 0;
1179}
1180
1181/*
1182 * Packet IO worker thread using plain queues
1183 *
1184 * arg thread arguments of type 'thread_args_t *'
1185 */
1186static int run_worker_plain_queue_mode(void *arg)
1187{
1188 const int thr = odp_thread_id();
1189 int pkts;
1190 uint16_t max_burst;
1191 odp_packet_t pkt_tbl[MAX_PKT_BURST];
1192 int dst_idx, num_pktio;
1193 odp_queue_t queue;
1194 odp_pktout_queue_t pktout;
1195 odp_queue_t tx_queue;
1196 int pktio = 0;
1197 thread_args_t *thr_args = arg;
1198 const int thr_idx = thr_args->thr_idx;
1199 stats_t *stats = &thr_args->stats;
1200 const appl_args_t *appl_args = &gbl_args->appl;
1201 uint8_t *const memcpy_src = appl_args->memcpy_bytes ?
1202 &gbl_args->memcpy_data[thr_idx * 2 * appl_args->memcpy_bytes] : NULL;
1203 state_t *state = appl_args->has_state ? &thr_args->state : NULL;
1204 int use_event_queue = gbl_args->appl.out_mode;
1205 int i;
1206
1207 max_burst = gbl_args->appl.burst_rx;
1208
1209 num_pktio = thr_args->num_pktio;
1210 dst_idx = thr_args->pktio[pktio].tx_idx;
1211 queue = thr_args->pktio[pktio].rx_queue;
1212 pktout = thr_args->pktio[pktio].pktout;
1213 tx_queue = thr_args->pktio[pktio].tx_queue;
1214
1215 printf("[%02i] num pktios %i, PKTIN_QUEUE, %s\n", thr, num_pktio,
1216 (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
1217
1218 odp_barrier_wait(&gbl_args->init_barrier);
1219
1220 /* Loop packets */
1221 while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
1222 odp_event_t event[MAX_PKT_BURST];
1223
1224 if (num_pktio > 1) {
1225 dst_idx = thr_args->pktio[pktio].tx_idx;
1226 queue = thr_args->pktio[pktio].rx_queue;
1227 pktout = thr_args->pktio[pktio].pktout;
1228 if (odp_unlikely(use_event_queue))
1229 tx_queue = thr_args->pktio[pktio].tx_queue;
1230
1231 pktio++;
1232 if (pktio == num_pktio)
1233 pktio = 0;
1234 }
1235
1236 pkts = odp_queue_deq_multi(queue, event, max_burst);
1237 if (odp_unlikely(pkts <= 0))
1238 continue;
1239
1240 odp_packet_from_event_multi(pkt_tbl, event, pkts);
1241
1242 prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
1243
1244 pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats, memcpy_src);
1245
1246 if (odp_unlikely(pkts == 0))
1247 continue;
1248
1249 fill_eth_addrs(pkt_tbl, pkts, dst_idx);
1250
1251 send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue, pktout, state,
1252 stats);
1253 }
1254
1255 /* Make sure that latest stat writes are visible to other threads */
1256 odp_mb_full();
1257
1258 /* Wait until pktio devices are stopped */
1259 odp_barrier_wait(&gbl_args->term_barrier);
1260
1261 /* Free remaining events in queues */
1262 for (i = 0; i < num_pktio; i++) {
1263 odp_time_t recv_last = odp_time_local();
1264 odp_time_t since_last;
1265
1266 queue = thr_args->pktio[i].rx_queue;
1267 do {
1268 odp_event_t ev = odp_queue_deq(queue);
1269
1270 if (ev != ODP_EVENT_INVALID) {
1271 recv_last = odp_time_local();
1272 odp_event_free(ev);
1273 }
1274
1275 since_last = odp_time_diff(odp_time_local(), recv_last);
1276 } while (odp_time_to_ns(since_last) < ODP_TIME_SEC_IN_NS);
1277 }
1278
1279 return 0;
1280}
1281
1282/*
1283 * Packet IO worker thread accessing IO resources directly
1284 *
1285 * arg thread arguments of type 'thread_args_t *'
1286 */
1287static int run_worker_direct_mode(void *arg)
1288{
1289 const int thr = odp_thread_id();
1290 int pkts;
1291 uint16_t max_burst;
1292 odp_packet_t pkt_tbl[MAX_PKT_BURST];
1293 int dst_idx, num_pktio;
1294 odp_pktin_queue_t pktin;
1295 odp_pktout_queue_t pktout;
1296 odp_queue_t tx_queue;
1297 int pktio = 0;
1298 thread_args_t *thr_args = arg;
1299 const int thr_idx = thr_args->thr_idx;
1300 stats_t *stats = &thr_args->stats;
1301 const appl_args_t *appl_args = &gbl_args->appl;
1302 uint8_t *const memcpy_src = appl_args->memcpy_bytes ?
1303 &gbl_args->memcpy_data[thr_idx * 2 * appl_args->memcpy_bytes] : NULL;
1304 state_t *state = appl_args->has_state ? &thr_args->state : NULL;
1305 int use_event_queue = gbl_args->appl.out_mode;
1306
1307 max_burst = gbl_args->appl.burst_rx;
1308
1309 num_pktio = thr_args->num_pktio;
1310 dst_idx = thr_args->pktio[pktio].tx_idx;
1311 pktin = thr_args->pktio[pktio].pktin;
1312 pktout = thr_args->pktio[pktio].pktout;
1313 tx_queue = thr_args->pktio[pktio].tx_queue;
1314
1315 printf("[%02i] num pktios %i, PKTIN_DIRECT, %s\n", thr, num_pktio,
1316 (use_event_queue) ? "PKTOUT_QUEUE" : "PKTOUT_DIRECT");
1317
1318 odp_barrier_wait(&gbl_args->init_barrier);
1319
1320 /* Loop packets */
1321 while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
1322 if (num_pktio > 1) {
1323 dst_idx = thr_args->pktio[pktio].tx_idx;
1324 pktin = thr_args->pktio[pktio].pktin;
1325 pktout = thr_args->pktio[pktio].pktout;
1326 if (odp_unlikely(use_event_queue))
1327 tx_queue = thr_args->pktio[pktio].tx_queue;
1328
1329 pktio++;
1330 if (pktio == num_pktio)
1331 pktio = 0;
1332 }
1333
1334 pkts = odp_pktin_recv(pktin, pkt_tbl, max_burst);
1335 if (odp_unlikely(pkts <= 0))
1336 continue;
1337
1338 prefetch_data(appl_args->prefetch, pkt_tbl, pkts);
1339
1340 pkts = process_extra_features(appl_args, pkt_tbl, pkts, stats, memcpy_src);
1341
1342 if (odp_unlikely(pkts == 0))
1343 continue;
1344
1345 fill_eth_addrs(pkt_tbl, pkts, dst_idx);
1346
1347 send_packets(pkt_tbl, pkts, use_event_queue, dst_idx, tx_queue, pktout, state,
1348 stats);
1349 }
1350
1351 /* Make sure that latest stat writes are visible to other threads */
1352 odp_mb_full();
1353
1354 return 0;
1355}
1356
1357static int set_pktin_vector_params(odp_pktin_queue_param_t *pktin_param, odp_pool_t vec_pool,
1358 const odp_pktio_capability_t *pktio_capa)
1359{
1360 uint64_t vec_tmo_ns;
1361 uint32_t vec_size;
1362
1363 pktin_param->vector.enable = true;
1364 pktin_param->vector.pool = vec_pool;
1365
1366 if (gbl_args->appl.vec_size == 0)
1367 vec_size = DEFAULT_VEC_SIZE;
1368 else
1369 vec_size = gbl_args->appl.vec_size;
1370
1371 if (vec_size > pktio_capa->vector.max_size || vec_size < pktio_capa->vector.min_size) {
1372 if (gbl_args->appl.vec_size == 0) {
1373 vec_size = (vec_size > pktio_capa->vector.max_size) ?
1374 pktio_capa->vector.max_size : pktio_capa->vector.min_size;
1375 printf("\nWarning: Modified vector size to %u\n\n", vec_size);
1376 } else {
1377 ODPH_ERR("Invalid pktio vector size %u, valid range [%u, %u]\n",
1378 vec_size, pktio_capa->vector.min_size,
1379 pktio_capa->vector.max_size);
1380 return -1;
1381 }
1382 }
1383 pktin_param->vector.max_size = vec_size;
1384
1385 vec_tmo_ns = gbl_args->appl.vec_tmo_ns < 0 ? DEFAULT_VEC_TMO :
1386 (uint64_t)gbl_args->appl.vec_tmo_ns;
1387
1388 if (vec_tmo_ns > pktio_capa->vector.max_tmo_ns ||
1389 vec_tmo_ns < pktio_capa->vector.min_tmo_ns) {
1390 if (gbl_args->appl.vec_tmo_ns < 0) {
1391 vec_tmo_ns = (vec_tmo_ns > pktio_capa->vector.max_tmo_ns) ?
1392 pktio_capa->vector.max_tmo_ns : pktio_capa->vector.min_tmo_ns;
1393 printf("\nWarning: Modified vector timeout to %" PRIu64 "\n\n", vec_tmo_ns);
1394 } else {
1395 ODPH_ERR("Invalid vector timeout %" PRIu64 ", valid range [%" PRIu64
1396 ", %" PRIu64 "]\n", vec_tmo_ns,
1397 pktio_capa->vector.min_tmo_ns, pktio_capa->vector.max_tmo_ns);
1398 return -1;
1399 }
1400 }
1401 pktin_param->vector.max_tmo_ns = vec_tmo_ns;
1402
1403 return 0;
1404}
1405
1406static int set_event_aggr_config(odp_event_aggr_config_t *config, odp_pool_t vec_pool,
1407 const odp_schedule_capability_t *capa)
1408{
1409 uint64_t vec_tmo_ns;
1410 uint32_t vec_size, max_size;
1411
1412 memset(config, 0, sizeof(odp_event_aggr_config_t));
1413 config->pool = vec_pool;
1414 config->event_type = ODP_EVENT_PACKET;
1415
1416 vec_size = gbl_args->appl.vec_size == 0 ? DEFAULT_VEC_SIZE :
1417 (uint64_t)gbl_args->appl.vec_size;
1418
1419 max_size = ODPH_MIN(capa->aggr.max_size, MAX_EVENT_VEC_SIZE);
1420 if (vec_size > max_size || vec_size < capa->aggr.min_size) {
1421 if (gbl_args->appl.vec_size == 0) {
1422 vec_size = vec_size > max_size ? max_size : capa->aggr.min_size;
1423 printf("\nWarning: Modified vector size to %u\n\n", vec_size);
1424 } else {
1425 ODPH_ERR("Invalid vector size %u, valid range [%u, %u]\n",
1426 vec_size, capa->aggr.min_size, max_size);
1427 return -1;
1428 }
1429 }
1430 config->max_size = vec_size;
1431
1432 vec_tmo_ns = gbl_args->appl.vec_tmo_ns < 0 ? DEFAULT_VEC_TMO :
1433 (uint64_t)gbl_args->appl.vec_tmo_ns;
1434
1435 if (vec_tmo_ns > capa->aggr.max_tmo_ns || vec_tmo_ns < capa->aggr.min_tmo_ns) {
1436 if (gbl_args->appl.vec_tmo_ns < 0) {
1437 vec_tmo_ns = vec_tmo_ns > capa->aggr.max_tmo_ns ?
1438 capa->aggr.max_tmo_ns : capa->aggr.min_tmo_ns;
1439 printf("\nWarning: Modified vector timeout to %" PRIu64 "\n\n", vec_tmo_ns);
1440 } else {
1441 ODPH_ERR("Invalid vector timeout %" PRIu64 ", valid range [%" PRIu64
1442 ", %" PRIu64 "]\n", vec_tmo_ns,
1443 capa->aggr.min_tmo_ns, capa->aggr.max_tmo_ns);
1444 return -1;
1445 }
1446 }
1447 config->max_tmo_ns = vec_tmo_ns;
1448
1449 return 0;
1450}
1451
1452/*
1453 * Create a pktio handle, optionally associating a default input queue.
1454 *
1455 * dev Name of device to open
1456 * index Pktio index
1457 * pool Pool to associate with device for packet RX/TX
1458 *
1459 * Returns 0 on success, -1 on failure
1460 */
1461static int create_pktio(const char *dev, int idx, int num_rx, int num_tx, odp_pool_t pool,
1462 odp_pool_t vec_pool, odp_schedule_group_t group)
1463{
1464 odp_pktio_t pktio;
1465 odp_pktio_param_t pktio_param;
1466 odp_schedule_sync_t sync_mode;
1467 odp_pktio_capability_t pktio_capa;
1468 odp_pktio_config_t config;
1469 odp_pktin_queue_param_t pktin_param;
1470 odp_event_aggr_config_t aggr_config;
1471 odp_pktout_queue_param_t pktout_param;
1472 odp_queue_param_t compl_queue;
1473 odp_pktio_op_mode_t mode_rx;
1474 odp_pktio_op_mode_t mode_tx;
1475 pktin_mode_t in_mode = gbl_args->appl.in_mode;
1476 odp_pktio_info_t info;
1477 uint8_t *addr;
1478
1479 odp_pktio_param_init(&pktio_param);
1480
1481 if (in_mode == PLAIN_QUEUE)
1482 pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
1483 else if (in_mode != DIRECT_RECV) /* pktin_mode SCHED_* */
1484 pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
1485
1486 if (gbl_args->appl.out_mode != PKTOUT_DIRECT)
1487 pktio_param.out_mode = ODP_PKTOUT_MODE_QUEUE;
1488
1489 if (num_rx == 0)
1490 pktio_param.in_mode = ODP_PKTIN_MODE_DISABLED;
1491
1492 if (num_tx == 0)
1493 pktio_param.out_mode = ODP_PKTOUT_MODE_DISABLED;
1494
1495 pktio = odp_pktio_open(dev, pool, &pktio_param);
1496 if (pktio == ODP_PKTIO_INVALID) {
1497 ODPH_ERR("Pktio open failed: %s\n", dev);
1498 return -1;
1499 }
1500
1501 if (odp_pktio_info(pktio, &info)) {
1502 ODPH_ERR("Pktio info failed: %s\n", dev);
1503 return -1;
1504 }
1505
1506 if (odp_pktio_capability(pktio, &pktio_capa)) {
1507 ODPH_ERR("Pktio capability query failed: %s\n", dev);
1508 return -1;
1509 }
1510
1511 odp_pktio_config_init(&config);
1512
1513 if (gbl_args->appl.input_ts) {
1514 if (!pktio_capa.config.pktin.bit.ts_all) {
1515 ODPH_ERR("Packet input timestamping not supported: %s\n", dev);
1516 return -1;
1517 }
1518 config.pktin.bit.ts_all = 1;
1519 }
1520
1522 if (gbl_args->appl.error_check || gbl_args->appl.chksum)
1524
1525 if (gbl_args->appl.chksum) {
1526 config.pktout.bit.ipv4_chksum_ena = 1;
1527 config.pktout.bit.udp_chksum_ena = 1;
1528 config.pktout.bit.tcp_chksum_ena = 1;
1529 }
1530
1531 if (gbl_args->appl.tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED) {
1532 if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT &&
1533 !(pktio_capa.tx_compl.mode_event && pktio_capa.tx_compl.queue_type_sched)) {
1534 ODPH_ERR("Transmit event completion not supported: %s\n", dev);
1535 return -1;
1536 }
1537
1538 if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_POLL &&
1539 !(pktio_capa.tx_compl.mode_poll &&
1540 pktio_capa.tx_compl.max_compl_id >= gbl_args->appl.tx_compl.tot_compl_id)) {
1541 ODPH_ERR("Transmit poll completion not supported: %s\n", dev);
1542 return -1;
1543 }
1544
1545 if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT)
1546 config.tx_compl.mode_event = 1;
1547
1548 if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_POLL) {
1549 config.tx_compl.mode_poll = 1;
1550 config.tx_compl.max_compl_id = gbl_args->appl.tx_compl.tot_compl_id;
1551 }
1552 }
1553
1554 /* Provide hint to pktio that packet references are not used */
1555 config.pktout.bit.no_packet_refs = 1;
1556
1557 if (gbl_args->appl.pause_rx) {
1558 if (!pktio_capa.flow_control.pause_rx) {
1559 ODPH_ERR("Reception of pause frames not supported: %s\n", dev);
1560 return -1;
1561 }
1563 }
1564
1565 if (gbl_args->appl.pause_tx) {
1566 if (!pktio_capa.flow_control.pause_tx) {
1567 ODPH_ERR("Transmission of pause frames not supported: %s\n", dev);
1568 return -1;
1569 }
1571 }
1572
1573 odp_pktio_config(pktio, &config);
1574
1575 if (gbl_args->appl.promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
1576 if (!pktio_capa.set_op.op.promisc_mode) {
1577 ODPH_ERR("Promisc mode set not supported: %s\n", dev);
1578 return -1;
1579 }
1580
1581 /* Enable promisc mode */
1582 if (odp_pktio_promisc_mode_set(pktio, true)) {
1583 ODPH_ERR("Promisc mode enable failed: %s\n", dev);
1584 return -1;
1585 }
1586 }
1587
1588 if (gbl_args->appl.mtu) {
1589 uint32_t maxlen_input = pktio_capa.maxlen.max_input ? gbl_args->appl.mtu : 0;
1590 uint32_t maxlen_output = pktio_capa.maxlen.max_output ? gbl_args->appl.mtu : 0;
1591
1592 if (!pktio_capa.set_op.op.maxlen) {
1593 ODPH_ERR("Modifying interface MTU not supported: %s\n", dev);
1594 return -1;
1595 }
1596
1597 if (maxlen_input &&
1598 (maxlen_input < pktio_capa.maxlen.min_input ||
1599 maxlen_input > pktio_capa.maxlen.max_input)) {
1600 ODPH_ERR("Unsupported MTU value %" PRIu32 " for %s "
1601 "(min %" PRIu32 ", max %" PRIu32 ")\n", maxlen_input, dev,
1602 pktio_capa.maxlen.min_input, pktio_capa.maxlen.max_input);
1603 return -1;
1604 }
1605 if (maxlen_output &&
1606 (maxlen_output < pktio_capa.maxlen.min_output ||
1607 maxlen_output > pktio_capa.maxlen.max_output)) {
1608 ODPH_ERR("Unsupported MTU value %" PRIu32 " for %s "
1609 "(min %" PRIu32 ", max %" PRIu32 ")\n", maxlen_output, dev,
1610 pktio_capa.maxlen.min_output, pktio_capa.maxlen.max_output);
1611 return -1;
1612 }
1613
1614 if (odp_pktio_maxlen_set(pktio, maxlen_input, maxlen_output)) {
1615 ODPH_ERR("Setting MTU failed: %s\n", dev);
1616 return -1;
1617 }
1618 }
1619
1620 odp_pktin_queue_param_init(&pktin_param);
1621 odp_pktout_queue_param_init(&pktout_param);
1622
1623 /* By default use a queue per worker. Sched mode ignores rx side
1624 * setting. */
1625 mode_rx = ODP_PKTIO_OP_MT_UNSAFE;
1626 mode_tx = ODP_PKTIO_OP_MT_UNSAFE;
1627
1628 if (gbl_args->appl.sched_mode) {
1630
1631 if (gbl_args->appl.num_prio) {
1632 prio = gbl_args->appl.prio[idx];
1633 } else {
1635 gbl_args->appl.prio[idx] = prio;
1636 }
1637
1638 if (gbl_args->appl.in_mode == SCHED_ATOMIC)
1639 sync_mode = ODP_SCHED_SYNC_ATOMIC;
1640 else if (gbl_args->appl.in_mode == SCHED_ORDERED)
1641 sync_mode = ODP_SCHED_SYNC_ORDERED;
1642 else
1643 sync_mode = ODP_SCHED_SYNC_PARALLEL;
1644
1645 pktin_param.queue_param.sched.prio = prio;
1646 pktin_param.queue_param.sched.sync = sync_mode;
1647 pktin_param.queue_param.sched.group = group;
1648
1649 if (gbl_args->appl.tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT) {
1650 odp_queue_param_init(&compl_queue);
1651 compl_queue.type = ODP_QUEUE_TYPE_SCHED;
1652 compl_queue.sched.prio = prio;
1653 compl_queue.sched.sync = ODP_SCHED_SYNC_PARALLEL;
1654 compl_queue.sched.group = group;
1655 gbl_args->pktios[idx].compl_q = odp_queue_create(NULL, &compl_queue);
1656
1657 if (gbl_args->pktios[idx].compl_q == ODP_QUEUE_INVALID) {
1658 ODPH_ERR("Creating completion queue failed: %s\n", dev);
1659 return -1;
1660 }
1661 }
1662 }
1663
1664 if (num_rx > (int)pktio_capa.max_input_queues || num_rx > MAX_QUEUES) {
1665 num_rx = ODPH_MIN((int)pktio_capa.max_input_queues, MAX_QUEUES);
1666 mode_rx = ODP_PKTIO_OP_MT;
1667 printf("Warning: %s: maximum number of input queues: %i\n", dev, num_rx);
1668 }
1669
1670 if (num_rx < gbl_args->appl.num_workers)
1671 printf("Warning: %s: sharing %i input queues between %i workers\n",
1672 dev, num_rx, gbl_args->appl.num_workers);
1673
1674 if (num_tx > (int)pktio_capa.max_output_queues || num_tx > MAX_QUEUES) {
1675 int num_tx_orig = num_tx;
1676
1677 num_tx = ODPH_MIN((int)pktio_capa.max_output_queues, MAX_QUEUES);
1678 printf("Warning: %s: sharing %i output queues between %i workers\n",
1679 dev, num_tx, num_tx_orig);
1680 mode_tx = ODP_PKTIO_OP_MT;
1681 }
1682
1683 pktin_param.hash_enable = (num_rx > 1 || gbl_args->appl.flow_aware) ? 1 : 0;
1684 pktin_param.hash_proto.proto.ipv4_udp = 1;
1685 pktin_param.num_queues = num_rx;
1686 pktin_param.op_mode = mode_rx;
1687
1688 pktout_param.op_mode = mode_tx;
1689 pktout_param.num_queues = num_tx;
1690
1691 if (gbl_args->appl.vector_mode == VECTOR_MODE_PACKET) {
1692 if (!pktio_capa.vector.supported) {
1693 ODPH_ERR("Packet vector input not supported: %s\n", dev);
1694 return -1;
1695 }
1696 if (set_pktin_vector_params(&pktin_param, vec_pool, &pktio_capa))
1697 return -1;
1698 }
1699
1700 if (gbl_args->appl.vector_mode == VECTOR_MODE_EVENT) {
1701 odp_schedule_capability_t sched_capa;
1702
1703 if (odp_schedule_capability(&sched_capa)) {
1704 ODPH_ERR("Scheduler capability query failed: %s\n", dev);
1705 return -1;
1706 }
1707
1708 if (!sched_capa.aggr.max_num) {
1709 ODPH_ERR("Event vector input not supported: %s\n", dev);
1710 return -1;
1711 }
1712 if (set_event_aggr_config(&aggr_config, vec_pool, &sched_capa))
1713 return -1;
1714 pktin_param.queue_param.aggr = &aggr_config;
1715 pktin_param.queue_param.num_aggr = 1;
1716 }
1717
1718 if (num_rx > 0 && odp_pktin_queue_config(pktio, &pktin_param)) {
1719 ODPH_ERR("Input queue config failed: %s\n", dev);
1720 return -1;
1721 }
1722
1723 if (num_tx > 0 && odp_pktout_queue_config(pktio, &pktout_param)) {
1724 ODPH_ERR("Output queue config failed: %s\n", dev);
1725 return -1;
1726 }
1727
1728 if (num_rx > 0) {
1729 if (gbl_args->appl.in_mode == DIRECT_RECV) {
1730 if (odp_pktin_queue(pktio, gbl_args->pktios[idx].pktin, num_rx)
1731 != num_rx) {
1732 ODPH_ERR("Pktin queue query failed: %s\n", dev);
1733 return -1;
1734 }
1735 } else {
1736 if (odp_pktin_event_queue(pktio, gbl_args->pktios[idx].rx_q, num_rx)
1737 != num_rx) {
1738 ODPH_ERR("Pktin event queue query failed: %s\n", dev);
1739 return -1;
1740 }
1741 }
1742 }
1743
1744 if (num_tx > 0) {
1745 if (gbl_args->appl.out_mode == PKTOUT_DIRECT) {
1746 if (odp_pktout_queue(pktio, gbl_args->pktios[idx].pktout, num_tx)
1747 != num_tx) {
1748 ODPH_ERR("Pktout queue query failed: %s\n", dev);
1749 return -1;
1750 }
1751 } else {
1752 if (odp_pktout_event_queue(pktio, gbl_args->pktios[idx].tx_q, num_tx)
1753 != num_tx) {
1754 ODPH_ERR("Event queue query failed: %s\n", dev);
1755 return -1;
1756 }
1757 }
1758 }
1759
1760 if (odp_pktio_mac_addr(pktio, gbl_args->port_eth_addr[idx].addr,
1761 ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
1762 ODPH_ERR("Reading interface Ethernet address failed: %s\n", dev);
1763 return -1;
1764 }
1765 addr = gbl_args->port_eth_addr[idx].addr;
1766
1767 printf(" dev: %s, drv: %s, rx_queues: %i, tx_queues: %i, mac: "
1768 "%02x:%02x:%02x:%02x:%02x:%02x\n", dev, info.drv_name, num_rx, num_tx,
1769 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
1770
1771 if (gbl_args->appl.verbose)
1772 odp_pktio_print(pktio);
1773
1774 gbl_args->pktios[idx].num_rx_queue = num_rx;
1775 gbl_args->pktios[idx].num_tx_queue = num_tx;
1776 gbl_args->pktios[idx].pktio = pktio;
1777
1778 return 0;
1779}
1780
1781static int start_pktios(args_t *gbl_args)
1782{
1783 appl_args_t *appl = &gbl_args->appl;
1784 int if_count = appl->if_count;
1785 odp_pktio_t pktios[MAX_PKTIOS];
1786 const char *names[MAX_PKTIOS];
1787
1788 for (int i = 0; i < if_count; i++) {
1789 if (odp_pktio_start(gbl_args->pktios[i].pktio)) {
1790 ODPH_ERR("Error (%s): Pktio start failed.\n", appl->if_names[i]);
1791 return -1;
1792 }
1793
1794 pktios[i] = gbl_args->pktios[i].pktio;
1795 names[i] = appl->if_names[i];
1796 }
1797
1798 /* Wait until all links are up */
1799 if (appl->wait_sec && pktio_common_check_link_status_wait(pktios, names, if_count,
1800 appl->wait_sec) == -1)
1801 return -1;
1802
1803 pktio_common_print_link_info_multi(pktios, names, if_count);
1804
1805 return 0;
1806}
1807
1808static int stop_pktios(args_t *gbl_args)
1809{
1810 uint32_t i;
1811 odp_pktio_t pktio;
1812 int ret = 0;
1813 appl_args_t *appl = &gbl_args->appl;
1814 uint32_t if_count = appl->if_count;
1815
1816 for (i = 0; i < if_count; i++) {
1817 pktio = gbl_args->pktios[i].pktio;
1818
1819 if (pktio == ODP_PKTIO_INVALID)
1820 continue;
1821
1822 if (odp_pktio_stop(pktio)) {
1823 ODPH_ERR("Error (%s): Pktio stop failed.\n", appl->if_names[i]);
1824 ret = -1;
1825 }
1826 }
1827
1828 return ret;
1829}
1830
1831/*
1832 * Print statistics
1833 *
1834 * num_workers Number of worker threads
1835 * thr_stats Pointers to stats storage
1836 * duration Number of seconds to loop in
1837 * timeout Number of seconds for stats calculation
1838 */
1839static int print_speed_stats(int num_workers, stats_t **thr_stats,
1840 int duration, int timeout)
1841{
1842 uint64_t pkts = 0;
1843 uint64_t pkts_prev = 0;
1844 uint64_t pps;
1845 uint64_t rx_drops, tx_drops, tx_c_misses, tx_c_fails, copy_fails;
1846 uint64_t maximum_pps = 0;
1847 int i;
1848 int elapsed = 0;
1849 int stats_enabled = 1;
1850 int loop_forever = (duration == 0);
1851
1852 if (timeout <= 0) {
1853 stats_enabled = 0;
1854 timeout = 1;
1855 }
1856 /* Wait for all threads to be ready*/
1857 odp_barrier_wait(&gbl_args->init_barrier);
1858
1859 do {
1860 pkts = 0;
1861 rx_drops = 0;
1862 tx_drops = 0;
1863 tx_c_misses = 0;
1864 tx_c_fails = 0;
1865 copy_fails = 0;
1866
1867 sleep(timeout);
1868
1869 for (i = 0; i < num_workers; i++) {
1870 pkts += thr_stats[i]->s.packets;
1871 rx_drops += thr_stats[i]->s.rx_drops;
1872 tx_drops += thr_stats[i]->s.tx_drops;
1873 tx_c_misses += thr_stats[i]->s.tx_c_misses;
1874 tx_c_fails += thr_stats[i]->s.tx_c_fails;
1875 copy_fails += thr_stats[i]->s.copy_fails;
1876 }
1877 if (stats_enabled) {
1878 pps = (pkts - pkts_prev) / timeout;
1879 if (pps > maximum_pps)
1880 maximum_pps = pps;
1881 printf("%" PRIu64 " pps, %" PRIu64 " max pps, ", pps,
1882 maximum_pps);
1883
1884 if (gbl_args->appl.packet_copy)
1885 printf("%" PRIu64 " copy fails, ", copy_fails);
1886
1887 if (gbl_args->appl.tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED)
1888 printf("%" PRIu64 " tx compl misses, %" PRIu64 " tx compl fails, ",
1889 tx_c_misses, tx_c_fails);
1890
1891 printf("%" PRIu64 " rx drops, %" PRIu64 " tx drops\n",
1892 rx_drops, tx_drops);
1893
1894 pkts_prev = pkts;
1895 }
1896 elapsed += timeout;
1897 } while (!odp_atomic_load_u32(&gbl_args->exit_threads) && (loop_forever ||
1898 (elapsed < duration)));
1899
1900 if (stats_enabled)
1901 printf("TEST RESULT: %" PRIu64 " maximum packets per second.\n",
1902 maximum_pps);
1903
1904 return pkts > 100 ? 0 : -1;
1905}
1906
1907static void print_port_mapping(void)
1908{
1909 int if_count;
1910 int pktio;
1911
1912 if_count = gbl_args->appl.if_count;
1913
1914 printf("\nPort config\n--------------------\n");
1915
1916 for (pktio = 0; pktio < if_count; pktio++) {
1917 const char *dev = gbl_args->appl.if_names[pktio];
1918
1919 printf("Port %i (%s)\n", pktio, dev);
1920 printf(" rx workers %i\n",
1921 gbl_args->pktios[pktio].num_rx_thr);
1922 printf(" tx workers %i\n",
1923 gbl_args->pktios[pktio].num_tx_thr);
1924 printf(" rx queues %i\n",
1925 gbl_args->pktios[pktio].num_rx_queue);
1926 printf(" tx queues %i\n",
1927 gbl_args->pktios[pktio].num_tx_queue);
1928 }
1929
1930 printf("\n");
1931}
1932
1933/*
1934 * Find the destination port for a given input port
1935 *
1936 * port Input port index
1937 */
1938static int find_dest_port(int port)
1939{
1940 const char *output = gbl_args->appl.output_map[port];
1941
1942 /* Check output mappings first */
1943 if (output != NULL)
1944 for (int i = 0; i < gbl_args->appl.if_count; i++)
1945 if (strcmp(output, gbl_args->appl.if_names[i]) == 0)
1946 return i;
1947
1948 /* Even number of ports */
1949 if (gbl_args->appl.if_count % 2 == 0)
1950 return (port % 2 == 0) ? port + 1 : port - 1;
1951
1952 /* Odd number of ports */
1953 if (port == gbl_args->appl.if_count - 1)
1954 return 0;
1955 else
1956 return port + 1;
1957}
1958
1959/*
1960 * Bind worker threads to interfaces and calculate number of queues needed
1961 *
1962 * less workers (N) than interfaces (M)
1963 * - assign each worker to process every Nth interface
1964 * - workers process inequal number of interfaces, when M is not divisible by N
1965 * - needs only single queue per interface
1966 * otherwise
1967 * - assign an interface to every Mth worker
1968 * - interfaces are processed by inequal number of workers, when N is not
1969 * divisible by M
1970 * - tries to configure a queue per worker per interface
1971 * - shares queues, if interface capability does not allows a queue per worker
1972 */
1973static void bind_workers(void)
1974{
1975 int if_count, num_workers;
1976 int rx_idx, tx_idx, thr, pktio, i;
1977 thread_args_t *thr_args;
1978
1979 if_count = gbl_args->appl.if_count;
1980 num_workers = gbl_args->appl.num_workers;
1981
1982 if (gbl_args->appl.sched_mode) {
1983 /* all threads receive and send on all pktios */
1984 for (i = 0; i < if_count; i++) {
1985 gbl_args->pktios[i].num_rx_thr = num_workers;
1986 gbl_args->pktios[i].num_tx_thr = num_workers;
1987 }
1988
1989 for (thr = 0; thr < num_workers; thr++) {
1990 thr_args = &gbl_args->thread_args[thr];
1991 thr_args->num_pktio = if_count;
1992
1993 /* In sched mode, pktios are not cross connected with
1994 * local pktio indexes */
1995 for (i = 0; i < if_count; i++) {
1996 thr_args->pktio[i].rx_idx = i;
1997 thr_args->pktio[i].tx_idx = i;
1998 }
1999 }
2000 } else {
2001 /* initialize port forwarding table */
2002 for (rx_idx = 0; rx_idx < if_count; rx_idx++)
2003 gbl_args->dst_port[rx_idx] = find_dest_port(rx_idx);
2004
2005 if (if_count > num_workers) {
2006 /* Less workers than pktios. Assign single worker per
2007 * pktio. */
2008 thr = 0;
2009
2010 for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
2011 thr_args = &gbl_args->thread_args[thr];
2012 pktio = thr_args->num_pktio;
2013 /* Cross connect rx to tx */
2014 tx_idx = gbl_args->dst_port[rx_idx];
2015 thr_args->pktio[pktio].rx_idx = rx_idx;
2016 thr_args->pktio[pktio].tx_idx = tx_idx;
2017 thr_args->num_pktio++;
2018
2019 gbl_args->pktios[rx_idx].num_rx_thr++;
2020 gbl_args->pktios[tx_idx].num_tx_thr++;
2021
2022 thr++;
2023 if (thr >= num_workers)
2024 thr = 0;
2025 }
2026 } else {
2027 /* More workers than pktios. Assign at least one worker
2028 * per pktio. */
2029 rx_idx = 0;
2030
2031 for (thr = 0; thr < num_workers; thr++) {
2032 thr_args = &gbl_args->thread_args[thr];
2033 pktio = thr_args->num_pktio;
2034 /* Cross connect rx to tx */
2035 tx_idx = gbl_args->dst_port[rx_idx];
2036 thr_args->pktio[pktio].rx_idx = rx_idx;
2037 thr_args->pktio[pktio].tx_idx = tx_idx;
2038 thr_args->num_pktio++;
2039
2040 gbl_args->pktios[rx_idx].num_rx_thr++;
2041 gbl_args->pktios[tx_idx].num_tx_thr++;
2042
2043 rx_idx++;
2044 if (rx_idx >= if_count)
2045 rx_idx = 0;
2046 }
2047 }
2048 }
2049}
2050
2051/*
2052 * Bind queues to threads and fill in missing thread arguments (handles)
2053 */
2054static void bind_queues(void)
2055{
2056 int num_workers;
2057 int thr, i;
2058
2059 num_workers = gbl_args->appl.num_workers;
2060
2061 printf("\nQueue binding (indexes)\n-----------------------\n");
2062
2063 for (thr = 0; thr < num_workers; thr++) {
2064 int rx_idx, tx_idx;
2065 thread_args_t *thr_args = &gbl_args->thread_args[thr];
2066 int num = thr_args->num_pktio;
2067
2068 printf("worker %i\n", thr);
2069
2070 for (i = 0; i < num; i++) {
2071 int rx_queue, tx_queue;
2072
2073 rx_idx = thr_args->pktio[i].rx_idx;
2074 tx_idx = thr_args->pktio[i].tx_idx;
2075 rx_queue = gbl_args->pktios[rx_idx].next_rx_queue;
2076 tx_queue = gbl_args->pktios[tx_idx].next_tx_queue;
2077
2078 thr_args->pktio[i].rx_queue_idx = rx_queue;
2079 thr_args->pktio[i].tx_queue_idx = tx_queue;
2080 thr_args->pktio[i].pktin =
2081 gbl_args->pktios[rx_idx].pktin[rx_queue];
2082 thr_args->pktio[i].rx_queue =
2083 gbl_args->pktios[rx_idx].rx_q[rx_queue];
2084 thr_args->pktio[i].pktout =
2085 gbl_args->pktios[tx_idx].pktout[tx_queue];
2086 thr_args->pktio[i].tx_queue =
2087 gbl_args->pktios[tx_idx].tx_q[tx_queue];
2088
2089 if (!gbl_args->appl.sched_mode)
2090 printf(" rx: pktio %i, queue %i\n",
2091 rx_idx, rx_queue);
2092
2093 printf(" tx: pktio %i, queue %i\n",
2094 tx_idx, tx_queue);
2095
2096 rx_queue++;
2097 tx_queue++;
2098
2099 if (rx_queue >= gbl_args->pktios[rx_idx].num_rx_queue)
2100 rx_queue = 0;
2101 if (tx_queue >= gbl_args->pktios[tx_idx].num_tx_queue)
2102 tx_queue = 0;
2103
2104 gbl_args->pktios[rx_idx].next_rx_queue = rx_queue;
2105 gbl_args->pktios[tx_idx].next_tx_queue = tx_queue;
2106 }
2107 }
2108
2109 printf("\n");
2110}
2111
2112static void init_state(const appl_args_t *args, state_t *state, int thr_idx)
2113{
2114 const uint32_t cnt = args->tx_compl.thr_compl_id + 1;
2115
2116 state->tx_compl.opt.mode = args->tx_compl.mode;
2117 state->tx_compl.init = thr_idx * cnt;
2118 state->tx_compl.max = state->tx_compl.init + cnt - 1;
2119 state->tx_compl.free_head = state->tx_compl.init;
2120 state->tx_compl.poll_head = state->tx_compl.init;
2121 state->tx_compl.num_act = 0;
2122 state->tx_compl.max_act = state->tx_compl.max - state->tx_compl.init + 1;
2123 state->tx_compl.interval = args->tx_compl.nth;
2124 state->tx_compl.next_req = state->tx_compl.interval;
2125}
2126
2127static void init_port_lookup_tbl(void)
2128{
2129 int rx_idx, if_count;
2130
2131 if_count = gbl_args->appl.if_count;
2132
2133 for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
2134 odp_pktio_t pktio = gbl_args->pktios[rx_idx].pktio;
2135 int pktio_idx = odp_pktio_index(pktio);
2136 int dst_port = find_dest_port(rx_idx);
2137
2138 if (pktio_idx < 0) {
2139 ODPH_ERR("Reading pktio (%s) index failed: %i\n",
2140 gbl_args->appl.if_names[rx_idx], pktio_idx);
2141
2142 exit(EXIT_FAILURE);
2143 }
2144
2145 gbl_args->dst_port_from_idx[pktio_idx] = dst_port;
2146 }
2147}
2148
2149static void set_cache_stash(odp_cache_stash_region_t *region, uint32_t level, uint32_t len,
2150 uint32_t offset)
2151{
2152 if (level == 0) {
2153 region->l2.len = len;
2154 region->l2.offset = offset;
2155 } else {
2156 region->l3.len = len;
2157 region->l3.offset = offset;
2158 }
2159}
2160
2161static int parse_cache_stash_config(char *optarg, appl_args_t *appl)
2162{
2163 uint32_t region, level, offset, len;
2164 odp_cache_stash_region_t *stash_region;
2165 odp_cache_stash_config_t *stash_config = &appl->cache_stash_config;
2166
2167 if (sscanf(optarg, "%u,%u,%u,%u", &region, &level, &offset, &len) != 4) {
2168 ODPH_ERR("Invalid number of arguments for cache stashing\n");
2169 return -1;
2170 }
2171
2172 if (region > 3) {
2173 ODPH_ERR("Invalid region for cache stashing: %u\n", region);
2174 return -1;
2175 }
2176
2177 if (level > 1) {
2178 ODPH_ERR("Invalid cache level for cache stashing: %u\n", level);
2179 return -1;
2180 }
2181
2182 if (len == 0) {
2183 ODPH_ERR("Invalid len for cache stashing: %u\n", len);
2184 return -1;
2185 }
2186
2187 switch (region) {
2188 case 0: /* event_metadata */
2189 stash_region = &stash_config->event_metadata;
2190 if (level == 0)
2191 stash_config->regions.event_metadata_l2 = 1;
2192 else
2193 stash_config->regions.event_metadata_l3 = 1;
2194 break;
2195 case 1: /* event_data */
2196 stash_region = &stash_config->event_data;
2197 if (level == 0)
2198 stash_config->regions.event_data_l2 = 1;
2199 else
2200 stash_config->regions.event_data_l3 = 1;
2201 break;
2202 case 2: /* event_user_area */
2203 stash_region = &stash_config->event_user_area;
2204 if (level == 0)
2205 stash_config->regions.event_user_area_l2 = 1;
2206 else
2207 stash_config->regions.event_user_area_l3 = 1;
2208 break;
2209 default: /* queue_context */
2210 stash_region = &stash_config->queue_context;
2211 if (level == 0)
2212 stash_config->regions.queue_context_l2 = 1;
2213 else
2214 stash_config->regions.queue_context_l3 = 1;
2215 break;
2216 }
2217
2218 set_cache_stash(stash_region, level, len, offset);
2219
2220 return 0;
2221}
2222
2223/*
2224 * Print usage information
2225 */
2226static void usage(char *progname)
2227{
2228 printf("\n"
2229 "OpenDataPlane L2 forwarding application.\n"
2230 "\n"
2231 "Usage: %s [options]\n"
2232 "\n"
2233 " E.g. %s -i eth0,eth1,eth2,eth3 -m 0 -t 1\n"
2234 " In the above example,\n"
2235 " eth0 will send pkts to eth1 and vice versa\n"
2236 " eth2 will send pkts to eth3 and vice versa\n"
2237 "\n"
2238 "Mandatory OPTIONS:\n"
2239 " -i, --interface <name> Eth interfaces (comma-separated, no spaces)\n"
2240 " Interface count min 1, max %i\n"
2241 "\n"
2242 "Optional OPTIONS:\n"
2243 " -m, --mode <arg> Packet input mode\n"
2244 " 0: Direct mode: PKTIN_MODE_DIRECT (default)\n"
2245 " 1: Scheduler mode with parallel queues:\n"
2246 " PKTIN_MODE_SCHED + SCHED_SYNC_PARALLEL\n"
2247 " 2: Scheduler mode with atomic queues:\n"
2248 " PKTIN_MODE_SCHED + SCHED_SYNC_ATOMIC\n"
2249 " 3: Scheduler mode with ordered queues:\n"
2250 " PKTIN_MODE_SCHED + SCHED_SYNC_ORDERED\n"
2251 " 4: Plain queue mode: PKTIN_MODE_QUEUE\n"
2252 " -o, --out_mode <arg> Packet output mode\n"
2253 " 0: Direct mode: PKTOUT_MODE_DIRECT (default)\n"
2254 " 1: Queue mode: PKTOUT_MODE_QUEUE\n"
2255 " -O, --output_map <list> List of destination ports for passed interfaces\n"
2256 " (comma-separated, no spaces). Ordering follows\n"
2257 " the '--interface' option, e.g. passing\n"
2258 " '-i eth0,eth1' and '-O eth0,eth1' would result\n"
2259 " in eth0 and eth1 looping packets back.\n"
2260 " -c, --count <num> CPU count, 0=all available, default=1\n"
2261 " -t, --time <sec> Time in seconds to run.\n"
2262 " -a, --accuracy <sec> Time in seconds get print statistics\n"
2263 " (default is 1 second).\n"
2264 " -d, --dst_change <arg> 0: Don't change packets' dst eth addresses\n"
2265 " 1: Change packets' dst eth addresses (default)\n"
2266 " -s, --src_change <arg> 0: Don't change packets' src eth addresses\n"
2267 " 1: Change packets' src eth addresses (default)\n"
2268 " -r, --dst_addr <addr> Destination addresses (comma-separated, no\n"
2269 " spaces) Requires also the -d flag to be set\n"
2270 " -e, --error_check <arg> 0: Don't check packet errors (default)\n"
2271 " 1: Check packet errors\n"
2272 " -k, --chksum <arg> 0: Don't use checksum offload (default)\n"
2273 " 1: Use checksum offload\n",
2274 NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS);
2275
2276 printf(" -g, --groups <num> Number of new groups to create (1 ... num).\n"
2277 " Interfaces are placed into the groups in round\n"
2278 " robin.\n"
2279 " 0: Use SCHED_GROUP_ALL (default)\n"
2280 " -1: Use SCHED_GROUP_WORKER\n"
2281 " -G, --group_mode <arg> Select how threads join new groups\n"
2282 " (when -g > 0)\n"
2283 " 0: All threads join all created groups\n"
2284 " (default)\n"
2285 " 1: All threads join first N created groups.\n"
2286 " N is number of interfaces (== active\n"
2287 " groups).\n"
2288 " 2: Each thread joins a part of the first N\n"
2289 " groups (in round robin).\n"
2290 " -I, --prio <prio list> Schedule priority of packet input queues.\n"
2291 " Comma separated list of priorities (no spaces).\n"
2292 " A value per interface. All queues of an\n"
2293 " interface have the same priority. Values must\n"
2294 " be between odp_schedule_min_prio and\n"
2295 " odp_schedule_max_prio.\n"
2296 " odp_schedule_default_prio is used by default.\n"
2297 " -b, --burst_rx <num> 0: Use max burst size (default)\n"
2298 " num: Max number of packets per receive call\n"
2299 " -q, --rx_queues <num> Number of RX queues per interface in scheduler\n"
2300 " mode\n"
2301 " 0: RX queue per worker CPU (default)\n"
2302 " -p, --packet_copy 0: Don't copy packet (default)\n"
2303 " 1: Create and send copy of the received packet.\n"
2304 " Free the original packet.\n"
2305 " -R, --data_rd <num> Number of packet data words (uint64_t) to read\n"
2306 " from every received packet. Number of words is\n"
2307 " rounded down to fit into the first segment of a\n"
2308 " packet. Default is 0.\n"
2309 " -E, --memcpy <num> Number of bytes to memcpy per RX burst before\n"
2310 " forwarding packets. Default: 0.\n"
2311 " -W, --wait_ns <ns> Number of nsecs to wait per receive burst before\n"
2312 " forwarding packets. Default: 0.\n"
2313 " -y, --pool_per_if Create a packet (and packet vector) pool per\n"
2314 " interface.\n"
2315 " 0: Share a single pool between all interfaces\n"
2316 " (default)\n"
2317 " 1: Create a pool per interface\n"
2318 " -n, --num_pkt <num> Number of packets per pool. Default is 16k or\n"
2319 " the maximum capability. Use 0 for the default.\n"
2320 " -u, --vector_mode <num> Enable vector mode.\n"
2321 " Supported only with scheduler packet input\n"
2322 " modes (1-3).\n"
2323 " 0: Disabled (default)\n"
2324 " 1: Event vector mode\n"
2325 " 2: Packet vector mode\n"
2326 " -w, --num_vec <num> Number of vectors per pool.\n"
2327 " Default is num_pkts divided by vec_size.\n"
2328 " -x, --vec_size <num> Vector size (default %i).\n"
2329 " -z, --vec_tmo_ns <ns> Vector timeout in ns (default %llu ns).\n",
2330 DEFAULT_VEC_SIZE, DEFAULT_VEC_TMO);
2331
2332 printf(" -M, --mtu <len> Interface MTU in bytes.\n"
2333 " -P, --promisc_mode Enable promiscuous mode.\n"
2334 " -l, --packet_len <len> Maximum length of packets supported\n"
2335 " (default %d).\n"
2336 " -L, --seg_len <len> Packet pool segment length\n"
2337 " (default equal to packet length).\n"
2338 " -F, --prefetch <num> Prefetch packet data in 64 byte multiples\n"
2339 " (default 1).\n"
2340 " -f, --flow_aware Enable flow aware scheduling.\n"
2341 " -T, --input_ts Enable packet input timestamping.\n"
2342 " -C, --tx_compl <mode,n,max_id> Enable transmit completion with a specified\n"
2343 " completion mode for nth packet, with maximum\n"
2344 " completion ID per worker thread in case of poll\n"
2345 " completion (comma-separated, no spaces).\n"
2346 " 0: Event completion mode\n"
2347 " 1: Poll completion mode\n"
2348 " -X, --flow_control <mode> Ethernet flow control mode.\n"
2349 " 0: Flow control disabled (default)\n"
2350 " 1: Enable reception of pause frames\n"
2351 " 2: Enable transmission of pause frames\n"
2352 " 3: Enable reception and transmission of pause\n"
2353 " frames\n"
2354 " --cache_stash <num> Enable common group level cache stashing. Format: region,level,offset,len\n"
2355 " region: 0: Event metadata, 1: Event data, 2: Event user area, 3: Queue context\n"
2356 " level: 0: L2, 1: L3\n"
2357 " offset/len: in bytes\n"
2358 " E.g.: 1,0,16,32 enables 32-byte L2 stash on event data with a 16-byte offset\n"
2359 " A region can be cached on multiple levels by specifying this option more than once.\n"
2360 " Similarly, to stash multiple regions, use --cache_stash multiple times.\n"
2361 " --schedule_prefetch <num> Number of events to be prefetched for scheduling. Default: 0.\n"
2362 " --wait_link <sec> Wait up to <sec> seconds for network links to be up.\n"
2363 " Default: 0 (don't check link status)\n"
2364 " -v, --verbose Verbose output.\n"
2365 " -V, --verbose_pkt Print debug information on every received\n"
2366 " packet.\n"
2367 " -h, --help Display help and exit.\n\n"
2368 "\n", POOL_PKT_LEN);
2369}
2370
2371/*
2372 * Parse and store the command line arguments
2373 *
2374 * argc argument count
2375 * argv[] argument vector
2376 * appl_args Store application arguments here
2377 */
2378static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
2379{
2380 int opt;
2381 char *token;
2382 char *tmp_str, *tmp;
2383 size_t str_len, len;
2384 int i;
2385 static const struct option longopts[] = {
2386 {"count", required_argument, NULL, 'c'},
2387 {"time", required_argument, NULL, 't'},
2388 {"accuracy", required_argument, NULL, 'a'},
2389 {"interface", required_argument, NULL, 'i'},
2390 {"mode", required_argument, NULL, 'm'},
2391 {"memcpy", required_argument, NULL, 'E'},
2392 {"out_mode", required_argument, NULL, 'o'},
2393 {"output_map", required_argument, NULL, 'O'},
2394 {"dst_addr", required_argument, NULL, 'r'},
2395 {"dst_change", required_argument, NULL, 'd'},
2396 {"src_change", required_argument, NULL, 's'},
2397 {"error_check", required_argument, NULL, 'e'},
2398 {"chksum", required_argument, NULL, 'k'},
2399 {"groups", required_argument, NULL, 'g'},
2400 {"group_mode", required_argument, NULL, 'G'},
2401 {"prio", required_argument, NULL, 'I'},
2402 {"burst_rx", required_argument, NULL, 'b'},
2403 {"rx_queues", required_argument, NULL, 'q'},
2404 {"packet_copy", required_argument, NULL, 'p'},
2405 {"data_rd", required_argument, NULL, 'R'},
2406 {"pool_per_if", required_argument, NULL, 'y'},
2407 {"num_pkt", required_argument, NULL, 'n'},
2408 {"num_vec", required_argument, NULL, 'w'},
2409 {"wait_ns", required_argument, NULL, 'W'},
2410 {"wait_link", required_argument, NULL, OPT_WAIT_LINK},
2411 {"vec_size", required_argument, NULL, 'x'},
2412 {"vec_tmo_ns", required_argument, NULL, 'z'},
2413 {"vector_mode", required_argument, NULL, 'u'},
2414 {"mtu", required_argument, NULL, 'M'},
2415 {"promisc_mode", no_argument, NULL, 'P'},
2416 {"packet_len", required_argument, NULL, 'l'},
2417 {"seg_len", required_argument, NULL, 'L'},
2418 {"prefetch", required_argument, NULL, 'F'},
2419 {"flow_aware", no_argument, NULL, 'f'},
2420 {"input_ts", no_argument, NULL, 'T'},
2421 {"tx_compl", required_argument, NULL, 'C'},
2422 {"flow_control", required_argument, NULL, 'X'},
2423 {"cache_stash", required_argument, NULL, OPT_CACHE_STASH},
2424 {"schedule_prefetch", required_argument, NULL, OPT_SCHED_PREFETCH},
2425 {"verbose", no_argument, NULL, 'v'},
2426 {"verbose_pkt", no_argument, NULL, 'V'},
2427 {"help", no_argument, NULL, 'h'},
2428 {NULL, 0, NULL, 0}
2429 };
2430
2431 static const char *shortopts = "+c:t:a:i:m:o:O:r:d:s:e:E:k:g:G:I:"
2432 "b:q:p:R:y:n:l:L:w:W:x:X:z:M:F:u:PfTC:vVh";
2433
2434 appl_args->accuracy = 1; /* get and print pps stats second */
2435 appl_args->cpu_count = 1; /* use one worker by default */
2436 appl_args->dst_change = 1; /* change eth dst address by default */
2437 appl_args->src_change = 1; /* change eth src address by default */
2438 appl_args->packet_len = POOL_PKT_LEN;
2439 appl_args->seg_len = UINT32_MAX;
2440 appl_args->vector_mode = VECTOR_MODE_DISABLED;
2441 appl_args->vec_tmo_ns = -1;
2442 appl_args->prefetch = 1;
2443
2444 while (1) {
2445 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
2446
2447 if (opt == -1)
2448 break; /* No more options */
2449
2450 switch (opt) {
2451 case 'c':
2452 appl_args->cpu_count = atoi(optarg);
2453 break;
2454 case 't':
2455 appl_args->time = atoi(optarg);
2456 break;
2457 case 'a':
2458 appl_args->accuracy = atoi(optarg);
2459 break;
2460 case 'r':
2461 len = strlen(optarg);
2462 if (len == 0) {
2463 ODPH_ERR("Bad dest address string\n");
2464 exit(EXIT_FAILURE);
2465 }
2466
2467 str_len = len + 1;
2468
2469 tmp_str = malloc(str_len);
2470 if (tmp_str == NULL) {
2471 ODPH_ERR("Dest address malloc() failed\n");
2472 exit(EXIT_FAILURE);
2473 }
2474
2475 /* store the mac addresses names */
2476 memcpy(tmp_str, optarg, str_len);
2477 for (token = strtok(tmp_str, ","), i = 0;
2478 token != NULL; token = strtok(NULL, ","), i++) {
2479 if (i >= MAX_PKTIOS) {
2480 ODPH_ERR("Too many MAC addresses\n");
2481 exit(EXIT_FAILURE);
2482 }
2483 if (odph_eth_addr_parse(&appl_args->addrs[i], token) != 0) {
2484 ODPH_ERR("Invalid MAC address\n");
2485 exit(EXIT_FAILURE);
2486 }
2487 }
2488 appl_args->addr_count = i;
2489 if (appl_args->addr_count < 1) {
2490 ODPH_ERR("Bad dest address count\n");
2491 exit(EXIT_FAILURE);
2492 }
2493 free(tmp_str);
2494 break;
2495 case 'i':
2496 len = strlen(optarg);
2497 if (len == 0) {
2498 ODPH_ERR("Bad pktio interface string\n");
2499 exit(EXIT_FAILURE);
2500 }
2501
2502 str_len = len + 1;
2503
2504 appl_args->if_str = malloc(str_len);
2505 if (appl_args->if_str == NULL) {
2506 ODPH_ERR("Pktio interface malloc() failed\n");
2507 exit(EXIT_FAILURE);
2508 }
2509
2510 /* count the number of tokens separated by ',' */
2511 memcpy(appl_args->if_str, optarg, str_len);
2512 for (token = strtok(appl_args->if_str, ","), i = 0;
2513 token != NULL;
2514 token = strtok(NULL, ","), i++)
2515 ;
2516
2517 appl_args->if_count = i;
2518
2519 if (appl_args->if_count < 1 || appl_args->if_count > MAX_PKTIOS) {
2520 ODPH_ERR("Bad pktio interface count: %i\n", appl_args->if_count);
2521 exit(EXIT_FAILURE);
2522 }
2523
2524 /* allocate storage for the if names */
2525 appl_args->if_names = calloc(appl_args->if_count, sizeof(char *));
2526
2527 /* store the if names (reset names string) */
2528 memcpy(appl_args->if_str, optarg, str_len);
2529 for (token = strtok(appl_args->if_str, ","), i = 0;
2530 token != NULL; token = strtok(NULL, ","), i++) {
2531 appl_args->if_names[i] = token;
2532 }
2533 break;
2534 case 'm':
2535 i = atoi(optarg);
2536 if (i == 1)
2537 appl_args->in_mode = SCHED_PARALLEL;
2538 else if (i == 2)
2539 appl_args->in_mode = SCHED_ATOMIC;
2540 else if (i == 3)
2541 appl_args->in_mode = SCHED_ORDERED;
2542 else if (i == 4)
2543 appl_args->in_mode = PLAIN_QUEUE;
2544 else
2545 appl_args->in_mode = DIRECT_RECV;
2546 break;
2547 case 'o':
2548 i = atoi(optarg);
2549 if (i != 0)
2550 appl_args->out_mode = PKTOUT_QUEUE;
2551 break;
2552 case 'O':
2553 if (strlen(optarg) == 0) {
2554 ODPH_ERR("Bad output map string\n");
2555 exit(EXIT_FAILURE);
2556 }
2557
2558 tmp_str = strdup(optarg);
2559
2560 if (tmp_str == NULL) {
2561 ODPH_ERR("Output map string duplication failed\n");
2562 exit(EXIT_FAILURE);
2563 }
2564
2565 token = strtok(tmp_str, ",");
2566
2567 while (token) {
2568 if (appl_args->num_om >= MAX_PKTIOS) {
2569 ODPH_ERR("Bad output map element count\n");
2570 exit(EXIT_FAILURE);
2571 }
2572
2573 appl_args->output_map[appl_args->num_om] = strdup(token);
2574
2575 if (appl_args->output_map[appl_args->num_om] == NULL) {
2576 ODPH_ERR("Output map element duplication failed\n");
2577 exit(EXIT_FAILURE);
2578 }
2579
2580 appl_args->num_om++;
2581 token = strtok(NULL, ",");
2582 }
2583
2584 free(tmp_str);
2585 break;
2586 case 'd':
2587 appl_args->dst_change = atoi(optarg);
2588 break;
2589 case 's':
2590 appl_args->src_change = atoi(optarg);
2591 break;
2592 case 'e':
2593 appl_args->error_check = atoi(optarg);
2594 break;
2595 case 'E':
2596 appl_args->memcpy_bytes = atoll(optarg);
2597 break;
2598 case 'k':
2599 appl_args->chksum = atoi(optarg);
2600 break;
2601 case 'g':
2602 appl_args->num_groups = atoi(optarg);
2603 break;
2604 case 'G':
2605 appl_args->group_mode = atoi(optarg);
2606 break;
2607 case 'I':
2608 len = strlen(optarg);
2609 if (len == 0) {
2610 ODPH_ERR("Bad priority list\n");
2611 exit(EXIT_FAILURE);
2612 }
2613
2614 str_len = len + 1;
2615
2616 tmp_str = malloc(str_len);
2617 if (tmp_str == NULL) {
2618 ODPH_ERR("Priority list malloc() failed\n");
2619 exit(EXIT_FAILURE);
2620 }
2621
2622 memcpy(tmp_str, optarg, str_len);
2623 token = strtok(tmp_str, ",");
2624
2625 for (i = 0; token != NULL; token = strtok(NULL, ","), i++) {
2626 if (i >= MAX_PKTIOS) {
2627 ODPH_ERR("Too many priorities\n");
2628 exit(EXIT_FAILURE);
2629 }
2630
2631 appl_args->prio[i] = atoi(token);
2632 appl_args->num_prio++;
2633 }
2634
2635 if (appl_args->num_prio == 0) {
2636 ODPH_ERR("Bad priority list\n");
2637 exit(EXIT_FAILURE);
2638 }
2639
2640 free(tmp_str);
2641 break;
2642 case 'b':
2643 appl_args->burst_rx = atoi(optarg);
2644 break;
2645 case 'q':
2646 appl_args->rx_queues = atoi(optarg);
2647 break;
2648 case 'p':
2649 appl_args->packet_copy = atoi(optarg);
2650 break;
2651 case 'R':
2652 appl_args->data_rd = atoi(optarg);
2653 break;
2654 case 'y':
2655 appl_args->pool_per_if = atoi(optarg);
2656 break;
2657 case 'n':
2658 appl_args->num_pkt = atoi(optarg);
2659 break;
2660 case 'l':
2661 appl_args->packet_len = atoi(optarg);
2662 break;
2663 case 'L':
2664 appl_args->seg_len = atoi(optarg);
2665 break;
2666 case 'M':
2667 appl_args->mtu = atoi(optarg);
2668 break;
2669 case 'P':
2670 appl_args->promisc_mode = 1;
2671 break;
2672 case 'u':
2673 appl_args->vector_mode = atoi(optarg);
2674 break;
2675 case 'w':
2676 appl_args->num_vec = atoi(optarg);
2677 break;
2678 case 'W':
2679 appl_args->wait_ns = atoll(optarg);
2680 break;
2681 case 'x':
2682 appl_args->vec_size = atoi(optarg);
2683 break;
2684 case 'X':
2685 appl_args->flow_control = atoi(optarg);
2686 if (appl_args->flow_control == 1 || appl_args->flow_control == 3)
2687 appl_args->pause_rx = true;
2688 if (appl_args->flow_control == 2 || appl_args->flow_control == 3)
2689 appl_args->pause_tx = true;
2690 break;
2691 case 'z':
2692 appl_args->vec_tmo_ns = atoi(optarg);
2693 break;
2694 case OPT_WAIT_LINK:
2695 appl_args->wait_sec = atoi(optarg);
2696 break;
2697 case 'F':
2698 appl_args->prefetch = atoi(optarg);
2699 break;
2700 case 'f':
2701 appl_args->flow_aware = 1;
2702 break;
2703 case 'T':
2704 appl_args->input_ts = 1;
2705 break;
2706 case 'C':
2707 if (strlen(optarg) == 0) {
2708 ODPH_ERR("Bad transmit completion parameter string\n");
2709 exit(EXIT_FAILURE);
2710 }
2711
2712 tmp_str = strdup(optarg);
2713
2714 if (tmp_str == NULL) {
2715 ODPH_ERR("Transmit completion parameter string duplication"
2716 " failed\n");
2717 exit(EXIT_FAILURE);
2718 }
2719
2720 tmp = strtok(tmp_str, ",");
2721
2722 if (tmp == NULL) {
2723 ODPH_ERR("Invalid transmit completion parameter format\n");
2724 exit(EXIT_FAILURE);
2725 }
2726
2727 i = atoi(tmp);
2728
2729 if (i == 0)
2730 appl_args->tx_compl.mode = ODP_PACKET_TX_COMPL_EVENT;
2731 else if (i == 1)
2732 appl_args->tx_compl.mode = ODP_PACKET_TX_COMPL_POLL;
2733
2734 tmp = strtok(NULL, ",");
2735
2736 if (tmp == NULL) {
2737 ODPH_ERR("Invalid transmit completion parameter format\n");
2738 exit(EXIT_FAILURE);
2739 }
2740
2741 appl_args->tx_compl.nth = atoi(tmp);
2742
2743 if (appl_args->tx_compl.mode == ODP_PACKET_TX_COMPL_POLL) {
2744 tmp = strtok(NULL, ",");
2745
2746 if (tmp == NULL) {
2747 ODPH_ERR("Invalid transmit completion parameter format\n");
2748 exit(EXIT_FAILURE);
2749 }
2750
2751 appl_args->tx_compl.thr_compl_id = atoi(tmp);
2752 }
2753
2754 free(tmp_str);
2755 break;
2756 case OPT_CACHE_STASH:
2757 if (parse_cache_stash_config(optarg, appl_args))
2758 exit(EXIT_FAILURE);
2759 break;
2760 case OPT_SCHED_PREFETCH:
2761 appl_args->sched_prefetch = atoi(optarg);
2762 break;
2763 case 'v':
2764 appl_args->verbose = 1;
2765 break;
2766 case 'V':
2767 appl_args->verbose_pkt = 1;
2768 break;
2769 case 'h':
2770 usage(argv[0]);
2771 exit(EXIT_SUCCESS);
2772 break;
2773 default:
2774 break;
2775 }
2776 }
2777
2778 if (appl_args->if_count == 0) {
2779 ODPH_ERR("No pktio interfaces\n");
2780 exit(EXIT_FAILURE);
2781 }
2782
2783 if (appl_args->num_om && appl_args->num_om != appl_args->if_count) {
2784 ODPH_ERR("Different number of output mappings and pktio interfaces\n");
2785 exit(EXIT_FAILURE);
2786 }
2787
2788 if (appl_args->num_prio && appl_args->num_prio != appl_args->if_count) {
2789 ODPH_ERR("Different number of priorities and pktio interfaces\n");
2790 exit(EXIT_FAILURE);
2791 }
2792
2793 if (appl_args->addr_count != 0 && appl_args->addr_count != appl_args->if_count) {
2794 ODPH_ERR("Number of dest addresses differs from number of interfaces\n");
2795 exit(EXIT_FAILURE);
2796 }
2797
2798 if (appl_args->burst_rx > MAX_PKT_BURST) {
2799 ODPH_ERR("Burst size (%i) too large. Maximum is %i.\n",
2800 appl_args->burst_rx, MAX_PKT_BURST);
2801 exit(EXIT_FAILURE);
2802 }
2803
2804 if (appl_args->rx_queues > MAX_QUEUES) {
2805 ODPH_ERR("Number of RX queues per interface (%i) too large. Maximum is %i.\n",
2806 appl_args->rx_queues, MAX_QUEUES);
2807 exit(EXIT_FAILURE);
2808 }
2809
2810 if (appl_args->vector_mode != VECTOR_MODE_DISABLED &&
2811 appl_args->vector_mode != VECTOR_MODE_PACKET &&
2812 appl_args->vector_mode != VECTOR_MODE_EVENT) {
2813 ODPH_ERR("Invalid vector mode: %d\n", appl_args->vector_mode);
2814 exit(EXIT_FAILURE);
2815 }
2816
2817 if (appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED &&
2818 appl_args->tx_compl.nth == 0) {
2819 ODPH_ERR("Invalid packet interval for transmit completion: %u\n",
2820 appl_args->tx_compl.nth);
2821 exit(EXIT_FAILURE);
2822 }
2823
2824 if (appl_args->tx_compl.mode == ODP_PACKET_TX_COMPL_EVENT &&
2825 (appl_args->in_mode == PLAIN_QUEUE || appl_args->in_mode == DIRECT_RECV)) {
2826 ODPH_ERR("Transmit event completion mode not supported with plain queue or direct "
2827 "input modes\n");
2828 exit(EXIT_FAILURE);
2829 }
2830
2831 appl_args->tx_compl.tot_compl_id = (appl_args->tx_compl.thr_compl_id + 1) *
2832 appl_args->cpu_count - 1;
2833
2834 if (appl_args->burst_rx == 0)
2835 appl_args->burst_rx = MAX_PKT_BURST;
2836
2837 appl_args->extra_feat = 0;
2838 if (appl_args->error_check || appl_args->chksum || appl_args->packet_copy ||
2839 appl_args->data_rd || appl_args->verbose_pkt || appl_args->wait_ns ||
2840 appl_args->memcpy_bytes)
2841 appl_args->extra_feat = 1;
2842
2843 appl_args->has_state = 0;
2844 if (appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED)
2845 appl_args->has_state = 1;
2846
2847 optind = 1; /* reset 'extern optind' from the getopt lib */
2848}
2849
2850static void print_options(void)
2851{
2852 int i;
2853 appl_args_t *appl_args = &gbl_args->appl;
2854 odp_cache_stash_config_t *cs_conf = &appl_args->cache_stash_config;
2855
2856 printf("\n"
2857 "odp_l2fwd options\n"
2858 "-----------------\n"
2859 "IF-count: %i\n"
2860 "Using IFs: ", appl_args->if_count);
2861
2862 for (i = 0; i < appl_args->if_count; ++i)
2863 printf(" %s", appl_args->if_names[i]);
2864 printf("\n"
2865 "Mode: ");
2866 if (appl_args->in_mode == DIRECT_RECV)
2867 printf("PKTIN_DIRECT, ");
2868 else if (appl_args->in_mode == PLAIN_QUEUE)
2869 printf("PKTIN_QUEUE, ");
2870 else if (appl_args->in_mode == SCHED_PARALLEL)
2871 printf("PKTIN_SCHED_PARALLEL, ");
2872 else if (appl_args->in_mode == SCHED_ATOMIC)
2873 printf("PKTIN_SCHED_ATOMIC, ");
2874 else if (appl_args->in_mode == SCHED_ORDERED)
2875 printf("PKTIN_SCHED_ORDERED, ");
2876
2877 if (appl_args->out_mode)
2878 printf("PKTOUT_QUEUE\n");
2879 else
2880 printf("PKTOUT_DIRECT\n");
2881
2882 if (appl_args->num_om > 0) {
2883 printf("Output mappings: ");
2884
2885 for (i = 0; i < appl_args->num_om; ++i)
2886 printf(" %s", appl_args->output_map[i]);
2887
2888 printf("\n");
2889 }
2890
2891 printf("MTU: ");
2892 if (appl_args->mtu)
2893 printf("%i bytes\n", appl_args->mtu);
2894 else
2895 printf("interface default\n");
2896 printf("Promisc mode: %s\n", appl_args->promisc_mode ?
2897 "enabled" : "disabled");
2898 if (appl_args->flow_control)
2899 printf("Flow control: %s%s\n",
2900 appl_args->pause_rx ? "rx " : "",
2901 appl_args->pause_tx ? "tx" : "");
2902 printf("Flow aware: %s\n", appl_args->flow_aware ?
2903 "yes" : "no");
2904 printf("Input TS: %s\n", appl_args->input_ts ? "yes" : "no");
2905 printf("Burst size: %i\n", appl_args->burst_rx);
2906 printf("RX queues per IF: %i\n", appl_args->rx_queues);
2907 printf("Number of pools: %i\n", appl_args->pool_per_if ?
2908 appl_args->if_count : 1);
2909
2910 if (appl_args->extra_feat || appl_args->has_state) {
2911 printf("Extra features: %s%s%s%s%s%s%s%s\n",
2912 appl_args->error_check ? "error_check " : "",
2913 appl_args->chksum ? "chksum " : "",
2914 appl_args->packet_copy ? "packet_copy " : "",
2915 appl_args->data_rd ? "data_rd" : "",
2916 appl_args->tx_compl.mode != ODP_PACKET_TX_COMPL_DISABLED ? "tx_compl" : "",
2917 appl_args->verbose_pkt ? "verbose_pkt" : "",
2918 appl_args->wait_ns ? "wait_ns" : "",
2919 appl_args->memcpy_bytes ? "memcpy" : "");
2920
2921 if (appl_args->memcpy_bytes)
2922 printf(" Memcpy: %" PRIu64 " bytes\n", appl_args->memcpy_bytes);
2923 if (appl_args->wait_ns)
2924 printf(" Wait: %" PRIu64 " ns\n", appl_args->wait_ns);
2925 }
2926
2927 printf("Num worker threads: %i\n", appl_args->num_workers);
2928 printf("CPU mask: %s\n", gbl_args->cpumaskstr);
2929
2930 if (appl_args->num_groups > 0)
2931 printf("num groups: %i\n", appl_args->num_groups);
2932 else if (appl_args->num_groups == 0)
2933 printf("group: ODP_SCHED_GROUP_ALL\n");
2934 else
2935 printf("group: ODP_SCHED_GROUP_WORKER\n");
2936
2937 printf("Packets per pool: %u\n", appl_args->num_pkt);
2938 printf("Packet length: %u\n", appl_args->packet_len);
2939 printf("Segment length: %u\n", appl_args->seg_len == UINT32_MAX ? 0 :
2940 appl_args->seg_len);
2941 printf("Read data: %u bytes\n", appl_args->data_rd * 8);
2942 printf("Prefetch data: %u bytes\n", appl_args->prefetch * 64);
2943 printf("Vector mode: %s\n", appl_args->vector_mode == VECTOR_MODE_PACKET ?
2944 "packet" : appl_args->vector_mode == VECTOR_MODE_EVENT ? "event" : "disabled");
2945 printf("Vectors per pool: %u\n", appl_args->num_vec);
2946 printf("Vector size: %u\n", appl_args->vec_size);
2947 printf("Priority per IF: ");
2948
2949 for (i = 0; i < appl_args->if_count; i++)
2950 printf(" %i", appl_args->prio[i]);
2951 printf("\n");
2952
2953 printf("Schedule prefetch: %u events\n", appl_args->sched_prefetch);
2954
2955 printf("Cache stash: ");
2956 if (cs_conf->regions.all) {
2957 printf("\n%-18s %10s %8s %10s %8s\n",
2958 "Region", "L2 offset", "L2 len", "L3 offset", "L3 len");
2959 printf("----------------------------------------------------------\n");
2960
2961 printf("%-18s %10d %8d %10d %8d\n",
2962 "Event metadata",
2963 cs_conf->event_metadata.l2.offset,
2964 cs_conf->event_metadata.l2.len,
2965 cs_conf->event_metadata.l3.offset,
2966 cs_conf->event_metadata.l3.len);
2967
2968 printf("%-18s %10d %8d %10d %8d\n",
2969 "Event data",
2970 cs_conf->event_data.l2.offset,
2971 cs_conf->event_data.l2.len,
2972 cs_conf->event_data.l3.offset,
2973 cs_conf->event_data.l3.len);
2974
2975 printf("%-18s %10d %8d %10d %8d\n",
2976 "Event user area",
2977 cs_conf->event_user_area.l2.offset,
2978 cs_conf->event_user_area.l2.len,
2979 cs_conf->event_user_area.l3.offset,
2980 cs_conf->event_user_area.l3.len);
2981
2982 printf("%-18s %10d %8d %10d %8d\n",
2983 "Queue context",
2984 cs_conf->queue_context.l2.offset,
2985 cs_conf->queue_context.l2.len,
2986 cs_conf->queue_context.l3.offset,
2987 cs_conf->queue_context.l3.len);
2988 } else {
2989 printf("disabled\n");
2990 }
2991
2992 printf("\n");
2993}
2994
2995static void gbl_args_init(args_t *args)
2996{
2997 int pktio, queue;
2998
2999 memset(args, 0, sizeof(args_t));
3000 odp_atomic_init_u32(&args->exit_threads, 0);
3001 args->memcpy_shm = ODP_SHM_INVALID;
3002
3003 for (pktio = 0; pktio < MAX_PKTIOS; pktio++) {
3004 args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
3005
3006 for (queue = 0; queue < MAX_QUEUES; queue++)
3007 args->pktios[pktio].rx_q[queue] = ODP_QUEUE_INVALID;
3008
3009 args->pktios[pktio].compl_q = ODP_QUEUE_INVALID;
3010 }
3011
3012 args->appl.tx_compl.mode = ODP_PACKET_TX_COMPL_DISABLED;
3013}
3014
3015static void create_groups(int num, odp_schedule_group_t *group,
3016 odp_cache_stash_config_t *cache_stash_config)
3017{
3018 int i;
3019 odp_thrmask_t zero;
3020 odp_schedule_group_param_t group_param;
3021
3022 odp_thrmask_zero(&zero);
3023 odp_schedule_group_param_init(&group_param);
3024 group_param.cache_stash_hints.common = *cache_stash_config;
3025
3026 /* Create groups */
3027 for (i = 0; i < num; i++) {
3028 group[i] = odp_schedule_group_create_2(NULL, &zero, &group_param);
3029
3030 if (group[i] == ODP_SCHED_GROUP_INVALID) {
3031 ODPH_ERR("Group create failed\n");
3032 exit(EXIT_FAILURE);
3033 }
3034 }
3035}
3036
3037static int set_vector_pool_params(odp_pool_param_t *params, const odp_pool_capability_t *pool_capa)
3038{
3039 uint32_t num_vec, vec_size;
3040
3041 vec_size = gbl_args->appl.vec_size == 0 ? DEFAULT_VEC_SIZE : gbl_args->appl.vec_size;
3042
3043 ODPH_ASSERT(pool_capa->vector.max_size > 0);
3044 if (vec_size > pool_capa->vector.max_size) {
3045 if (gbl_args->appl.vec_size == 0) {
3046 vec_size = pool_capa->vector.max_size;
3047 printf("\nWarning: Vector size reduced to %u\n\n", vec_size);
3048 } else {
3049 ODPH_ERR("Vector size too big %u. Maximum is %u.\n",
3050 vec_size, pool_capa->vector.max_size);
3051 return -1;
3052 }
3053 }
3054
3055 if (gbl_args->appl.num_vec == 0) {
3056 uint32_t num_pkt = gbl_args->appl.num_pkt ?
3057 gbl_args->appl.num_pkt : DEFAULT_NUM_PKT;
3058
3059 num_vec = (num_pkt + vec_size - 1) / vec_size;
3060 } else {
3061 num_vec = gbl_args->appl.num_vec;
3062 }
3063
3064 if (pool_capa->vector.max_num && num_vec > pool_capa->vector.max_num) {
3065 if (gbl_args->appl.num_vec == 0) {
3066 num_vec = pool_capa->vector.max_num;
3067 printf("\nWarning: number of vectors reduced to %u\n\n", num_vec);
3068 } else {
3069 ODPH_ERR("Too many vectors (%u) per pool. Maximum is %u.\n",
3070 num_vec, pool_capa->vector.max_num);
3071 return -1;
3072 }
3073 }
3074
3075 params->vector.num = num_vec;
3076 params->vector.max_size = vec_size;
3077 params->type = ODP_POOL_VECTOR;
3078
3079 return 0;
3080}
3081
3082static int set_event_vector_pool_params(odp_pool_param_t *params,
3083 const odp_pool_capability_t *pool_capa)
3084{
3085 uint32_t num_vec, vec_size, max_size;
3086
3087 vec_size = gbl_args->appl.vec_size == 0 ? DEFAULT_VEC_SIZE : gbl_args->appl.vec_size;
3088
3089 max_size = ODPH_MIN(pool_capa->event_vector.max_size, MAX_EVENT_VEC_SIZE);
3090 if (vec_size > max_size) {
3091 if (gbl_args->appl.vec_size == 0) {
3092 vec_size = max_size;
3093 printf("\nWarning: Vector size reduced to %u\n\n", vec_size);
3094 } else {
3095 ODPH_ERR("Vector size too big %u. Maximum is %u.\n", vec_size, max_size);
3096 return -1;
3097 }
3098 }
3099
3100 if (gbl_args->appl.num_vec == 0) {
3101 uint32_t num_pkt = gbl_args->appl.num_pkt ?
3102 gbl_args->appl.num_pkt : DEFAULT_NUM_PKT;
3103
3104 num_vec = (num_pkt + vec_size - 1) / vec_size;
3105 } else {
3106 num_vec = gbl_args->appl.num_vec;
3107 }
3108
3109 if (pool_capa->event_vector.max_num && num_vec > pool_capa->event_vector.max_num) {
3110 if (gbl_args->appl.num_vec == 0) {
3111 num_vec = pool_capa->event_vector.max_num;
3112 printf("\nWarning: number of vectors reduced to %u\n\n", num_vec);
3113 } else {
3114 ODPH_ERR("Too many vectors (%u) per pool. Maximum is %u.\n",
3115 num_vec, pool_capa->event_vector.max_num);
3116 return -1;
3117 }
3118 }
3119
3120 params->event_vector.num = num_vec;
3121 params->event_vector.max_size = vec_size;
3122 params->type = ODP_POOL_EVENT_VECTOR;
3123
3124 return 0;
3125}
3126
3127static int reserve_memcpy_memory(args_t *args)
3128{
3129 uint64_t total_bytes;
3130
3131 if (args->appl.memcpy_bytes == 0)
3132 return 0;
3133
3134 /* Private memory area (read + write) for each worker */
3135 total_bytes = 2 * args->appl.memcpy_bytes * args->appl.num_workers;
3136
3137 args->memcpy_shm = odp_shm_reserve("memcpy_shm", total_bytes, ODP_CACHE_LINE_SIZE, 0);
3138 if (args->memcpy_shm == ODP_SHM_INVALID) {
3139 ODPH_ERR("Reserving %" PRIu64 " bytes for memcpy failed.\n", total_bytes);
3140 return -1;
3141 }
3142 args->memcpy_data = odp_shm_addr(args->memcpy_shm);
3143 if (args->memcpy_data == NULL) {
3144 ODPH_ERR("Shared mem addr for memcpy failed.\n");
3145 return -1;
3146 }
3147
3148 return 0;
3149}
3150
3151static int create_vector_pools(args_t *args, odp_pool_capability_t *pool_capa,
3152 odp_pool_t vec_pool_tbl[])
3153{
3154 odp_pool_param_t params;
3155 int num_vec_pools;
3156 vector_mode_t mode = args->appl.vector_mode;
3157
3158 if (mode == VECTOR_MODE_DISABLED)
3159 return 0;
3160
3161 if (!sched_mode(args->appl.in_mode)) {
3162 ODPH_ERR("Vector mode only supports scheduler pktin modes (1-3)\n");
3163 return -1;
3164 }
3165
3166 num_vec_pools = args->appl.pool_per_if ? gbl_args->appl.if_count : 1;
3167
3168 if ((mode == VECTOR_MODE_PACKET && num_vec_pools > (int)pool_capa->vector.max_pools) ||
3169 (mode == VECTOR_MODE_EVENT && num_vec_pools > (int)pool_capa->event_vector.max_pools)) {
3170 ODPH_ERR("Too many vector pools %i\n", num_vec_pools);
3171 return -1;
3172 }
3173
3174 odp_pool_param_init(&params);
3175
3176 if (mode == VECTOR_MODE_PACKET) {
3177 if (set_vector_pool_params(&params, pool_capa))
3178 return -1;
3179 } else {
3180 if (set_event_vector_pool_params(&params, pool_capa))
3181 return -1;
3182 }
3183
3184 args->vector_num = mode == VECTOR_MODE_PACKET ? params.vector.num : params.event_vector.num;
3185 args->vector_max_size = mode == VECTOR_MODE_PACKET ? params.vector.max_size :
3186 params.event_vector.max_size;
3187
3188 /* Print resulting values */
3189 printf("Vectors per pool: %u\n", args->vector_num);
3190 printf("Vector size: %u\n", args->vector_max_size);
3191
3192 for (int i = 0; i < num_vec_pools; i++) {
3193 vec_pool_tbl[i] = odp_pool_create("vector pool", &params);
3194
3195 if (vec_pool_tbl[i] == ODP_POOL_INVALID) {
3196 ODPH_ERR("Vector pool create failed %i\n", i);
3197 exit(EXIT_FAILURE);
3198 }
3199
3200 if (args->appl.verbose)
3201 odp_pool_print(vec_pool_tbl[i]);
3202 }
3203
3204 return num_vec_pools;
3205}
3206
3207/*
3208 * L2 forwarding main function
3209 */
3210int main(int argc, char *argv[])
3211{
3212 odph_helper_options_t helper_options;
3213 odph_thread_param_t thr_param[MAX_WORKERS];
3214 odph_thread_common_param_t thr_common;
3215 int i;
3216 int num_workers, num_thr;
3217 odp_shm_t shm;
3218 odp_cpumask_t cpumask;
3219 odph_ethaddr_t new_addr;
3220 odp_pool_param_t params;
3221 int ret;
3222 stats_t *stats[MAX_WORKERS];
3223 int if_count, num_pools, num_vec_pools;
3224 int (*thr_run_func)(void *);
3225 odp_instance_t instance;
3226 int num_groups, max_groups;
3227 odp_schedule_group_t group[MAX_GROUPS];
3228 odp_pool_t pool_tbl[MAX_PKTIOS], vec_pool_tbl[MAX_PKTIOS];
3229 odp_pool_t pool, vec_pool;
3230 odp_init_t init;
3231 odp_pool_capability_t pool_capa;
3232 odp_schedule_config_t sched_config;
3233 odp_schedule_capability_t sched_capa;
3234 uint32_t pkt_len, num_pkt, seg_len;
3235 odp_schedule_group_param_t group_param;
3236
3237 /* Let helper collect its own arguments (e.g. --odph_proc) */
3238 argc = odph_parse_options(argc, argv);
3239 if (odph_options(&helper_options)) {
3240 ODPH_ERR("Reading ODP helper options failed.\n");
3241 exit(EXIT_FAILURE);
3242 }
3243
3244 odp_init_param_init(&init);
3245
3246 /* List features not to be used (may optimize performance) */
3247 init.not_used.feat.cls = 1;
3248 init.not_used.feat.compress = 1;
3249 init.not_used.feat.crypto = 1;
3250 init.not_used.feat.ipsec = 1;
3251 init.not_used.feat.timer = 1;
3252 init.not_used.feat.tm = 1;
3253
3254 init.mem_model = helper_options.mem_model;
3255
3256 if (setup_sig_handler()) {
3257 ODPH_ERR("Signal handler setup failed\n");
3258 exit(EXIT_FAILURE);
3259 }
3260
3261 /* Init ODP before calling anything else */
3262 if (odp_init_global(&instance, &init, NULL)) {
3263 ODPH_ERR("ODP global init failed.\n");
3264 exit(EXIT_FAILURE);
3265 }
3266
3267 /* Init this thread */
3268 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
3269 ODPH_ERR("ODP local init failed.\n");
3270 exit(EXIT_FAILURE);
3271 }
3272
3273 /* Reserve memory for args from shared mem */
3274 shm = odp_shm_reserve("shm_args", sizeof(args_t),
3275 ODP_CACHE_LINE_SIZE, 0);
3276
3277 if (shm == ODP_SHM_INVALID) {
3278 ODPH_ERR("Shared mem reserve failed.\n");
3279 exit(EXIT_FAILURE);
3280 }
3281
3282 gbl_args = odp_shm_addr(shm);
3283
3284 if (gbl_args == NULL) {
3285 ODPH_ERR("Shared mem addr failed.\n");
3286 exit(EXIT_FAILURE);
3287 }
3288 gbl_args_init(gbl_args);
3289
3290 /* Parse and store the application arguments */
3291 parse_args(argc, argv, &gbl_args->appl);
3292
3294
3295 if (sched_mode(gbl_args->appl.in_mode))
3296 gbl_args->appl.sched_mode = 1;
3297
3298 num_workers = MAX_WORKERS;
3299 if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
3300 num_workers = gbl_args->appl.cpu_count;
3301
3302 /* Get default worker cpumask */
3303 num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
3304 (void)odp_cpumask_to_str(&cpumask, gbl_args->cpumaskstr, sizeof(gbl_args->cpumaskstr));
3305
3306 gbl_args->appl.num_workers = num_workers;
3307
3308 print_options();
3309
3310 if (reserve_memcpy_memory(gbl_args))
3311 exit(EXIT_FAILURE);
3312
3313 if_count = gbl_args->appl.if_count;
3314
3315 num_pools = 1;
3316 if (gbl_args->appl.pool_per_if)
3317 num_pools = if_count;
3318
3319 if (odp_pool_capability(&pool_capa)) {
3320 ODPH_ERR("Pool capability failed\n");
3321 return -1;
3322 }
3323
3324 if (num_pools > (int)pool_capa.pkt.max_pools) {
3325 ODPH_ERR("Too many pools %i\n", num_pools);
3326 return -1;
3327 }
3328
3329 pkt_len = gbl_args->appl.packet_len;
3330
3331 if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len) {
3332 pkt_len = pool_capa.pkt.max_len;
3333 printf("\nWarning: packet length reduced to %u\n\n", pkt_len);
3334 }
3335
3336 if (gbl_args->appl.seg_len == UINT32_MAX)
3337 seg_len = gbl_args->appl.packet_len;
3338 else
3339 seg_len = gbl_args->appl.seg_len;
3340
3341 /* Check whether we have sufficient segments to support requested packet
3342 * length, if not adjust to bigger segment size */
3343 if (seg_len < (pkt_len / pool_capa.pkt.max_segs_per_pkt))
3344 seg_len = pkt_len / pool_capa.pkt.max_segs_per_pkt;
3345
3346 if (pool_capa.pkt.min_seg_len && seg_len < pool_capa.pkt.min_seg_len)
3347 seg_len = pool_capa.pkt.min_seg_len;
3348
3349 if (pool_capa.pkt.max_seg_len && seg_len > pool_capa.pkt.max_seg_len)
3350 seg_len = pool_capa.pkt.max_seg_len;
3351
3352 if ((gbl_args->appl.seg_len != UINT32_MAX) && (seg_len != gbl_args->appl.seg_len))
3353 printf("\nWarning: Segment length requested %d configured %d\n",
3354 gbl_args->appl.seg_len, seg_len);
3355
3356 if (seg_len < gbl_args->appl.data_rd * 8) {
3357 ODPH_ERR("Requested data read length %u exceeds maximum segment length %u\n",
3358 gbl_args->appl.data_rd * 8, seg_len);
3359 return -1;
3360 }
3361
3362 /* zero means default number of packets */
3363 if (gbl_args->appl.num_pkt == 0)
3364 num_pkt = DEFAULT_NUM_PKT;
3365 else
3366 num_pkt = gbl_args->appl.num_pkt;
3367
3368 if (pool_capa.pkt.max_num && num_pkt > pool_capa.pkt.max_num) {
3369 if (gbl_args->appl.num_pkt == 0) {
3370 num_pkt = pool_capa.pkt.max_num;
3371 printf("\nWarning: number of packets reduced to %u\n\n",
3372 num_pkt);
3373 } else {
3374 ODPH_ERR("Too many packets %u. Maximum is %u.\n",
3375 num_pkt, pool_capa.pkt.max_num);
3376 return -1;
3377 }
3378 }
3379
3380 gbl_args->num_pkt = num_pkt;
3381 gbl_args->pkt_len = pkt_len;
3382 gbl_args->seg_len = seg_len;
3383
3384 printf("Resulting pool parameter values:\n");
3385 printf("Packets per pool: %u\n", num_pkt);
3386 printf("Packet length: %u\n", pkt_len);
3387 printf("Segment length: %u\n", seg_len);
3388
3389 /* Create packet pool */
3390 odp_pool_param_init(&params);
3391 params.pkt.seg_len = seg_len;
3392 params.pkt.len = pkt_len;
3393 params.pkt.num = num_pkt;
3394 params.type = ODP_POOL_PACKET;
3395
3396 for (i = 0; i < num_pools; i++) {
3397 pool_tbl[i] = odp_pool_create("packet pool", &params);
3398
3399 if (pool_tbl[i] == ODP_POOL_INVALID) {
3400 ODPH_ERR("Pool create failed %i\n", i);
3401 exit(EXIT_FAILURE);
3402 }
3403
3404 if (gbl_args->appl.verbose)
3405 odp_pool_print(pool_tbl[i]);
3406 }
3407
3408 num_vec_pools = create_vector_pools(gbl_args, &pool_capa, vec_pool_tbl);
3409 if (num_vec_pools < 0)
3410 exit(EXIT_FAILURE);
3411
3412 printf("\n");
3413
3414 bind_workers();
3415
3416 odp_schedule_config_init(&sched_config);
3417 odp_schedule_group_param_init(&group_param);
3418 group_param.cache_stash_hints.common = gbl_args->appl.cache_stash_config;
3419
3420 if (odp_schedule_capability(&sched_capa)) {
3421 ODPH_ERR("Schedule capability failed\n");
3422 exit(EXIT_FAILURE);
3423 }
3424
3425 if (gbl_args->appl.flow_aware) {
3426 if (sched_capa.max_flow_id) {
3427 sched_config.max_flow_id = sched_capa.max_flow_id;
3428 } else {
3429 ODPH_ERR("Flow aware mode not supported\n");
3430 exit(EXIT_FAILURE);
3431 }
3432 }
3433
3434 num_groups = gbl_args->appl.num_groups;
3435 /* Predefined groups are enabled by default */
3436 max_groups = sched_capa.max_groups - 3;
3437 if (max_groups > MAX_GROUPS)
3438 max_groups = MAX_GROUPS;
3439
3440 if (num_groups > max_groups) {
3441 ODPH_ERR("Too many groups. Maximum is %i.\n", max_groups);
3442 exit(EXIT_FAILURE);
3443 }
3444
3445 if (num_groups == -1)
3446 sched_config.sched_group.worker_param = group_param;
3447 else if (num_groups == 0)
3448 sched_config.sched_group.all_param = group_param;
3449
3450 if (odp_schedule_config(&sched_config)) {
3451 ODPH_ERR("Schedule configuration failed\n");
3452 exit(EXIT_FAILURE);
3453 }
3454
3455 /* Default */
3456 if (num_groups == 0) {
3457 group[0] = ODP_SCHED_GROUP_ALL;
3458 num_groups = 1;
3459 } else if (num_groups == -1) {
3460 group[0] = ODP_SCHED_GROUP_WORKER;
3461 num_groups = 1;
3462 } else {
3463 create_groups(num_groups, group, &gbl_args->appl.cache_stash_config);
3464 }
3465
3466 pool = pool_tbl[0];
3467 vec_pool = vec_pool_tbl[0];
3468
3469 printf("\nInterfaces\n----------\n");
3470
3471 for (i = 0; i < if_count; ++i) {
3472 const char *dev = gbl_args->appl.if_names[i];
3473 int num_rx, num_tx;
3475
3476 /* A queue per worker in scheduled mode */
3477 num_rx = gbl_args->appl.rx_queues > 0 ? gbl_args->appl.rx_queues : num_workers;
3478 num_tx = num_workers;
3479
3480 if (!gbl_args->appl.sched_mode) {
3481 /* A queue per assigned worker */
3482 num_rx = gbl_args->pktios[i].num_rx_thr;
3483 num_tx = gbl_args->pktios[i].num_tx_thr;
3484 }
3485
3486 /* Round robin pktios to groups */
3487 grp = group[i % num_groups];
3488
3489 if (gbl_args->appl.pool_per_if) {
3490 pool = pool_tbl[i];
3491 vec_pool = vec_pool_tbl[i];
3492 }
3493
3494 if (create_pktio(dev, i, num_rx, num_tx, pool, vec_pool, grp))
3495 exit(EXIT_FAILURE);
3496
3497 /* Save destination eth address */
3498 if (gbl_args->appl.dst_change) {
3499 /* 02:00:00:00:00:XX */
3500 memset(&new_addr, 0, sizeof(odph_ethaddr_t));
3501 if (gbl_args->appl.addr_count) {
3502 memcpy(&new_addr, &gbl_args->appl.addrs[i],
3503 sizeof(odph_ethaddr_t));
3504 } else {
3505 new_addr.addr[0] = 0x02;
3506 new_addr.addr[5] = i;
3507 }
3508 gbl_args->dst_eth_addr[i] = new_addr;
3509 }
3510 }
3511
3512 gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
3513
3514 bind_queues();
3515
3516 init_port_lookup_tbl();
3517
3518 if (!gbl_args->appl.sched_mode)
3519 print_port_mapping();
3520
3521 odp_barrier_init(&gbl_args->init_barrier, num_workers + 1);
3522 odp_barrier_init(&gbl_args->term_barrier, num_workers + 1);
3523
3524 if (gbl_args->appl.in_mode == DIRECT_RECV) {
3525 thr_run_func = run_worker_direct_mode;
3526 } else if (gbl_args->appl.in_mode == PLAIN_QUEUE) {
3527 thr_run_func = run_worker_plain_queue_mode;
3528 } else { /* SCHED_PARALLEL / SCHED_ATOMIC / SCHED_ORDERED */
3529 if (gbl_args->appl.vector_mode == VECTOR_MODE_PACKET)
3530 thr_run_func = run_worker_sched_mode_vector;
3531 else if (gbl_args->appl.vector_mode == VECTOR_MODE_EVENT)
3532 thr_run_func = run_worker_sched_mode_event_vector;
3533 else
3534 thr_run_func = run_worker_sched_mode;
3535 }
3536
3537 /* Create worker threads */
3538 odph_thread_common_param_init(&thr_common);
3539
3540 thr_common.instance = instance;
3541 thr_common.cpumask = &cpumask;
3542 /* Synchronize thread start up. Test runs are more repeatable when
3543 * thread / thread ID / CPU ID mapping stays constant. */
3544 thr_common.sync = 1;
3545
3546 for (i = 0; i < num_workers; ++i) {
3547 int j;
3548 int num_join;
3549 int mode = gbl_args->appl.group_mode;
3550
3551 init_state(&gbl_args->appl, &gbl_args->thread_args[i].state, i);
3552 odph_thread_param_init(&thr_param[i]);
3553 thr_param[i].start = thr_run_func;
3554 thr_param[i].arg = &gbl_args->thread_args[i];
3555 thr_param[i].thr_type = ODP_THREAD_WORKER;
3556
3557 gbl_args->thread_args[i].num_grp_join = 0;
3558 gbl_args->thread_args[i].thr_idx = i;
3559
3560 /* Fill in list of groups to join */
3561 if (gbl_args->appl.num_groups > 0) {
3562 num_join = if_count < num_groups ? if_count : num_groups;
3563
3564 if (mode == 0 || mode == 1) {
3565 /* All threads join all groups */
3566 if (mode == 0)
3567 num_join = num_groups;
3568
3569 gbl_args->thread_args[i].num_grp_join = num_join;
3570
3571 for (j = 0; j < num_join; j++)
3572 gbl_args->thread_args[i].group[j] = group[j];
3573 } else {
3574 /* Thread joins first groups in round robin */
3575 if (num_workers >= num_join) {
3576 gbl_args->thread_args[i].num_grp_join = 1;
3577 gbl_args->thread_args[i].group[0] = group[i % num_join];
3578 } else {
3579 int cnt = 0;
3580
3581 for (j = 0; i + j < num_join; j += num_workers) {
3582 gbl_args->thread_args[i].group[cnt] = group[i + j];
3583 cnt++;
3584 }
3585
3586 gbl_args->thread_args[i].num_grp_join = cnt;
3587 }
3588 }
3589 }
3590
3591 stats[i] = &gbl_args->thread_args[i].stats;
3592 }
3593
3594 num_thr = odph_thread_create(gbl_args->thread_tbl, &thr_common,
3595 thr_param, num_workers);
3596
3597 if (num_thr != num_workers) {
3598 ODPH_ERR("Worker create failed: %i\n", num_thr);
3599 exit(EXIT_FAILURE);
3600 }
3601
3602 if (gbl_args->appl.verbose)
3604
3605 if (start_pktios(gbl_args))
3606 exit(EXIT_FAILURE);
3607
3608 ret = print_speed_stats(num_workers, stats, gbl_args->appl.time,
3609 gbl_args->appl.accuracy);
3610
3611 if (stop_pktios(gbl_args))
3612 exit(EXIT_FAILURE);
3613
3614 odp_atomic_store_u32(&gbl_args->exit_threads, 1);
3615 if (gbl_args->appl.in_mode != DIRECT_RECV)
3616 odp_barrier_wait(&gbl_args->term_barrier);
3617
3618 odph_thread_join_result_t res[num_workers];
3619
3620 /* Master thread waits for other threads to exit */
3621 if (odph_thread_join_result(gbl_args->thread_tbl, res, num_workers) != num_workers) {
3622 ODPH_ERR("Worker join failed\n");
3623 exit(EXIT_FAILURE);
3624 }
3625
3626 for (i = 0; i < num_workers; i++) {
3627 if (res[i].is_sig || res[i].ret != 0) {
3628 ODPH_ERR("Worker thread failure%s: %d\n", res[i].is_sig ?
3629 " (signaled)" : "", res[i].ret);
3630 exit(EXIT_FAILURE);
3631 }
3632 }
3633
3634 for (i = 0; i < if_count; ++i) {
3635 odp_pktio_t pktio = gbl_args->pktios[i].pktio;
3636
3637 if (gbl_args->appl.verbose && odp_pktio_extra_stat_info(pktio, NULL, 0) > 0) {
3638 printf("Pktio %s extra statistics:\n", gbl_args->appl.if_names[i]);
3640 }
3641
3642 if (gbl_args->pktios[i].compl_q != ODP_QUEUE_INVALID)
3643 (void)odp_queue_destroy(gbl_args->pktios[i].compl_q);
3644
3645 if (odp_pktio_close(pktio)) {
3646 ODPH_ERR("Pktio close failed: %s\n", gbl_args->appl.if_names[i]);
3647 exit(EXIT_FAILURE);
3648 }
3649 }
3650
3651 free(gbl_args->appl.if_names);
3652 free(gbl_args->appl.if_str);
3653
3654 if (gbl_args->memcpy_shm != ODP_SHM_INVALID && odp_shm_free(gbl_args->memcpy_shm)) {
3655 ODPH_ERR("Shared mem free failed\n");
3656 exit(EXIT_FAILURE);
3657 }
3658
3659 for (i = 0; i < gbl_args->appl.num_om; i++)
3660 free(gbl_args->appl.output_map[i]);
3661
3662 gbl_args = NULL;
3663 odp_mb_full();
3664
3665 for (i = 0; i < num_pools; i++) {
3666 if (odp_pool_destroy(pool_tbl[i])) {
3667 ODPH_ERR("Pool destroy failed: %i\n", i);
3668 exit(EXIT_FAILURE);
3669 }
3670 }
3671
3672 for (i = 0; i < num_vec_pools; i++) {
3673 if (odp_pool_destroy(vec_pool_tbl[i])) {
3674 ODPH_ERR("Vector pool destroy failed: %i\n", i);
3675 exit(EXIT_FAILURE);
3676 }
3677 }
3678
3679 if (odp_shm_free(shm)) {
3680 ODPH_ERR("Shm free failed\n");
3681 exit(EXIT_FAILURE);
3682 }
3683
3684 if (odp_term_local()) {
3685 ODPH_ERR("Term local failed\n");
3686 exit(EXIT_FAILURE);
3687 }
3688
3689 if (odp_term_global(instance)) {
3690 ODPH_ERR("Term global failed\n");
3691 exit(EXIT_FAILURE);
3692 }
3693
3694 return ret;
3695}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_mb_full(void)
Full memory barrier.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
#define ODP_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
#define odp_likely(x)
Branch likely taken.
Definition spec/hints.h:59
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
void odp_event_vector_free(odp_event_vector_t evv)
Free event vector.
odp_event_vector_t odp_event_vector_from_event(odp_event_t ev)
Get event vector handle from event.
uint32_t odp_event_vector_tbl(odp_event_vector_t evv, odp_event_t **event_tbl)
Get event vector table.
void odp_event_free_multi(const odp_event_t event[], int num)
Free multiple events.
void odp_event_free(odp_event_t event)
Free event.
odp_event_type_t odp_event_type(odp_event_t event)
Event type of an event.
#define ODP_EVENT_INVALID
Invalid event.
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.
int odp_pktio_info(odp_pktio_t pktio, odp_pktio_info_t *info)
Retrieve information about a pktio.
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_extra_stats_print(odp_pktio_t pktio)
Print extra statistics for a packet IO interface.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
int odp_pktin_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
Event queues for packet input.
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.
void odp_pktio_print(odp_pktio_t pktio)
Print pktio info to the console.
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_pktout_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
Event queues for packet output.
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.
odp_pktio_op_mode_t
Packet IO operation mode.
int odp_pktio_extra_stat_info(odp_pktio_t pktio, odp_pktio_extra_stat_info_t info[], int num)
Get extra statistics counter information for a packet IO interface.
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_QUEUE
Packet output through event queues.
@ ODP_PKTOUT_MODE_DISABLED
Application will never send to this interface.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIO_OP_MT
Multithread safe operation.
@ ODP_PKTIO_LINK_PAUSE_ON
Pause frame flow control enabled.
@ ODP_PKTIN_MODE_QUEUE
Packet input through plain event queues.
@ ODP_PKTIN_MODE_DISABLED
Application will never receive from this 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_tx_compl_request(odp_packet_t pkt, const odp_packet_tx_compl_opt_t *opt)
Request packet transmit completion.
int odp_packet_input_index(odp_packet_t pkt)
Packet input interface index.
int odp_packet_tx_compl_done(odp_pktio_t pktio, uint32_t compl_id)
Check packet transmit completion.
void odp_packet_to_event_multi(const odp_packet_t pkt[], odp_event_t ev[], int num)
Convert multiple packet handles to events.
uint32_t odp_packet_seg_len(odp_packet_t pkt)
Packet data length following the data pointer.
uint32_t odp_packet_headroom(odp_packet_t pkt)
Packet headroom length.
void odp_packet_prefetch(odp_packet_t pkt, uint32_t offset, uint32_t len)
Packet data prefetch.
odp_packet_vector_t odp_packet_vector_from_event(odp_event_t ev)
Get packet vector handle from event.
int odp_packet_num_segs(odp_packet_t pkt)
Number of segments.
odp_packet_t odp_packet_copy(odp_packet_t pkt, odp_pool_t pool)
Full copy of a packet.
uint32_t odp_packet_user_area_size(odp_packet_t pkt)
User area size.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
odp_packet_tx_compl_mode_t
Packet transmit completion mode.
uint32_t odp_packet_len(odp_packet_t pkt)
Packet data length.
int odp_packet_has_error(odp_packet_t pkt)
Check for all parse errors in packet.
int odp_packet_has_tx_compl_request(odp_packet_t pkt)
Check if packet transmit completion is requested.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
void odp_packet_free(odp_packet_t pkt)
Free packet.
void odp_packet_vector_free(odp_packet_vector_t pktv)
Free packet vector.
void odp_packet_l4_chksum_insert(odp_packet_t pkt, int insert)
Layer 4 checksum insertion override.
#define ODP_PACKET_INVALID
Invalid packet.
uint32_t odp_packet_vector_tbl(odp_packet_vector_t pktv, odp_packet_t **pkt_tbl)
Get packet vector table.
odp_pool_t odp_packet_pool(odp_packet_t pkt)
Packet pool.
#define ODP_PACKET_VECTOR_INVALID
Invalid packet vector.
void odp_packet_l3_chksum_insert(odp_packet_t pkt, int insert)
Layer 3 checksum insertion override.
@ ODP_PROTO_LAYER_ALL
All layers.
@ ODP_PROTO_LAYER_NONE
No layers.
@ ODP_PACKET_TX_COMPL_POLL
Enable packet transmit completion check through polling.
@ ODP_PACKET_TX_COMPL_DISABLED
Disable packet transmit completion.
@ ODP_PACKET_TX_COMPL_EVENT
Enable packet transmit completion event.
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()
void odp_pool_print(odp_pool_t pool)
Print pool info.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_VECTOR
Vector event pool.
@ ODP_POOL_PACKET
Packet pool.
@ ODP_POOL_EVENT_VECTOR
Event vector pool.
int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num)
Enqueue multiple events to a queue.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_event_t odp_queue_deq(odp_queue_t queue)
Dequeue an event from a queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
int odp_queue_deq_multi(odp_queue_t queue, odp_event_t events[], int num)
Dequeue multiple events from a queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
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.
int odp_schedule_prio_t
Scheduling priority level.
int odp_schedule_group_t
Scheduler thread group.
void odp_schedule_config_init(odp_schedule_config_t *config)
Initialize schedule configuration options.
int odp_schedule_group_join(odp_schedule_group_t group, const odp_thrmask_t *mask)
Join a schedule group.
void odp_schedule_group_param_init(odp_schedule_group_param_t *param)
Initialize schedule group parameters.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
#define ODP_SCHED_GROUP_WORKER
Group of all worker threads.
#define ODP_SCHED_GROUP_INVALID
Invalid scheduler group.
#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.
void odp_schedule_prefetch(int num)
Prefetch events for next schedule call.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
int odp_schedule_capability(odp_schedule_capability_t *capa)
Query scheduler capabilities.
odp_schedule_group_t odp_schedule_group_create_2(const char *name, const odp_thrmask_t *mask, const odp_schedule_group_param_t *param)
Schedule group create with parameters.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
void odp_schedule_resume(void)
Resume scheduling.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
void odp_shm_print_all(void)
Print all shared memory blocks.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
void odp_sys_info_print(void)
Print system info.
void odp_thrmask_set(odp_thrmask_t *mask, int thr)
Add thread to mask.
int odp_thread_id(void)
Get thread identifier.
void odp_thrmask_zero(odp_thrmask_t *mask)
Clear entire thread mask.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
uint64_t odp_time_to_ns(odp_time_t time)
Convert time to nanoseconds.
odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
Time difference.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_local(void)
Current local time.
The OpenDataPlane API.
Cache stashing configuration.
uint32_t event_data_l2
Enable/disable event_data L2 cache stashing.
uint32_t queue_context_l2
Enable/disable queue_context L2 cache stashing.
union odp_cache_stash_config_t::@161 regions
Region specific configuration toggle.
uint32_t event_metadata_l3
Enable/disable event_metadata L3 cache stashing.
uint32_t event_metadata_l2
Enable/disable event_metadata L2 cache stashing.
uint32_t queue_context_l3
Enable/disable queue_context L3 cache stashing.
odp_cache_stash_region_t event_user_area
Cache stashing for event user area.
uint32_t event_data_l3
Enable/disable event_data L3 cache stashing.
odp_cache_stash_region_t event_data
Cache stashing for event data.
uint32_t all
All bits of the bit field structure.
uint32_t event_user_area_l2
Enable/disable event_user_area L2 cache stashing.
odp_cache_stash_region_t event_metadata
Cache stashing for event metadata.
odp_cache_stash_region_t queue_context
Cache stashing for queue context region.
uint32_t event_user_area_l3
Enable/disable event_user_area L3 cache stashing.
Region specific cache stashing configuration.
uint32_t len
Length in bytes to cache.
struct odp_cache_stash_region_t::@160 l3
L3 cache stashing.
struct odp_cache_stash_region_t::@159 l2
L2 cache stashing.
uint32_t offset
Byte offset into a region to start caching from.
uint32_t max_size
Maximum number of events that can be aggregated into an event vector.
uint64_t min_tmo_ns
Minimum time in nanoseconds for an aggregator to form an event vector.
uint32_t min_size
Minimum number of events that can be aggregated into an event vector.
uint64_t max_tmo_ns
Maximum allowed value of odp_event_aggr_config_t::max_tmo_ns.
uint32_t max_num
Maximum number of event aggregators for this queue type.
Event vector configuration.
odp_event_type_t event_type
Event type.
uint64_t max_tmo_ns
Maximum time to wait for events.
uint32_t max_size
Maximum number of events in vector.
odp_pool_t pool
Event vector pool.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
Packet transmit completion request options.
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_pktin_vector_config_t vector
Packet input vector configuration.
uint64_t max_tmo_ns
Maximum timeout in nanoseconds for the producer to wait for the vector of packets.
uint64_t min_tmo_ns
Minimum value allowed to be configured to odp_pktin_vector_config_t::max_tmo_ns.
uint32_t min_size
Minimum value allowed to be configured to odp_pktin_vector_config_t::max_size.
uint32_t max_size
Maximum number of packets that can be accumulated into a packet vector by a producer.
odp_support_t supported
Packet input vector availability.
odp_bool_t enable
Enable packet input vector.
uint32_t max_size
Maximum number of packets in a vector.
uint64_t max_tmo_ns
Maximum time to wait for packets.
odp_bool_t queue_type_sched
Scheduled queue support.
uint32_t mode_poll
Packet transmit completion mode ODP_PACKET_TX_COMPL_POLL support.
odp_pktio_set_op_t set_op
Supported set operations.
uint32_t max_output
Maximum valid value for 'maxlen_output'.
odp_pktin_vector_capability_t vector
Packet input vector capability.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_input
Maximum valid value for 'maxlen_input'.
odp_pktio_config_t config
Supported pktio configuration options.
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 max_compl_id
Maximum supported completion ID value.
uint32_t min_input
Minimum valid value for 'maxlen_input'.
uint32_t mode_event
Packet transmit completion mode ODP_PACKET_TX_COMPL_EVENT support.
struct odp_pktio_capability_t::@116 tx_compl
Supported packet Tx completion options.
uint32_t pause_tx
Generation of traditional Ethernet pause frames.
uint32_t pause_rx
Reception of traditional Ethernet pause frames.
struct odp_pktio_capability_t::@118 flow_control
Supported flow control modes.
Packet IO configuration options.
uint32_t max_compl_id
Maximum completion index.
struct odp_pktio_config_t::@111 tx_compl
Packet transmit completion configuration.
uint32_t mode_event
Enable packet transmit completion events.
odp_pktio_link_pause_t pause_tx
Transmission of flow control frames.
struct odp_pktio_config_t::@110 flow_control
Link flow control configuration.
odp_pktout_config_opt_t pktout
Packet output configuration options bit field.
uint32_t mode_poll
Enable packet transmit completion check through polling.
odp_pktio_link_pause_t pause_rx
Reception of flow control frames.
odp_pktio_parser_config_t parser
Packet input parser configuration.
odp_pktin_config_opt_t pktin
Packet input configuration options bit field.
Packet IO information.
const char * drv_name
Packet IO driver name (implementation specific)
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.
uint32_t max_segs_per_pkt
Maximum number of segments per packet.
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
struct odp_pool_capability_t::@137 event_vector
Event vector pool capabilities.
uint32_t max_size
Maximum buffer data size in bytes.
struct odp_pool_capability_t::@136 vector
Vector pool capabilities.
uint32_t min_seg_len
Minimum packet segment data length in bytes.
uint32_t max_pools
Maximum number of pools of any type (odp_pool_type_t)
uint32_t max_seg_len
Maximum packet segment data length in bytes.
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.
struct odp_pool_param_t::@142 event_vector
Parameters for event vector pools.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t max_size
Maximum number of handles (such as odp_packet_t) in a vector.
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::@141 vector
Parameters for vector pools.
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
const odp_event_aggr_config_t * aggr
Event aggregator configuration parameters.
uint32_t num_aggr
Number of event aggregators.
uint32_t max_flow_id
Maximum flow ID per queue.
uint32_t max_groups
Maximum number of scheduling groups.
odp_event_aggr_capability_t aggr
Event aggregator capabilities for scheduled queues.
Schedule configuration.
struct odp_schedule_config_t::@169 sched_group
Enable/disable predefined scheduling groups.
odp_schedule_group_param_t all_param
Parameters for ODP_SCHED_GROUP_ALL schedule group.
uint32_t max_flow_id
Maximum flow ID per queue.
odp_schedule_group_param_t worker_param
Parameters for ODP_SCHED_GROUP_WORKER schedule group.
Schedule group parameters.
odp_cache_stash_config_t common
Common group specific cache stashing hints.
struct odp_schedule_group_param_t::@165 cache_stash_hints
Group specific cache stashing hints.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
struct odp_feature_t::@174 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()
uint64_t ts_all
Timestamp all packets on packet input.
struct odp_pktin_config_opt_t::@108 bit
Option flags.
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.
struct odp_pktout_config_opt_t::@109 bit
Option flags for packet output.
uint64_t no_packet_refs
Packet references not used on packet output.
uint64_t ipv4_chksum_ena
Enable IPv4 header checksum insertion.
uint64_t tcp_chksum_ena
Enable TCP checksum insertion.
uint64_t udp_chksum_ena
Enable UDP checksum insertion.