API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_switch.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2016-2018 Linaro Limited
3 * Copyright (c) 2020 Nokia
4 */
5
14#include <stdio.h>
15#include <getopt.h>
16#include <unistd.h>
17#include <stdlib.h>
18#include <inttypes.h>
19#include <signal.h>
20
21#include <odp_api.h>
22#include <odp/helper/odph_api.h>
23
25#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
26
28#define SHM_PKT_POOL_SIZE 8192
29
31#define SHM_PKT_POOL_BUF_SIZE 1856
32
34#define MAX_PKT_BURST 32
35
37#define MAX_QUEUES 32
38
40#define MAX_PKTIOS 8
41
43#define MAC_TBL_SIZE UINT16_MAX
44
46#define AGING_TIME 5
47
49#define NO_PATH(file_name) (strrchr((file_name), '/') ? \
50 strrchr((file_name), '/') + 1 : (file_name))
51
53typedef union {
54 struct {
55 odph_ethaddr_t mac;
56 uint8_t port;
57 uint8_t tick;
58 } s;
59
60 uint64_t u64;
61} mac_tbl_entry_t;
62
66typedef struct {
67 unsigned int cpu_count;
68 unsigned int if_count;
69 int num_workers;
70 char **if_names;
71 int time;
72 int accuracy;
73 char *if_str;
74} appl_args_t;
75
76typedef enum frame_type_t {
77 FRAME_UNICAST,
78 FRAME_BROADCAST,
79 FRAME_INVALID
80} frame_type_t;
81
85typedef union ODP_ALIGNED_CACHE {
86 struct {
88 uint64_t rx_packets;
90 uint64_t tx_packets;
92 uint64_t rx_drops;
94 uint64_t tx_drops;
95 } s;
96
97 uint8_t padding[ODP_CACHE_LINE_SIZE];
98} stats_t;
99
103typedef struct pkt_buf_t {
104 odp_packet_t pkt[MAX_PKT_BURST];
105 unsigned int len;
106} pkt_buf_t;
107
111typedef struct thread_args_t {
113 int num_rx_pktio;
114 struct {
115 odp_pktin_queue_t pktin;
116 uint8_t port_idx;
117 int queue_idx;
118 } rx_pktio[MAX_PKTIOS];
119 struct {
120 odp_pktout_queue_t pktout;
121 int queue_idx;
122 pkt_buf_t buf;
123 } tx_pktio[MAX_PKTIOS];
124
125 stats_t *stats[MAX_PKTIOS];
126} thread_args_t;
127
131typedef struct {
133 stats_t stats[MAX_WORKERS][MAX_PKTIOS];
134 appl_args_t appl;
135 thread_args_t thread[MAX_WORKERS];
136 odp_pool_t pool;
138 odp_barrier_t barrier;
140 odp_atomic_u32_t exit_threads;
142 struct {
143 odp_pktio_t pktio;
144 odp_pktin_queue_t pktin[MAX_QUEUES];
145 odp_pktout_queue_t pktout[MAX_QUEUES];
146 int num_rx_thr;
147 int num_rx_queue;
148 int num_tx_queue;
149 int next_rx_queue;
150 int next_tx_queue;
151 } pktios[MAX_PKTIOS];
152
153 odp_atomic_u64_t mac_tbl[MAC_TBL_SIZE];
154} args_t;
155
157static args_t *gbl_args;
158
159static void sig_handler(int signo ODP_UNUSED)
160{
161 if (gbl_args == NULL)
162 return;
163 odp_atomic_store_u32(&gbl_args->exit_threads, 1);
164}
165
173static inline uint16_t calc_mac_tbl_idx(odph_ethaddr_t *mac)
174{
175 uint32_t hash;
176
177 hash = odp_hash_crc32c(mac->addr, ODPH_ETHADDR_LEN, 0);
178
179 return (uint16_t)(hash & 0xFFFF);
180}
181
185static inline uint8_t diff_ticks(uint8_t t2, uint8_t t1)
186{
187 if (t1 < t2)
188 return t2 - t1;
189 else if (t1 > t2)
190 return UINT8_MAX + t2 - t1;
191 return 0;
192}
193
204static inline int mac_table_get(odph_ethaddr_t *mac, uint8_t *port,
205 uint8_t cur_tick)
206{
207 mac_tbl_entry_t entry;
208 uint16_t idx;
209
210 idx = calc_mac_tbl_idx(mac);
211
212 entry.u64 = odp_atomic_load_u64(&gbl_args->mac_tbl[idx]);
213
214 if (memcmp(mac->addr, entry.s.mac.addr, ODPH_ETHADDR_LEN))
215 return 0;
216
217 if (odp_unlikely(diff_ticks(cur_tick, entry.s.tick) > AGING_TIME))
218 return 0;
219
220 *port = entry.s.port;
221 return 1;
222}
223
231static inline void mac_table_update(odph_ethaddr_t *mac, uint8_t port,
232 uint8_t cur_tick)
233{
234 mac_tbl_entry_t entry;
235 uint16_t idx;
236
237 idx = calc_mac_tbl_idx(mac);
238 entry.u64 = odp_atomic_load_u64(&gbl_args->mac_tbl[idx]);
239
240 if (memcmp(entry.s.mac.addr, mac->addr, ODPH_ETHADDR_LEN) ||
241 entry.s.port != port || entry.s.tick != cur_tick) {
242 entry.s.mac = *mac;
243 entry.s.port = port;
244 entry.s.tick = cur_tick;
245 odp_atomic_store_u64(&gbl_args->mac_tbl[idx], entry.u64);
246 }
247}
248
261static int create_pktio(const char *dev, int idx, int num_rx, int num_tx,
262 odp_pool_t pool)
263{
264 odp_pktio_t pktio;
265 odp_pktio_param_t pktio_param;
267 odp_pktio_config_t config;
268 odp_pktin_queue_param_t pktin_param;
269 odp_pktout_queue_param_t pktout_param;
270 odp_pktio_op_mode_t mode_rx;
271 odp_pktio_op_mode_t mode_tx;
272
273 odp_pktio_param_init(&pktio_param);
274
275 pktio = odp_pktio_open(dev, pool, &pktio_param);
276 if (pktio == ODP_PKTIO_INVALID) {
277 printf("Error: failed to open %s\n", dev);
278 return -1;
279 }
280
281 printf("created pktio %" PRIu64 " (%s)\n", odp_pktio_to_u64(pktio),
282 dev);
283
284 if (odp_pktio_capability(pktio, &capa)) {
285 printf("Error: capability query failed %s\n", dev);
286 return -1;
287 }
288
289 odp_pktio_config_init(&config);
291 odp_pktio_config(pktio, &config);
292
293 odp_pktin_queue_param_init(&pktin_param);
294 odp_pktout_queue_param_init(&pktout_param);
295
296 mode_tx = ODP_PKTIO_OP_MT_UNSAFE;
297 mode_rx = ODP_PKTIO_OP_MT_UNSAFE;
298
299 if (num_rx > (int)capa.max_input_queues) {
300 printf("Sharing %i input queues between %i workers\n",
301 capa.max_input_queues, num_rx);
302 num_rx = capa.max_input_queues;
303 mode_rx = ODP_PKTIO_OP_MT;
304 }
305
306 if (num_tx > (int)capa.max_output_queues) {
307 printf("Sharing %i output queues between %i workers\n",
308 capa.max_output_queues, num_tx);
309 num_tx = capa.max_output_queues;
310 mode_tx = ODP_PKTIO_OP_MT;
311 }
312
313 pktin_param.hash_enable = (num_rx > 1) ? 1 : 0;
314 pktin_param.hash_proto.proto.ipv4_tcp = 1;
315 pktin_param.hash_proto.proto.ipv4_udp = 1;
316 pktin_param.num_queues = num_rx;
317 pktin_param.op_mode = mode_rx;
318
319 pktout_param.op_mode = mode_tx;
320 pktout_param.num_queues = num_tx;
321
322 if (odp_pktin_queue_config(pktio, &pktin_param)) {
323 printf("Error: input queue config failed %s\n", dev);
324 return -1;
325 }
326 if (odp_pktout_queue_config(pktio, &pktout_param)) {
327 printf("Error: output queue config failed %s\n", dev);
328 return -1;
329 }
330 if (odp_pktin_queue(pktio, gbl_args->pktios[idx].pktin,
331 num_rx) != num_rx) {
332 printf("Error: pktin queue query failed %s\n", dev);
333 return -1;
334 }
335 if (odp_pktout_queue(pktio, gbl_args->pktios[idx].pktout,
336 num_tx) != num_tx) {
337 printf("Error: pktout queue query failed %s\n", dev);
338 return -1;
339 }
340
341 printf("created %i input and %i output queues on (%s)\n", num_rx,
342 num_tx, dev);
343
344 gbl_args->pktios[idx].num_rx_queue = num_rx;
345 gbl_args->pktios[idx].num_tx_queue = num_tx;
346 gbl_args->pktios[idx].pktio = pktio;
347
348 return 0;
349}
350
362static int print_speed_stats(int num_workers, stats_t (*thr_stats)[MAX_PKTIOS],
363 int duration, int timeout)
364{
365 uint64_t rx_pkts_prev[MAX_PKTIOS] = {0};
366 uint64_t tx_pkts_prev[MAX_PKTIOS] = {0};
367 uint64_t rx_pkts_tot;
368 uint64_t tx_pkts_tot;
369 uint64_t rx_drops_tot;
370 uint64_t tx_drops_tot;
371 uint64_t rx_pps;
372 uint64_t tx_pps;
373 int i, j;
374 int elapsed = 0;
375 int stats_enabled = 1;
376 int loop_forever = (duration == 0);
377 int num_ifaces = gbl_args->appl.if_count;
378
379 if (timeout <= 0) {
380 stats_enabled = 0;
381 timeout = 1;
382 }
383 /* Wait for all threads to be ready*/
384 odp_barrier_wait(&gbl_args->barrier);
385
386 do {
387 uint64_t rx_pkts[MAX_PKTIOS] = {0};
388 uint64_t tx_pkts[MAX_PKTIOS] = {0};
389 uint64_t rx_drops[MAX_PKTIOS] = {0};
390 uint64_t tx_drops[MAX_PKTIOS] = {0};
391
392 rx_pkts_tot = 0;
393 tx_pkts_tot = 0;
394 rx_drops_tot = 0;
395 tx_drops_tot = 0;
396
397 sleep(timeout);
398 elapsed += timeout;
399
400 for (i = 0; i < num_workers; i++) {
401 for (j = 0; j < num_ifaces; j++) {
402 rx_pkts[j] += thr_stats[i][j].s.rx_packets;
403 tx_pkts[j] += thr_stats[i][j].s.tx_packets;
404 rx_drops[j] += thr_stats[i][j].s.rx_drops;
405 tx_drops[j] += thr_stats[i][j].s.tx_drops;
406 }
407 }
408
409 if (!stats_enabled)
410 continue;
411
412 for (j = 0; j < num_ifaces; j++) {
413 rx_pps = (rx_pkts[j] - rx_pkts_prev[j]) / timeout;
414 tx_pps = (tx_pkts[j] - tx_pkts_prev[j]) / timeout;
415 printf(" Port %d: %" PRIu64 " rx pps, %" PRIu64
416 " tx pps, %" PRIu64 " rx pkts, %" PRIu64
417 " tx pkts, %" PRIu64 " rx drops, %" PRIu64
418 " tx drops\n", j, rx_pps, tx_pps, rx_pkts[j],
419 tx_pkts[j], rx_drops[j], tx_drops[j]);
420
421 rx_pkts_prev[j] = rx_pkts[j];
422 tx_pkts_prev[j] = tx_pkts[j];
423 rx_pkts_tot += rx_pkts[j];
424 tx_pkts_tot += tx_pkts[j];
425 rx_drops_tot += rx_drops[j];
426 tx_drops_tot += tx_drops[j];
427 }
428
429 printf("Total: %" PRIu64 " rx pkts, %" PRIu64 " tx pkts, %"
430 PRIu64 " rx drops, %" PRIu64 " tx drops\n", rx_pkts_tot,
431 tx_pkts_tot, rx_drops_tot, tx_drops_tot);
432
433 } while (!odp_atomic_load_u32(&gbl_args->exit_threads) &&
434 (loop_forever || (elapsed < duration)));
435
436 return rx_pkts_tot >= 100 ? 0 : -1;
437}
438
442static void print_port_mapping(void)
443{
444 int if_count, num_workers;
445 int thr, pktio;
446
447 if_count = gbl_args->appl.if_count;
448 num_workers = gbl_args->appl.num_workers;
449
450 printf("\nWorker mapping table (port[queue])\n--------------------\n");
451
452 for (thr = 0; thr < num_workers; thr++) {
453 uint8_t port_idx;
454 int queue_idx;
455 thread_args_t *thr_args = &gbl_args->thread[thr];
456 int num = thr_args->num_rx_pktio;
457
458 printf("Worker %i\n", thr);
459
460 for (pktio = 0; pktio < num; pktio++) {
461 port_idx = thr_args->rx_pktio[pktio].port_idx;
462 queue_idx = thr_args->rx_pktio[pktio].queue_idx;
463 printf(" %i[%i]\n", port_idx, queue_idx);
464 }
465 }
466
467 printf("\nPort config\n--------------------\n");
468
469 for (pktio = 0; pktio < if_count; pktio++) {
470 const char *dev = gbl_args->appl.if_names[pktio];
471
472 printf("Port %i (%s)\n", pktio, dev);
473 printf(" rx workers %i\n",
474 gbl_args->pktios[pktio].num_rx_thr);
475 printf(" rx queues %i\n",
476 gbl_args->pktios[pktio].num_rx_queue);
477 printf(" tx queues %i\n",
478 gbl_args->pktios[pktio].num_tx_queue);
479 }
480
481 printf("\n");
482}
483
491static inline void broadcast_packet(odp_packet_t pkt, thread_args_t *thr_arg,
492 uint8_t port_in)
493{
494 odp_bool_t first = 1;
495 uint8_t port_out;
496 unsigned int buf_len;
497
498 for (port_out = 0; port_out < gbl_args->appl.if_count; port_out++) {
499 if (port_out == port_in)
500 continue;
501
502 buf_len = thr_arg->tx_pktio[port_out].buf.len;
503
504 if (first) { /* No need to copy for the first interface */
505 thr_arg->tx_pktio[port_out].buf.pkt[buf_len] = pkt;
506 first = 0;
507 } else {
508 odp_packet_t pkt_cp;
509
510 pkt_cp = odp_packet_copy(pkt, gbl_args->pool);
511 if (pkt_cp == ODP_PACKET_INVALID) {
512 printf("Error: packet copy failed\n");
513 continue;
514 }
515 thr_arg->tx_pktio[port_out].buf.pkt[buf_len] = pkt_cp;
516 }
517 thr_arg->tx_pktio[port_out].buf.len++;
518 }
519}
520
528static frame_type_t check_frame(odph_ethhdr_t *eth)
529{
530 static uint8_t broadcast_addr[ODPH_ETHADDR_LEN] = {0xff, 0xff, 0xff,
531 0xff, 0xff, 0xff};
532 static uint8_t null_addr[ODPH_ETHADDR_LEN] = {0, 0, 0, 0, 0, 0};
533
534 /* Drop invalid frames */
535 if (odp_unlikely(!memcmp(eth->src.addr, broadcast_addr,
536 ODPH_ETHADDR_LEN) ||
537 !memcmp(eth->dst.addr, null_addr,
538 ODPH_ETHADDR_LEN) ||
539 !memcmp(eth->src.addr, null_addr,
540 ODPH_ETHADDR_LEN))) {
541 return FRAME_INVALID;
542 }
543 if (!memcmp(eth->dst.addr, broadcast_addr, ODPH_ETHADDR_LEN))
544 return FRAME_BROADCAST;
545
546 return FRAME_UNICAST;
547}
548
563static inline void forward_packets(odp_packet_t pkt_tbl[], unsigned int num,
564 thread_args_t *thr_arg, uint8_t port_in,
565 uint8_t cur_tick)
566{
567 odp_packet_t pkt;
568 odph_ethhdr_t *eth;
569 unsigned int i;
570 unsigned int buf_id;
571 uint8_t port_out = 0;
572 int frame_type;
573
574 for (i = 0; i < num; i++) {
575 pkt = pkt_tbl[i];
576
577 if (!odp_packet_has_eth(pkt)) {
578 thr_arg->stats[port_in]->s.rx_drops++;
579 odp_packet_free(pkt);
580 continue;
581 }
582
583 eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
584
585 /* Check Ethernet frame type */
586 frame_type = check_frame(eth);
587 if (odp_unlikely(frame_type == FRAME_INVALID)) {
588 thr_arg->stats[port_in]->s.rx_drops++;
589 odp_packet_free(pkt);
590 continue;
591 }
592
593 /* Update source address MAC table entry */
594 mac_table_update(&eth->src, port_in, cur_tick);
595
596 /* Broadcast frame is necessary */
597 if (frame_type == FRAME_BROADCAST ||
598 !mac_table_get(&eth->dst, &port_out, cur_tick)) {
599 broadcast_packet(pkt, thr_arg, port_in);
600 continue;
601 }
602 buf_id = thr_arg->tx_pktio[port_out].buf.len;
603
604 thr_arg->tx_pktio[port_out].buf.pkt[buf_id] = pkt;
605 thr_arg->tx_pktio[port_out].buf.len++;
606 }
607}
608
609/*
610 * Bind worker threads to switch ports and calculate number of queues needed
611 *
612 * less workers (N) than interfaces (M)
613 * - assign each worker to process every Nth interface
614 * - workers process inequal number of interfaces, when M is not divisible by N
615 * - needs only single queue per interface
616 * otherwise
617 * - assign an interface to every Mth worker
618 * - interfaces are processed by inequal number of workers, when N is not
619 * divisible by M
620 * - tries to configure a queue per worker per interface
621 * - shares queues, if interface capability does not allows a queue per worker
622 */
623static void bind_workers(void)
624{
625 int if_count, num_workers;
626 int rx_idx, thr, pktio;
627 thread_args_t *thr_args;
628
629 if_count = gbl_args->appl.if_count;
630 num_workers = gbl_args->appl.num_workers;
631
632 if (if_count > num_workers) {
633 thr = 0;
634
635 for (rx_idx = 0; rx_idx < if_count; rx_idx++) {
636 thr_args = &gbl_args->thread[thr];
637 pktio = thr_args->num_rx_pktio;
638 thr_args->rx_pktio[pktio].port_idx = rx_idx;
639 thr_args->num_rx_pktio++;
640
641 gbl_args->pktios[rx_idx].num_rx_thr++;
642
643 thr++;
644 if (thr >= num_workers)
645 thr = 0;
646 }
647 } else {
648 rx_idx = 0;
649
650 for (thr = 0; thr < num_workers; thr++) {
651 thr_args = &gbl_args->thread[thr];
652 pktio = thr_args->num_rx_pktio;
653 thr_args->rx_pktio[pktio].port_idx = rx_idx;
654 thr_args->num_rx_pktio++;
655
656 gbl_args->pktios[rx_idx].num_rx_thr++;
657
658 rx_idx++;
659 if (rx_idx >= if_count)
660 rx_idx = 0;
661 }
662 }
663}
664
670static int run_worker(void *arg)
671{
672 thread_args_t *thr_args = arg;
673 odp_packet_t pkt_tbl[MAX_PKT_BURST];
674 odp_pktin_queue_t pktin;
675 odp_pktout_queue_t pktout;
676 odp_time_t time_prev;
677 odp_time_t minute;
678 uint8_t cur_tick;
679 unsigned int num_pktio;
680 unsigned int pktio = 0;
681 uint8_t port_in;
682 uint8_t port_out;
683 int pkts;
684
685 num_pktio = thr_args->num_rx_pktio;
686 pktin = thr_args->rx_pktio[pktio].pktin;
687 port_in = thr_args->rx_pktio[pktio].port_idx;
688
689 odp_barrier_wait(&gbl_args->barrier);
690
692 time_prev = odp_time_local();
693 cur_tick = (odp_time_to_ns(time_prev) / ODP_TIME_MIN_IN_NS) % UINT8_MAX;
694
695 while (!odp_atomic_load_u32(&gbl_args->exit_threads)) {
696 odp_time_t time_cur;
697 odp_time_t time_diff;
698 int sent;
699 unsigned int drops;
700
701 if (num_pktio > 1) {
702 pktin = thr_args->rx_pktio[pktio].pktin;
703 port_in = thr_args->rx_pktio[pktio].port_idx;
704 pktio++;
705 if (pktio == num_pktio)
706 pktio = 0;
707 }
708
709 pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST);
710 if (odp_unlikely(pkts <= 0))
711 continue;
712
713 time_cur = odp_time_local();
714 time_diff = odp_time_diff(time_cur, time_prev);
715
716 if (odp_unlikely(odp_time_cmp(time_diff, minute))) {
717 /* Tick stored as 8 bit value */
718 cur_tick = (odp_time_to_ns(time_cur) /
719 ODP_TIME_MIN_IN_NS) % UINT8_MAX;
720 time_prev = time_cur;
721 }
722
723 thr_args->stats[port_in]->s.rx_packets += pkts;
724
725 /* Sort packets to thread local tx buffers */
726 forward_packets(pkt_tbl, pkts, thr_args, port_in, cur_tick);
727
728 /* Empty all thread local tx buffers */
729 for (port_out = 0; port_out < gbl_args->appl.if_count;
730 port_out++) {
731 unsigned int tx_pkts;
732 odp_packet_t *tx_pkt_tbl;
733
734 if (port_out == port_in ||
735 thr_args->tx_pktio[port_out].buf.len == 0)
736 continue;
737
738 tx_pkts = thr_args->tx_pktio[port_out].buf.len;
739 thr_args->tx_pktio[port_out].buf.len = 0;
740
741 tx_pkt_tbl = thr_args->tx_pktio[port_out].buf.pkt;
742
743 pktout = thr_args->tx_pktio[port_out].pktout;
744
745 sent = odp_pktout_send(pktout, tx_pkt_tbl, tx_pkts);
746 sent = odp_unlikely(sent < 0) ? 0 : sent;
747
748 thr_args->stats[port_out]->s.tx_packets += sent;
749
750 drops = tx_pkts - sent;
751
752 if (odp_unlikely(drops)) {
753 unsigned int i;
754
755 thr_args->stats[port_out]->s.tx_drops += drops;
756
757 /* Drop rejected packets */
758 for (i = sent; i < tx_pkts; i++)
759 odp_packet_free(tx_pkt_tbl[i]);
760 }
761 }
762 }
763
764 /* Make sure that latest stat writes are visible to other threads */
765 odp_mb_full();
766
767 return 0;
768}
769
770/*
771 * Bind queues to threads and fill in missing thread arguments (handles)
772 */
773static void bind_queues(void)
774{
775 int num_workers;
776 int thr, pktio;
777
778 num_workers = gbl_args->appl.num_workers;
779
780 for (thr = 0; thr < num_workers; thr++) {
781 int rx_idx;
782 thread_args_t *thr_args = &gbl_args->thread[thr];
783 int num = thr_args->num_rx_pktio;
784
785 /* Receive only from selected ports */
786 for (pktio = 0; pktio < num; pktio++) {
787 int rx_queue;
788
789 rx_idx = thr_args->rx_pktio[pktio].port_idx;
790 rx_queue = gbl_args->pktios[rx_idx].next_rx_queue;
791
792 thr_args->rx_pktio[pktio].pktin =
793 gbl_args->pktios[rx_idx].pktin[rx_queue];
794 thr_args->rx_pktio[pktio].queue_idx = rx_queue;
795
796 rx_queue++;
797 if (rx_queue >= gbl_args->pktios[rx_idx].num_rx_queue)
798 rx_queue = 0;
799 gbl_args->pktios[rx_idx].next_rx_queue = rx_queue;
800 }
801 /* Send to all ports */
802 for (pktio = 0; pktio < (int)gbl_args->appl.if_count; pktio++) {
803 int tx_queue;
804
805 tx_queue = gbl_args->pktios[pktio].next_tx_queue;
806
807 thr_args->tx_pktio[pktio].pktout =
808 gbl_args->pktios[pktio].pktout[tx_queue];
809 thr_args->tx_pktio[pktio].queue_idx = tx_queue;
810
811 tx_queue++;
812 if (tx_queue >= gbl_args->pktios[pktio].num_tx_queue)
813 tx_queue = 0;
814 gbl_args->pktios[pktio].next_tx_queue = tx_queue;
815 }
816 }
817}
818
822static void usage(char *progname)
823{
824 printf("\n"
825 "OpenDataPlane learning switch example.\n"
826 "\n"
827 "Usage: %s OPTIONS\n"
828 " E.g. %s -i eth0,eth1,eth2,eth3\n"
829 "\n"
830 "Mandatory OPTIONS:\n"
831 " -i, --interface Eth interfaces (comma-separated, no spaces)\n"
832 " Interface count min 2, max %i\n"
833 "\n"
834 "Optional OPTIONS:\n"
835 " -c, --count <number> CPU count, 0=all available, default=1\n"
836 " -t, --time <number> Time in seconds to run.\n"
837 " -a, --accuracy <number> Statistics print interval in seconds\n"
838 " (default is 10 second).\n"
839 " -h, --help Display help and exit.\n\n"
840 "\n", NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS
841 );
842}
843
851static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
852{
853 int opt;
854 char *token;
855 size_t len;
856 unsigned int i;
857 static const struct option longopts[] = {
858 {"count", required_argument, NULL, 'c'},
859 {"time", required_argument, NULL, 't'},
860 {"accuracy", required_argument, NULL, 'a'},
861 {"interface", required_argument, NULL, 'i'},
862 {"help", no_argument, NULL, 'h'},
863 {NULL, 0, NULL, 0}
864 };
865
866 static const char *shortopts = "+c:t:a:i:h";
867
868 appl_args->cpu_count = 1; /* use one worker by default */
869 appl_args->time = 0; /* loop forever if time to run is 0 */
870 appl_args->accuracy = 10; /* get and print pps stats second */
871
872 while (1) {
873 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
874
875 if (opt == -1)
876 break; /* No more options */
877
878 switch (opt) {
879 case 'c':
880 appl_args->cpu_count = atoi(optarg);
881 break;
882 case 't':
883 appl_args->time = atoi(optarg);
884 break;
885 case 'a':
886 appl_args->accuracy = atoi(optarg);
887 break;
888 case 'i':
889 len = strlen(optarg);
890 if (len == 0) {
891 usage(argv[0]);
892 exit(EXIT_FAILURE);
893 }
894 len += 1; /* add room for '\0' */
895
896 appl_args->if_str = malloc(len);
897 if (appl_args->if_str == NULL) {
898 usage(argv[0]);
899 exit(EXIT_FAILURE);
900 }
901
902 /* count the number of tokens separated by ',' */
903 strcpy(appl_args->if_str, optarg);
904 for (token = strtok(appl_args->if_str, ","), i = 0;
905 token != NULL;
906 token = strtok(NULL, ","), i++)
907 ;
908
909 appl_args->if_count = i;
910
911 if (appl_args->if_count < 2 ||
912 appl_args->if_count > MAX_PKTIOS) {
913 usage(argv[0]);
914 exit(EXIT_FAILURE);
915 }
916
917 /* allocate storage for the if names */
918 appl_args->if_names =
919 calloc(appl_args->if_count, sizeof(char *));
920
921 /* store the if names (reset names string) */
922 strcpy(appl_args->if_str, optarg);
923 for (token = strtok(appl_args->if_str, ","), i = 0;
924 token != NULL; token = strtok(NULL, ","), i++) {
925 appl_args->if_names[i] = token;
926 }
927 break;
928 case 'h':
929 usage(argv[0]);
930 exit(EXIT_SUCCESS);
931 break;
932 default:
933 break;
934 }
935 }
936
937 if (appl_args->if_count == 0) {
938 usage(argv[0]);
939 exit(EXIT_FAILURE);
940 }
941
942 optind = 1; /* reset 'extern optind' from the getopt lib */
943}
944
948static void print_info(char *progname, appl_args_t *appl_args)
949{
950 unsigned int i;
951
953
954 printf("Running ODP appl: \"%s\"\n"
955 "-----------------\n"
956 "IF-count: %i\n"
957 "Using IFs: ",
958 progname, appl_args->if_count);
959 for (i = 0; i < appl_args->if_count; ++i)
960 printf(" %s", appl_args->if_names[i]);
961 printf("\n\n");
962 fflush(NULL);
963}
964
965static void gbl_args_init(args_t *args)
966{
967 int pktio;
968
969 memset(args, 0, sizeof(args_t));
970 odp_atomic_init_u32(&args->exit_threads, 0);
971
972 for (pktio = 0; pktio < MAX_PKTIOS; pktio++)
973 args->pktios[pktio].pktio = ODP_PKTIO_INVALID;
974}
975
976int main(int argc, char **argv)
977{
978 odph_helper_options_t helper_options;
979 odph_thread_t thread_tbl[MAX_WORKERS];
980 odph_thread_common_param_t thr_common;
981 odph_thread_param_t thr_param[MAX_WORKERS];
982 int i, j;
983 int num_workers;
984 odp_shm_t shm;
985 odp_cpumask_t cpumask;
986 char cpumaskstr[ODP_CPUMASK_STR_SIZE];
987 odp_pool_param_t params;
988 int ret;
989 stats_t (*stats)[MAX_PKTIOS];
990 int if_count;
991 odp_instance_t instance;
992 odp_init_t init_param;
993
994 signal(SIGINT, sig_handler);
995
996 /* Let helper collect its own arguments (e.g. --odph_proc) */
997 argc = odph_parse_options(argc, argv);
998 if (odph_options(&helper_options)) {
999 printf("Error: reading ODP helper options failed.\n");
1000 exit(EXIT_FAILURE);
1001 }
1002
1003 odp_init_param_init(&init_param);
1004 init_param.mem_model = helper_options.mem_model;
1005
1006 /* Init ODP before calling anything else */
1007 if (odp_init_global(&instance, &init_param, NULL)) {
1008 printf("Error: ODP global init failed.\n");
1009 exit(EXIT_FAILURE);
1010 }
1011
1012 /* Init this thread */
1013 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
1014 printf("Error: ODP local init failed.\n");
1015 exit(EXIT_FAILURE);
1016 }
1017
1018 /* Reserve memory for args from shared mem */
1019 shm = odp_shm_reserve("shm_args", sizeof(args_t),
1020 ODP_CACHE_LINE_SIZE, 0);
1021
1022 if (shm == ODP_SHM_INVALID) {
1023 printf("Error: shared mem reserve failed.\n");
1024 exit(EXIT_FAILURE);
1025 }
1026
1027 gbl_args = odp_shm_addr(shm);
1028
1029 if (gbl_args == NULL) {
1030 printf("Error: shared mem alloc failed.\n");
1031 exit(EXIT_FAILURE);
1032 }
1033 gbl_args_init(gbl_args);
1034
1035 for (i = 0; (unsigned int)i < MAC_TBL_SIZE; i++)
1036 odp_atomic_init_u64(&gbl_args->mac_tbl[i], 0);
1037
1038 /* Parse and store the application arguments */
1039 parse_args(argc, argv, &gbl_args->appl);
1040
1041 /* Print both system and application information */
1042 print_info(NO_PATH(argv[0]), &gbl_args->appl);
1043
1044 num_workers = MAX_WORKERS;
1045 if (gbl_args->appl.cpu_count && gbl_args->appl.cpu_count < MAX_WORKERS)
1046 num_workers = gbl_args->appl.cpu_count;
1047
1048 /* Get default worker cpumask */
1049 num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
1050 (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
1051
1052 gbl_args->appl.num_workers = num_workers;
1053
1054 if_count = gbl_args->appl.if_count;
1055
1056 printf("num worker threads: %i\n", num_workers);
1057 printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
1058 printf("cpu mask: %s\n", cpumaskstr);
1059
1060 /* Create packet pool */
1061 odp_pool_param_init(&params);
1062 params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
1063 params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
1064 params.pkt.num = SHM_PKT_POOL_SIZE;
1065 params.type = ODP_POOL_PACKET;
1066
1067 gbl_args->pool = odp_pool_create("packet pool", &params);
1068 if (gbl_args->pool == ODP_POOL_INVALID) {
1069 printf("Error: packet pool create failed.\n");
1070 exit(EXIT_FAILURE);
1071 }
1072 odp_pool_print(gbl_args->pool);
1073
1074 bind_workers();
1075
1076 for (i = 0; i < if_count; ++i) {
1077 const char *dev = gbl_args->appl.if_names[i];
1078 int num_rx;
1079
1080 /* An RX queue per assigned worker and a private TX queue for
1081 * each worker */
1082 num_rx = gbl_args->pktios[i].num_rx_thr;
1083
1084 if (create_pktio(dev, i, num_rx, num_workers, gbl_args->pool))
1085 exit(EXIT_FAILURE);
1086
1087 if (odp_pktio_promisc_mode(gbl_args->pktios[i].pktio) != 1) {
1088 ret = odp_pktio_promisc_mode_set(gbl_args->pktios[i].pktio, 1);
1089 if (ret != 0) {
1090 printf("Error: failed to set %s to promiscuous mode.\n", dev);
1091 exit(EXIT_FAILURE);
1092 }
1093 }
1094 }
1095 gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID;
1096
1097 bind_queues();
1098
1099 print_port_mapping();
1100
1101 memset(thread_tbl, 0, sizeof(thread_tbl));
1102
1103 odp_barrier_init(&gbl_args->barrier, num_workers + 1);
1104
1105 stats = gbl_args->stats;
1106
1107 odph_thread_common_param_init(&thr_common);
1108 thr_common.instance = instance;
1109 thr_common.cpumask = &cpumask;
1110
1111 /* Create worker threads */
1112 for (i = 0; i < num_workers; ++i) {
1113 for (j = 0; j < MAX_PKTIOS; j++)
1114 gbl_args->thread[i].stats[j] = &stats[i][j];
1115
1116 odph_thread_param_init(&thr_param[i]);
1117 thr_param[i].start = run_worker;
1118 thr_param[i].arg = &gbl_args->thread[i];
1119 thr_param[i].thr_type = ODP_THREAD_WORKER;
1120 }
1121
1122 odph_thread_create(thread_tbl, &thr_common, thr_param, num_workers);
1123
1124 /* Start packet receive and transmit */
1125 for (i = 0; i < if_count; ++i) {
1126 odp_pktio_t pktio;
1127
1128 pktio = gbl_args->pktios[i].pktio;
1129 ret = odp_pktio_start(pktio);
1130 if (ret) {
1131 printf("Error: unable to start %s\n",
1132 gbl_args->appl.if_names[i]);
1133 exit(EXIT_FAILURE);
1134 }
1135 }
1136
1137 ret = print_speed_stats(num_workers, gbl_args->stats,
1138 gbl_args->appl.time, gbl_args->appl.accuracy);
1139 odp_atomic_store_u32(&gbl_args->exit_threads, 1);
1140
1141 /* Master thread waits for other threads to exit */
1142 odph_thread_join(thread_tbl, num_workers);
1143
1144 /* Stop and close used pktio devices */
1145 for (i = 0; i < if_count; i++) {
1146 odp_pktio_t pktio = gbl_args->pktios[i].pktio;
1147
1148 if (odp_pktio_stop(pktio) || odp_pktio_close(pktio)) {
1149 printf("Error: failed to close pktio\n");
1150 exit(EXIT_FAILURE);
1151 }
1152 }
1153
1154 free(gbl_args->appl.if_names);
1155 free(gbl_args->appl.if_str);
1156
1157 if (odp_pool_destroy(gbl_args->pool)) {
1158 printf("Error: pool destroy\n");
1159 exit(EXIT_FAILURE);
1160 }
1161
1162 if (odp_shm_free(shm)) {
1163 printf("Error: shm free\n");
1164 exit(EXIT_FAILURE);
1165 }
1166
1167 if (odp_term_local()) {
1168 printf("Error: term local\n");
1169 exit(EXIT_FAILURE);
1170 }
1171
1172 if (odp_term_global(instance)) {
1173 printf("Error: term global\n");
1174 exit(EXIT_FAILURE);
1175 }
1176
1177 return ret;
1178}
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_init_u64(odp_atomic_u64_t *atom, uint64_t val)
Initialize atomic uint64 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_atomic_store_u64(odp_atomic_u64_t *atom, uint64_t val)
Store value to atomic uint64 variable.
uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
Load value of atomic uint64 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
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_cpumask_first(const odp_cpumask_t *mask)
Find first set CPU in mask.
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_...
uint32_t odp_hash_crc32c(const void *data, uint32_t data_len, uint32_t init_val)
Calculate CRC-32C.
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.
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_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
int odp_pktio_promisc_mode_set(odp_pktio_t pktio, odp_bool_t enable)
Set promiscuous mode.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_config(odp_pktio_t pktio, const odp_pktio_config_t *config)
Configure packet IO interface options.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
Direct packet input queues.
void odp_pktout_queue_param_init(odp_pktout_queue_param_t *param)
Initialize packet output queue parameters.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
Receive packets directly from an interface input queue.
int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa)
Query packet IO interface capabilities.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
odp_pktio_op_mode_t
Packet IO operation mode.
uint64_t odp_pktio_to_u64(odp_pktio_t pktio)
Get printable value for an odp_pktio_t.
int odp_pktin_queue_config(odp_pktio_t pktio, const odp_pktin_queue_param_t *param)
Configure packet input queues.
int odp_pktout_queue_config(odp_pktio_t pktio, const odp_pktout_queue_param_t *param)
Configure packet output queues.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIO_OP_MT
Multithread safe operation.
odp_packet_t odp_packet_copy(odp_packet_t pkt, odp_pool_t pool)
Full copy of a packet.
int odp_packet_has_eth(odp_packet_t pkt)
Check for Ethernet header.
void odp_packet_free(odp_packet_t pkt)
Free packet.
void * odp_packet_l2_ptr(odp_packet_t pkt, uint32_t *len)
Layer 2 start pointer.
#define ODP_PACKET_INVALID
Invalid packet.
@ ODP_PROTO_LAYER_L2
Layer L2 protocols (Ethernet, VLAN, etc)
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
void odp_pool_print(odp_pool_t pool)
Print pool info.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
void odp_sys_info_print(void)
Print system info.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
uint64_t odp_time_to_ns(odp_time_t time)
Convert time to nanoseconds.
#define ODP_TIME_MIN_IN_NS
A minute in nanoseconds.
odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
Time difference.
odp_time_t odp_time_local_from_ns(uint64_t ns)
Convert nanoseconds to local time.
odp_time_t odp_time_local(void)
Current local time.
int odp_time_cmp(odp_time_t t2, odp_time_t t1)
Compare two times.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Packet input queue parameters.
uint32_t num_queues
Number of input queues to be created.
odp_pktio_op_mode_t op_mode
Operation mode.
odp_pktin_hash_proto_t hash_proto
Protocol field selection for hashing.
odp_bool_t hash_enable
Enable flow hashing.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_output_queues
Maximum number of output queues.
Packet IO configuration options.
odp_pktio_parser_config_t parser
Packet input parser configuration.
Packet IO parameters.
odp_proto_layer_t layer
Protocol parsing level in packet input.
Packet output queue parameters.
odp_pktio_op_mode_t op_mode
Operation mode.
uint32_t num_queues
Number of output queues to be created.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
uint32_t ipv4_tcp
IPv4 addresses and TCP port numbers.
struct odp_pktin_hash_proto_t::@107 proto
Protocol header fields for hashing.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.