API Reference Manual  1.46.0
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 
53 typedef 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 
66 typedef 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 
76 typedef enum frame_type_t {
77  FRAME_UNICAST,
78  FRAME_BROADCAST,
79  FRAME_INVALID
80 } frame_type_t;
81 
85 typedef 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 
103 typedef struct pkt_buf_t {
104  odp_packet_t pkt[MAX_PKT_BURST];
105  unsigned int len;
106 } pkt_buf_t;
107 
111 typedef 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 
131 typedef 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 
157 static args_t *gbl_args;
158 
159 static 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 
173 static 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 
185 static 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 
204 static 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 
231 static 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 
261 static 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 
362 static 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 
442 static 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 
491 static 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 
528 static 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 
563 static 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  */
623 static 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 
670 static 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  */
773 static 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 
822 static 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 
851 static 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 
948 static 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 
965 static 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 
976 int 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.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
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.
Pool parameters.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
uint32_t ipv4_tcp
IPv4 addresses and TCP port numbers.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.
struct odp_pktin_hash_proto_t::@99 proto
Protocol header fields for hashing.