API Reference Manual  1.46.0
odp_packet_gen.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2020-2024 Nokia
3  */
4 
13 /* enable usleep */
14 #ifndef _GNU_SOURCE
15 #define _GNU_SOURCE
16 #endif
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdint.h>
21 #include <inttypes.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <getopt.h>
25 #include <unistd.h>
26 
27 #include <odp_api.h>
28 #include <odp/helper/odph_api.h>
29 
30 #if ODP_THREAD_COUNT_MAX > 33
31 /* One control thread, even number of workers */
32 #define MAX_THREADS 33
33 #else
34 #define MAX_THREADS ODP_THREAD_COUNT_MAX
35 #endif
36 
37 #define MAX_WORKERS (MAX_THREADS - 1)
38 
39 /* At least one control and one worker thread */
40 ODP_STATIC_ASSERT(MAX_WORKERS >= 1, "Too few threads");
41 
42 /* Maximum number of packet IO interfaces */
43 #define MAX_PKTIOS 16
44 /* Maximum number of packets to be allocated for
45  * one transmit round: bursts * burst_size * bins */
46 #define MAX_ALLOC_PACKETS (64 * 1024)
47 /* Maximum number of packet length bins */
48 #define MAX_BINS 1024
49 #define MAX_PKTIO_NAME 255
50 #define RX_THREAD 1
51 #define TX_THREAD 2
52 #define MAX_VLANS 4
53 #define ETH_TYPE_QINQ 0x88a8
54 /* Number of random 16-bit words used to generate random length packets */
55 #define RAND_16BIT_WORDS MAX_BINS
56 /* Max retries to generate random data */
57 #define MAX_RAND_RETRIES 1000
58 #define MAX_HDR_NAME_LEN 32
59 #define MAX_HDR_FIELDS 16
60 #define MAX_HDR_VALUE_SZ 8
61 #define TOKEN_DELIMITER ","
62 #define FIELD_DELIMITER ":"
63 
64 /* Use don't free */
65 #define TX_MODE_DF 0
66 /* Use static references */
67 #define TX_MODE_REF 1
68 /* Use packet copy */
69 #define TX_MODE_COPY 2
70 
71 /* Minimum number of packets to receive in CI test */
72 #define MIN_RX_PACKETS_CI 800
73 
74 /* Identifier for payload-timestamped packets */
75 #define TS_MAGIC 0xff88ee99ddaaccbb
76 
77 #define S_(x) #x
78 #define S(x) S_(x)
79 
80 enum {
81  L4_PROTO_UDP = 0,
82  L4_PROTO_TCP,
83  L4_PROTO_NONE
84 };
85 
86 ODP_STATIC_ASSERT(MAX_PKTIOS <= UINT8_MAX, "Interface index must fit into uint8_t\n");
87 
88 typedef struct {
89  char name[MAX_HDR_NAME_LEN + 1];
90  uint64_t value;
91  int64_t diff;
92  uint32_t len;
93 } hdr_field_t;
94 
95 typedef struct {
96  hdr_field_t fields[MAX_HDR_FIELDS];
97  uint32_t tot_len;
98  uint16_t eth_type;
99 } hdr_t;
100 
101 typedef struct test_options_t {
102  uint64_t gap_nsec;
103  uint64_t quit;
104  uint64_t update_msec;
105  uint32_t num_rx;
106  uint32_t num_tx;
107  uint32_t num_cpu;
108  uint32_t num_pktio;
109  uint32_t num_pkt;
110  uint32_t pkt_len;
111  uint8_t use_rand_pkt_len;
112  uint8_t direct_rx;
113  uint32_t rand_pkt_len_min;
114  uint32_t rand_pkt_len_max;
115  uint32_t rand_pkt_len_bins;
116  uint32_t hdr_len;
117  uint32_t burst_size;
118  uint32_t bursts;
119  uint16_t eth_type;
120  uint32_t num_vlan;
121  uint32_t l3_len;
122  uint32_t ipv4_src;
123  uint32_t ipv4_dst;
124  uint16_t src_port;
125  uint16_t dst_port;
126  uint32_t wait_sec;
127  uint32_t wait_start_sec;
128  uint32_t mtu;
129  uint32_t num_custom_l3;
130  struct {
132  uint32_t payload_offset;
133  uint32_t max_payload_len;
134  odp_bool_t enabled;
135  } lso;
136  uint8_t l4_proto;
137  int tx_mode;
138  odp_bool_t promisc_mode;
139  odp_bool_t calc_latency;
140  odp_bool_t calc_cs;
141  odp_bool_t cs_offload;
142  odp_bool_t fill_pl;
143 
144  struct vlan_hdr {
145  uint16_t tpid;
146  uint16_t tci;
147  } vlan[MAX_VLANS];
148 
149  struct {
150  uint32_t src_port;
151  uint32_t dst_port;
152  } c_mode;
153 
154  char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1];
155  char ipv4_src_s[24];
156  char ipv4_dst_s[24];
157 
158  hdr_t custom_l3;
159 
160 } test_options_t;
161 
162 typedef struct thread_arg_t {
163  void *global;
164  int tx_thr;
165 
166  /* pktout queue per pktio interface (per thread) */
167  odp_pktout_queue_t pktout[MAX_PKTIOS];
168 
169  /* In direct_rx mode, pktin queue per pktio interface (per thread) */
170  odp_pktin_queue_t pktin[MAX_PKTIOS];
171 
172  /* LSO profile per pktio interface */
173  odp_lso_profile_t lso_profile[MAX_PKTIOS];
174 
175  /* Pre-built packets for TX thread */
176  odp_packet_t packet[MAX_PKTIOS][MAX_ALLOC_PACKETS];
177 
178 } thread_arg_t;
179 
180 typedef struct ODP_ALIGNED_CACHE thread_stat_t {
181  uint64_t time_nsec;
182  uint64_t rx_timeouts;
183  uint64_t rx_packets;
184  uint64_t rx_bytes;
185  uint64_t rx_lat_nsec;
186  uint64_t rx_lat_min_nsec;
187  uint64_t rx_lat_max_nsec;
188  uint64_t rx_lat_packets;
189 
190  uint64_t tx_timeouts;
191  uint64_t tx_packets;
192  uint64_t tx_bytes;
193  uint64_t tx_drops;
194 
195  int thread_type;
196 
197  struct {
198  uint64_t rx_packets;
199  uint64_t tx_packets;
200 
201  } pktio[MAX_PKTIOS];
202 
203 } thread_stat_t;
204 
205 typedef struct test_global_t {
206  test_options_t test_options;
207  odp_atomic_u32_t exit_test;
208  odp_barrier_t barrier;
209  odp_cpumask_t cpumask;
210  odp_pool_t pool;
211  uint64_t drained;
212  odph_thread_t thread_tbl[MAX_THREADS];
213  thread_stat_t stat[MAX_THREADS];
214  thread_arg_t thread_arg[MAX_THREADS];
215 
216  struct {
217  odph_ethaddr_t eth_src;
218  odph_ethaddr_t eth_dst;
219  odp_pktio_t pktio;
220  odp_lso_profile_t lso_profile;
221  odp_pktout_queue_t pktout[MAX_THREADS];
222  odp_pktin_queue_t pktin[MAX_THREADS];
223  int started;
224 
225  } pktio[MAX_PKTIOS];
226 
227  /* Interface lookup table. Table index is pktio_index of the API. */
228  uint8_t if_from_pktio_idx[ODP_PKTIO_MAX_INDEX + 1];
229 
230  uint32_t num_tx_pkt;
231  uint32_t num_bins;
232  uint32_t len_bin[MAX_BINS];
233 
234  /* Per thread random data */
235  uint16_t rand_data[MAX_THREADS][RAND_16BIT_WORDS];
236 
237 } test_global_t;
238 
239 typedef struct ODP_PACKED {
240  uint64_t magic;
241  odp_time_t tx_ts;
242 } ts_data_t;
243 
244 typedef struct {
245  uint64_t nsec;
246  uint64_t min;
247  uint64_t max;
248  uint64_t packets;
249 } rx_lat_data_t;
250 
251 typedef int (*send_fn_t)(odp_pktout_queue_t pktout, odp_packet_t pkt[], uint32_t num,
252  int tx_mode, uint64_t *drop_bytes, const odp_packet_lso_opt_t *lso_opt);
253 
254 static test_global_t *test_global;
255 
256 static void print_usage(void)
257 {
258  printf("\n"
259  "ODP packet generator\n"
260  "\n"
261  "Usage: odp_packet_gen [options]\n"
262  "\n"
263  " Mandatory:\n"
264  " -i, --interface <name> Packet IO interfaces. Comma-separated list of\n"
265  " interface names (no spaces) e.g. eth0,eth1.\n"
266  " At least one interface is required.\n"
267  "\n");
268  printf(" Optional:\n"
269  " -e, --eth_dst <mac> Destination MAC address. Comma-separated list of\n"
270  " addresses (no spaces), one address per packet IO\n"
271  " interface e.g. AA:BB:CC:DD:EE:FF,11:22:33:44:55:66\n"
272  " Default per interface: 02:00:00:A0:B0:CX, where X = 0,1,...\n"
273  " -v, --vlan <tpid:tci> VLAN configuration. Comma-separated list of VLAN TPID:TCI\n"
274  " values in hexadecimal, starting from the outer most VLAN.\n"
275  " For example:\n"
276  " VLAN 200 (decimal): 0x8100:c8\n"
277  " Double tagged VLANs 1 and 2: 0x88a8:1,0x8100:2\n"
278  " -r, --num_rx Number of receive threads. Default: 1\n"
279  " -t, --num_tx Number of transmit threads. Default: 1\n"
280  " -T, --lso <options> Transmit packets with Large Send Offload (LSO). Specify\n"
281  " LSO options as comma-separated list (no spaces) in\n"
282  " format: protocol,payload_offset(B),max_payload_len(B). E.g.:\n"
283  " 0,34,1500\n"
284  " In case of ODP_LSO_PROTO_CUSTOM the list is extended by\n"
285  " up to %d custom modification options in format:\n"
286  " mod_op:offset(B):size(B) separated by commas. E.g.:\n"
287  " 2,22,1500,0:19:1\n"
288  " Supported protocols:\n"
289  " 0: ODP_LSO_PROTO_IPV4\n"
290  " 1: ODP_LSO_PROTO_TCP_IPV4\n"
291  " 2: ODP_LSO_PROTO_CUSTOM\n"
292  " Custom modification options:\n"
293  " 0: ODP_LSO_ADD_SEGMENT_NUM\n"
294  " 1: ODP_LSO_ADD_PAYLOAD_LEN\n"
295  " 2: ODP_LSO_ADD_PAYLOAD_OFFSET\n"
296  " Depending on the implementation, all listed LSO options\n"
297  " may not be always supported.\n"
298  " -n, --num_pkt Number of packets in the pool. Default: 1000\n"
299  " -l, --len Packet length. Default: 512\n"
300  " -L, --len_range <min,max,bins>\n"
301  " Random packet length. Specify the minimum and maximum\n"
302  " packet lengths and the number of bins. To reduce pool size\n"
303  " requirement the length range can be divided into even sized\n"
304  " bins (max %u). Min and max size packets are always used and included\n"
305  " into the number of bins (bins >= 2). Bin value of 0 means\n"
306  " that each packet length is used. Comma-separated (no spaces).\n"
307  " Overrides standard packet length option.\n"
308  " -D, --direct_rx Direct input mode (default: 0)\n"
309  " 0: Use scheduler for packet input\n"
310  " 1: Poll packet input in direct mode\n",
311  ODP_LSO_MAX_CUSTOM, MAX_BINS);
312  printf(" -m, --tx_mode Transmit mode (default 1):\n"
313  " 0: Re-send packets with don't free option\n"
314  " 1: Send static packet references. Some features may\n"
315  " not be available with references.\n"
316  " 2: Send copies of packets\n"
317  " -M, --mtu <len> Interface MTU in bytes.\n"
318  " -b, --burst_size Transmit burst size. Default: 8\n"
319  " -x, --bursts Number of bursts per one transmit round. Default: 1\n"
320  " -g, --gap Gap between transmit rounds in nsec. Default: 1000000\n"
321  " Transmit packet rate per interface:\n"
322  " num_tx * burst_size * bursts * (10^9 / gap)\n"
323  " -s, --ipv4_src IPv4 source address. Default: 192.168.0.1\n"
324  " -d, --ipv4_dst IPv4 destination address. Default: 192.168.0.2\n"
325  " -o, --src_port UDP/TCP source port. Default: 10000\n"
326  " -p, --dst_port UDP/TCP destination port. Default: 20000\n"
327  " -N, --proto L4 protocol. Default: 0\n"
328  " 0: UDP\n"
329  " 1: TCP\n"
330  " 2: none\n"
331  " -P, --promisc_mode Enable promiscuous mode.\n"
332  " -a, --latency Calculate latency. Cannot be used with packet\n"
333  " references (see \"--tx_mode\").\n"
334  " -c, --c_mode <counts> Counter mode for incrementing UDP/TCP port numbers.\n"
335  " Specify the number of port numbers used starting from\n"
336  " src_port/dst_port. Comma-separated (no spaces) list of\n"
337  " count values: <src_port count>,<dst_port count>\n"
338  " Default value: 0,0\n"
339  " -C, --no_udp_checksum Do not calculate UDP SW checksum. Instead, set it to\n"
340  " zero in every packet, this may be overridden by\n"
341  " '--checksum_offload' option.\n"
342  " -X, --checksum_offload Enable L4 checksum offloads: checksums are checked\n"
343  " at input and inserted at output.\n"
344  " -A, --no_payload_fill Do not fill payload. By default, payload is filled\n"
345  " with a pattern until the end of first packet\n"
346  " segment.\n"
347  " -q, --quit Quit after this many transmit rounds.\n"
348  " Default: 0 (don't quit)\n"
349  " -u, --update_stat <msec> Update and print statistics every <msec> milliseconds.\n"
350  " 0: Don't print statistics periodically (default)\n"
351  " -h, --help This help\n"
352  " -w, --wait <sec> Wait up to <sec> seconds for network links to be up.\n"
353  " Default: 0 (don't check link status)\n");
354  printf(" -U, --custom_l3 <definition>\n"
355  " Define a custom L3 header for packets. This\n"
356  " overrides the default IP header and any related\n"
357  " options. Elements should be comma-separated (no\n"
358  " spaces). Definition should begin with an EtherType\n"
359  " value, followed by field definitions. Each field\n"
360  " should be in the format\n"
361  " <name>:<length(B)>:<value>:<diff>, i.e.\n"
362  " colon-separated (no spaces). Name/length/value\n"
363  " elements are self-explanatory, the 'diff' element\n"
364  " defines a value that's added (subtracted if\n"
365  " negative) to the 'value' element in successive\n"
366  " packets. Fields are used in the order they are\n"
367  " defined in the string. E.g.:\n\n"
368  " 0x900,a:4:0xaaaaaaaa:1,b:1:0xff:-2\n\n"
369  " would result in a header of EtherType 0x900 with\n"
370  " fields 'a' and 'b' of values and lengths\n"
371  " '0xaaaaaaaa' (4 bytes) and '0xff' (1 byte)\n"
372  " respectively. Value 1 is added to '0xaaaaaaaa' and\n"
373  " -2 to '0xff' in successive packets. EtherType and\n"
374  " 'value' elements should be given in hexadecimals.\n"
375  " Field names are only for information and debugging\n"
376  " purposes. Maximum amount of fields supported is %u,\n"
377  " maximum name length of a field is %u and maximum\n"
378  " value size is %u bytes.\n"
379  " -W, --wait_start <sec> Wait <sec> seconds before starting traffic. Default: 0\n"
380  "\n", MAX_HDR_FIELDS, MAX_HDR_NAME_LEN, MAX_HDR_VALUE_SZ);
381 }
382 
383 static int parse_vlan(const char *str, test_global_t *global)
384 {
385  struct vlan_hdr *vlan;
386  const char *start = str;
387  char *end;
388  int num_vlan = 0;
389  intptr_t str_len = strlen(str);
390 
391  while (num_vlan < MAX_VLANS) {
392  vlan = &global->test_options.vlan[num_vlan];
393 
394  /* TPID in hexadecimal */
395  end = NULL;
396  vlan->tpid = strtoul(start, &end, 16);
397  if (end < start)
398  break;
399 
400  /* Skip ':' */
401  start = end + 1;
402  if (start - str >= str_len)
403  break;
404 
405  /* TCI in hexadecimal */
406  end = NULL;
407  vlan->tci = strtoul(start, &end, 16);
408  if (end < start)
409  break;
410 
411  num_vlan++;
412 
413  /* Skip ',' or stop at the string end */
414  start = end + 1;
415  if (start - str >= str_len)
416  break;
417  }
418 
419  return num_vlan;
420 }
421 
422 static inline uint64_t bswap(uint64_t in, uint32_t len)
423 {
424  uint8_t byte;
425  uint64_t result = 0;
426 
427  if (ODP_BIG_ENDIAN || len == sizeof(uint8_t))
428  return in;
429 
430  for (uint32_t i = 0; i < len; i++) {
431  byte = (in >> (8 * i)) & 0xff;
432  result |= ((uint64_t)byte << (8 * (len - 1 - i)));
433  }
434 
435  return result;
436 }
437 
438 static odp_bool_t parse_custom_fields(const char *optarg, test_options_t *opts)
439 {
440  char *tmp_str = strdup(optarg), *tmp;
441  uint32_t num_fields = 0;
442  char name[MAX_HDR_NAME_LEN + 1];
443  uint32_t len;
444  uint64_t value;
445  int64_t diff;
446  hdr_field_t *hdr;
447  int ret;
448 
449  if (tmp_str == NULL)
450  return false;
451 
452  tmp = strtok(tmp_str, TOKEN_DELIMITER);
453 
454  if (tmp == NULL) {
455  free(tmp_str);
456  return false;
457  }
458 
459  opts->custom_l3.eth_type = strtoul(tmp, NULL, 16);
460  tmp = strtok(NULL, TOKEN_DELIMITER);
461 
462  while (tmp) {
463  if (num_fields == MAX_HDR_FIELDS) {
464  ODPH_ERR("Invalid custom header, too many fields: %u\n", num_fields + 1);
465  free(tmp_str);
466  return false;
467  }
468 
469  ret = sscanf(tmp, "%" S(MAX_HDR_NAME_LEN) "[^" FIELD_DELIMITER "]" FIELD_DELIMITER
470  "%u" FIELD_DELIMITER "%" PRIx64 FIELD_DELIMITER "%" PRIi64 "", name,
471  &len, &value, &diff);
472 
473  if (ret != 4) {
474  ODPH_ERR("Invalid custom header, bad field format\n");
475  free(tmp_str);
476  return false;
477  }
478 
479  if (len > MAX_HDR_VALUE_SZ) {
480  ODPH_ERR("Invalid custom header, field length too long: %u\n", len);
481  free(tmp_str);
482  return false;
483  }
484 
485  hdr = &opts->custom_l3.fields[num_fields];
486  odph_strcpy(hdr->name, name, MAX_HDR_NAME_LEN + 1);
487  hdr->value = value;
488  hdr->diff = diff;
489  hdr->len = len;
490  opts->custom_l3.tot_len += len;
491  ++num_fields;
492  tmp = strtok(NULL, TOKEN_DELIMITER);
493  }
494 
495  opts->num_custom_l3 = num_fields;
496  free(tmp_str);
497 
498  return true;
499 }
500 
501 static odp_bool_t parse_lso_fields(const char *optarg, test_options_t *opts)
502 {
503  char *tmp_str = strdup(optarg), *tmp;
504  uint8_t num_custom = 0;
505  uint32_t proto, mod_op, offset, size;
506  int ret;
507  odp_bool_t ret_val = true;
508 
509  odp_lso_profile_param_init(&opts->lso.param);
510 
511  if (tmp_str == NULL) {
512  ODPH_ERR("Error: strdup() failed\n");
513  return false;
514  }
515 
516  /* Format: proto,payload_offset,max_payload_len[,mod_op:offset:size,mod_op:offset...] */
517 
518  tmp = strtok(tmp_str, TOKEN_DELIMITER);
519  if (tmp == NULL) {
520  ODPH_ERR("Error: Unable to parse LSO protocol\n");
521  ret_val = false;
522  goto exit;
523  }
524 
525  proto = strtoul(tmp, NULL, 0);
526 
527  switch (proto) {
528  case 0:
529  opts->lso.param.lso_proto = ODP_LSO_PROTO_IPV4;
530  break;
531  case 1:
532  opts->lso.param.lso_proto = ODP_LSO_PROTO_TCP_IPV4;
533  break;
534  case 2:
535  opts->lso.param.lso_proto = ODP_LSO_PROTO_CUSTOM;
536  break;
537  default:
538  ODPH_ERR("Error: Invalid LSO protocol: %u\n", proto);
539  ret_val = false;
540  goto exit;
541  }
542 
543  tmp = strtok(NULL, TOKEN_DELIMITER);
544  if (tmp == NULL) {
545  ODPH_ERR("Error: Unable to parse LSO payload offset\n");
546  ret_val = false;
547  goto exit;
548  }
549  opts->lso.payload_offset = strtoul(tmp, NULL, 0);
550 
551  tmp = strtok(NULL, TOKEN_DELIMITER);
552  if (tmp == NULL) {
553  ODPH_ERR("Error: Unable to parse LSO max payload length\n");
554  ret_val = false;
555  goto exit;
556  }
557  opts->lso.max_payload_len = strtoul(tmp, NULL, 0);
558 
559  tmp = strtok(NULL, TOKEN_DELIMITER);
560  while (opts->lso.param.lso_proto == ODP_LSO_PROTO_CUSTOM && tmp) {
561  if (num_custom == ODP_LSO_MAX_CUSTOM) {
562  ODPH_ERR("Error: Too many custom LSO operations (max %u)\n",
564  ret_val = false;
565  goto exit;
566  }
567 
568  ret = sscanf(tmp, "%" PRIu32 "" FIELD_DELIMITER
569  "%" PRIu32 "" FIELD_DELIMITER "%" PRIu32 "",
570  &mod_op, &offset, &size);
571 
572  if (ret != 3) {
573  ODPH_ERR("Error: Invalid custom LSO operation, bad field format\n");
574  ret_val = false;
575  goto exit;
576  }
577 
578  switch (mod_op) {
579  case 0:
580  opts->lso.param.custom.field[num_custom].mod_op = ODP_LSO_ADD_SEGMENT_NUM;
581  break;
582  case 1:
583  opts->lso.param.custom.field[num_custom].mod_op = ODP_LSO_ADD_PAYLOAD_LEN;
584  break;
585  case 2:
586  opts->lso.param.custom.field[num_custom].mod_op =
588  break;
589  default:
590  ODPH_ERR("Error: Invalid custom LSO operation: %" PRIu32 "\n", mod_op);
591  ret_val = false;
592  goto exit;
593  }
594 
595  opts->lso.param.custom.field[num_custom].offset = offset;
596 
597  if (size != 1 && size != 2 && size != 4 && size != 8) {
598  ODPH_ERR("Error: Invalid custom field size: %" PRIu32 "\n", size);
599  ret_val = false;
600  goto exit;
601  }
602  opts->lso.param.custom.field[num_custom].size = size;
603 
604  num_custom++;
605  tmp = strtok(NULL, TOKEN_DELIMITER);
606  }
607 
608  if (opts->lso.param.lso_proto == ODP_LSO_PROTO_CUSTOM && num_custom == 0) {
609  ODPH_ERR("Error: Custom LSO protocol requires at least one custom field\n");
610  ret_val = false;
611  goto exit;
612  }
613 
614  opts->lso.param.custom.num_custom = num_custom;
615  opts->lso.enabled = true;
616 
617 exit:
618  free(tmp_str);
619 
620  return ret_val;
621 }
622 
623 static int init_bins(test_global_t *global)
624 {
625  uint32_t i, bin_size;
626  test_options_t *test_options = &global->test_options;
627  uint32_t num_bins = test_options->rand_pkt_len_bins;
628  uint32_t len_min = test_options->rand_pkt_len_min;
629  uint32_t len_max = test_options->rand_pkt_len_max;
630  uint32_t num_bytes = len_max - len_min + 1;
631 
632  if (len_max <= len_min) {
633  ODPH_ERR("Error: Bad max packet length\n");
634  return -1;
635  }
636 
637  if (num_bins == 0)
638  num_bins = num_bytes;
639 
640  if (num_bins == 1 || num_bins > MAX_BINS || num_bins > num_bytes) {
641  ODPH_ERR("Error: Bad number of packet length bins: %u\n", num_bins);
642  return -1;
643  }
644 
645  bin_size = (len_max - len_min + 1) / (num_bins - 1);
646 
647  /* Min length is the first bin */
648  for (i = 0; i < num_bins - 1; i++)
649  global->len_bin[i] = len_min + (i * bin_size);
650 
651  /* Max length is the last bin */
652  global->len_bin[i] = len_max;
653  global->num_bins = num_bins;
654 
655  return 0;
656 }
657 
658 static int parse_options(int argc, char *argv[], test_global_t *global)
659 {
660  int opt, i, len, str_len, port;
661  unsigned long int count;
662  uint32_t min_packets, num_tx_pkt, num_tx_alloc, pkt_len, req_len, val, bins;
663  char *name, *str, *end;
664  test_options_t *test_options = &global->test_options;
665  int ret = 0;
666  uint8_t default_eth_dst[6] = {0x02, 0x00, 0x00, 0xa0, 0xb0, 0xc0};
667 
668  static const struct option longopts[] = {
669  {"interface", required_argument, NULL, 'i'},
670  {"eth_dst", required_argument, NULL, 'e'},
671  {"num_rx", required_argument, NULL, 'r'},
672  {"num_tx", required_argument, NULL, 't'},
673  {"lso", required_argument, NULL, 'T'},
674  {"num_pkt", required_argument, NULL, 'n'},
675  {"proto", required_argument, NULL, 'N'},
676  {"len", required_argument, NULL, 'l'},
677  {"len_range", required_argument, NULL, 'L'},
678  {"direct_rx", required_argument, NULL, 'D'},
679  {"tx_mode", required_argument, NULL, 'm'},
680  {"burst_size", required_argument, NULL, 'b'},
681  {"bursts", required_argument, NULL, 'x'},
682  {"gap", required_argument, NULL, 'g'},
683  {"vlan", required_argument, NULL, 'v'},
684  {"ipv4_src", required_argument, NULL, 's'},
685  {"ipv4_dst", required_argument, NULL, 'd'},
686  {"src_port", required_argument, NULL, 'o'},
687  {"dst_port", required_argument, NULL, 'p'},
688  {"promisc_mode", no_argument, NULL, 'P'},
689  {"latency", no_argument, NULL, 'a'},
690  {"c_mode", required_argument, NULL, 'c'},
691  {"no_udp_checksum", no_argument, NULL, 'C'},
692  {"checksum_offload", no_argument, NULL, 'X'},
693  {"no_payload_fill", no_argument, NULL, 'A'},
694  {"mtu", required_argument, NULL, 'M'},
695  {"quit", required_argument, NULL, 'q'},
696  {"wait", required_argument, NULL, 'w'},
697  {"wait_start", required_argument, NULL, 'W'},
698  {"update_stat", required_argument, NULL, 'u'},
699  {"custom_l3", required_argument, NULL, 'U'},
700  {"help", no_argument, NULL, 'h'},
701  {NULL, 0, NULL, 0}
702  };
703 
704  static const char *shortopts = "+i:e:r:t:T:n:N:l:L:D:m:M:b:x:g:v:s:d:o:"
705  "p:c:CXAq:u:w:W:PaU:h";
706 
707  test_options->num_pktio = 0;
708  test_options->num_rx = 1;
709  test_options->num_tx = 1;
710  test_options->num_pkt = 1000;
711  test_options->pkt_len = 512;
712  test_options->use_rand_pkt_len = 0;
713  test_options->direct_rx = 0;
714  test_options->tx_mode = TX_MODE_REF;
715  test_options->burst_size = 8;
716  test_options->bursts = 1;
717  test_options->gap_nsec = 1000000;
718  test_options->num_vlan = 0;
719  test_options->promisc_mode = 0;
720  test_options->calc_latency = 0;
721  test_options->calc_cs = 1;
722  test_options->cs_offload = 0;
723  test_options->fill_pl = 1;
724  odph_strcpy(test_options->ipv4_src_s, "192.168.0.1",
725  sizeof(test_options->ipv4_src_s));
726  odph_strcpy(test_options->ipv4_dst_s, "192.168.0.2",
727  sizeof(test_options->ipv4_dst_s));
728  if (odph_ipv4_addr_parse(&test_options->ipv4_src, test_options->ipv4_src_s)) {
729  ODPH_ERR("Address parse failed\n");
730  return -1;
731  }
732  if (odph_ipv4_addr_parse(&test_options->ipv4_dst, test_options->ipv4_dst_s)) {
733  ODPH_ERR("Address parse failed\n");
734  return -1;
735  }
736  test_options->src_port = 10000;
737  test_options->dst_port = 20000;
738  test_options->c_mode.src_port = 0;
739  test_options->c_mode.dst_port = 0;
740  test_options->quit = 0;
741  test_options->update_msec = 0;
742  test_options->wait_sec = 0;
743  test_options->wait_start_sec = 0;
744  test_options->mtu = 0;
745  test_options->l4_proto = L4_PROTO_UDP;
746  test_options->lso.enabled = false;
747 
748  for (i = 0; i < MAX_PKTIOS; i++) {
749  memcpy(global->pktio[i].eth_dst.addr, default_eth_dst, 6);
750  global->pktio[i].eth_dst.addr[5] += i;
751  }
752 
753  while (1) {
754  opt = getopt_long(argc, argv, shortopts, longopts, NULL);
755 
756  if (opt == -1)
757  break;
758 
759  switch (opt) {
760  case 'i':
761  i = 0;
762  str = optarg;
763  str_len = strlen(str);
764 
765  while (str_len > 0) {
766  len = strcspn(str, ",");
767  str_len -= len + 1;
768 
769  if (i == MAX_PKTIOS) {
770  ODPH_ERR("Error: Too many interfaces\n");
771  ret = -1;
772  break;
773  }
774 
775  if (len > MAX_PKTIO_NAME) {
776  ODPH_ERR("Error: Too long interface name %s\n", str);
777  ret = -1;
778  break;
779  }
780 
781  name = test_options->pktio_name[i];
782  memcpy(name, str, len);
783  str += len + 1;
784  i++;
785  }
786 
787  test_options->num_pktio = i;
788 
789  break;
790  case 'e':
791  i = 0;
792  str = optarg;
793  str_len = strlen(str);
794 
795  while (str_len > 0) {
796  odph_ethaddr_t *dst = &global->pktio[i].eth_dst;
797 
798  len = strcspn(str, ",");
799  str_len -= len + 1;
800 
801  if (i == MAX_PKTIOS) {
802  ODPH_ERR("Error: Too many MAC addresses\n");
803  ret = -1;
804  break;
805  }
806 
807  if (odph_eth_addr_parse(dst, str)) {
808  ODPH_ERR("Error: Bad MAC address: %s\n", str);
809  ret = -1;
810  break;
811  }
812 
813  str += len + 1;
814  i++;
815  }
816  break;
817  case 'o':
818  port = atoi(optarg);
819  if (port < 0 || port > UINT16_MAX) {
820  ODPH_ERR("Error: Bad source port: %d\n", port);
821  ret = -1;
822  break;
823  }
824  test_options->src_port = port;
825  break;
826  case 'p':
827  port = atoi(optarg);
828  if (port < 0 || port > UINT16_MAX) {
829  ODPH_ERR("Error: Bad destination port: %d\n", port);
830  ret = -1;
831  break;
832  }
833  test_options->dst_port = port;
834  break;
835  case 'P':
836  test_options->promisc_mode = 1;
837  break;
838  case 'a':
839  test_options->calc_latency = 1;
840  break;
841  case 'r':
842  test_options->num_rx = atoi(optarg);
843  break;
844  case 't':
845  test_options->num_tx = atoi(optarg);
846  break;
847  case 'T':
848  if (!parse_lso_fields(optarg, test_options))
849  ret = -1;
850  break;
851  case 'n':
852  test_options->num_pkt = atoi(optarg);
853  break;
854  case 'N':
855  test_options->l4_proto = atoi(optarg);
856  break;
857  case 'l':
858  test_options->pkt_len = atoi(optarg);
859  break;
860  case 'L':
861  pkt_len = strtoul(optarg, &end, 0);
862  test_options->rand_pkt_len_min = pkt_len;
863  end++;
864  pkt_len = strtoul(end, &str, 0);
865  test_options->rand_pkt_len_max = pkt_len;
866  str++;
867  val = strtoul(str, NULL, 0);
868  test_options->rand_pkt_len_bins = val;
869  test_options->use_rand_pkt_len = 1;
870  break;
871  case 'D':
872  test_options->direct_rx = atoi(optarg);
873  break;
874  case 'm':
875  test_options->tx_mode = atoi(optarg);
876  break;
877  case 'M':
878  test_options->mtu = atoi(optarg);
879  break;
880  case 'b':
881  test_options->burst_size = atoi(optarg);
882  break;
883  case 'x':
884  test_options->bursts = atoi(optarg);
885  break;
886  case 'g':
887  test_options->gap_nsec = atoll(optarg);
888  break;
889  case 'v':
890  test_options->num_vlan = parse_vlan(optarg, global);
891  if (test_options->num_vlan == 0) {
892  ODPH_ERR("Error: Did not find any VLANs\n");
893  ret = -1;
894  }
895  break;
896  case 's':
897  if (odph_ipv4_addr_parse(&test_options->ipv4_src,
898  optarg)) {
899  ODPH_ERR("Error: Bad IPv4 source address: %s\n", optarg);
900  ret = -1;
901  }
902  odph_strcpy(test_options->ipv4_src_s, optarg,
903  sizeof(test_options->ipv4_src_s));
904  break;
905  case 'd':
906  if (odph_ipv4_addr_parse(&test_options->ipv4_dst,
907  optarg)) {
908  ODPH_ERR("Error: Bad IPv4 destination address: %s\n", optarg);
909  ret = -1;
910  }
911  odph_strcpy(test_options->ipv4_dst_s, optarg,
912  sizeof(test_options->ipv4_dst_s));
913  break;
914  case 'c':
915  count = strtoul(optarg, &end, 0);
916  test_options->c_mode.src_port = count;
917 
918  end++;
919  count = strtoul(end, NULL, 0);
920  test_options->c_mode.dst_port = count;
921  break;
922  case 'C':
923  test_options->calc_cs = 0;
924  break;
925  case 'X':
926  test_options->cs_offload = 1;
927  break;
928  case 'A':
929  test_options->fill_pl = 0;
930  break;
931  case 'q':
932  test_options->quit = atoll(optarg);
933  break;
934  case 'u':
935  test_options->update_msec = atoll(optarg);
936  break;
937  case 'w':
938  test_options->wait_sec = atoi(optarg);
939  break;
940  case 'W':
941  test_options->wait_start_sec = atoi(optarg);
942  break;
943  case 'U':
944  if (!parse_custom_fields(optarg, test_options))
945  ret = -1;
946 
947  break;
948  case 'h':
949  /* fall through */
950  default:
951  print_usage();
952  ret = -1;
953  break;
954  }
955  }
956 
957  if (ret)
958  return -1;
959 
960  if (test_options->num_pktio == 0) {
961  ODPH_ERR("Error: At least one packet IO interface is needed.\n");
962  ODPH_ERR(" Use -i <name> to specify interfaces.\n");
963  return -1;
964  }
965 
966  if (test_options->num_rx < 1 && test_options->num_tx < 1) {
967  ODPH_ERR("Error: At least one rx or tx thread needed.\n");
968  return -1;
969  }
970 
971  test_options->num_cpu = test_options->num_rx + test_options->num_tx;
972 
973  if (test_options->num_cpu > MAX_WORKERS) {
974  ODPH_ERR("Error: Too many worker threads\n");
975  return -1;
976  }
977 
978  num_tx_pkt = test_options->burst_size * test_options->bursts;
979  global->num_tx_pkt = num_tx_pkt;
980 
981  if (num_tx_pkt == 0) {
982  ODPH_ERR("Error: Bad number of tx packets: %u\n", num_tx_pkt);
983  return -1;
984  }
985 
986  if (test_options->use_rand_pkt_len) {
987  if (init_bins(global))
988  return -1;
989  }
990 
991  bins = global->num_bins ? global->num_bins : 1;
992  num_tx_alloc = num_tx_pkt * bins;
993  if (num_tx_alloc > MAX_ALLOC_PACKETS) {
994  ODPH_ERR("Error: Too many tx packets: %u\n", num_tx_alloc);
995  return -1;
996  }
997 
998  /* Pool needs to have enough packets for all TX side pre-allocated packets and
999  * a burst per thread (for packet copies). RX side needs one burst per thread per pktio. */
1000  min_packets = test_options->num_pktio * test_options->num_tx * num_tx_alloc;
1001  min_packets += test_options->num_tx * test_options->burst_size;
1002  min_packets += test_options->num_pktio * test_options->num_rx * test_options->burst_size;
1003 
1004  if (test_options->num_pkt < min_packets) {
1005  ODPH_ERR("Error: Pool needs to have at least %u packets\n", min_packets);
1006  return -1;
1007  }
1008 
1009  if (test_options->calc_latency && test_options->tx_mode == TX_MODE_REF) {
1010  ODPH_ERR("Error: Latency test is not supported with packet references (--tx_mode 1)\n");
1011  return -1;
1012  }
1013  if (test_options->calc_latency && (test_options->num_rx < 1 || test_options->num_tx < 1)) {
1014  ODPH_ERR("Error: Latency test requires both rx and tx threads\n");
1015  return -1;
1016  }
1017 
1018  if (test_options->gap_nsec) {
1019  double gap_hz = 1000000000.0 / test_options->gap_nsec;
1020 
1021  if (gap_hz > (double)odp_time_local_res()) {
1022  ODPH_ERR("\nWARNING: Burst gap exceeds time counter resolution "
1023  "%" PRIu64 "\n\n", odp_time_local_res());
1024  }
1025  }
1026 
1027  if (global->num_bins) {
1028  if (num_tx_pkt > global->num_bins && num_tx_pkt % global->num_bins)
1029  ODPH_ERR("\nWARNING: Transmit packet count is not evenly divisible into packet length bins.\n\n");
1030 
1031  if (num_tx_pkt < global->num_bins)
1032  ODPH_ERR("\nWARNING: Not enough packets for every packet length bin.\n\n");
1033  }
1034 
1035  if (test_options->c_mode.dst_port && num_tx_pkt % test_options->c_mode.dst_port)
1036  ODPH_ERR("\nWARNING: Transmit packet count is not evenly divisible by destination port count.\n\n");
1037 
1038  if (test_options->c_mode.src_port && num_tx_pkt % test_options->c_mode.src_port)
1039  ODPH_ERR("\nWARNING: Transmit packet count is not evenly divisible by source port count.\n\n");
1040 
1041  if (test_options->l4_proto != L4_PROTO_TCP && test_options->l4_proto != L4_PROTO_UDP &&
1042  test_options->l4_proto != L4_PROTO_NONE) {
1043  ODPH_ERR("Error: Invalid L4 protocol: %" PRIu8 "\n", test_options->l4_proto);
1044  return -1;
1045  }
1046  if (test_options->l4_proto == L4_PROTO_TCP && test_options->tx_mode != TX_MODE_COPY) {
1047  ODPH_ERR("Error: TCP protocol supported only with copy transmit mode\n");
1048  return -1;
1049  }
1050 
1051  test_options->eth_type = test_options->num_custom_l3 ?
1052  test_options->custom_l3.eth_type : ODPH_ETHTYPE_IPV4;
1053  test_options->l3_len = test_options->num_custom_l3 ?
1054  test_options->custom_l3.tot_len : ODPH_IPV4HDR_LEN;
1055  test_options->hdr_len = ODPH_ETHHDR_LEN + (test_options->num_vlan * ODPH_VLANHDR_LEN) +
1056  test_options->l3_len;
1057 
1058  if (test_options->l4_proto != L4_PROTO_NONE)
1059  test_options->hdr_len += test_options->l4_proto == L4_PROTO_UDP ?
1060  ODPH_UDPHDR_LEN : ODPH_TCPHDR_LEN;
1061 
1062  pkt_len = test_options->use_rand_pkt_len ?
1063  test_options->rand_pkt_len_min : test_options->pkt_len;
1064 
1065  req_len = test_options->hdr_len;
1066  if (test_options->calc_latency)
1067  req_len += sizeof(ts_data_t);
1068 
1069  if (test_options->num_custom_l3 && test_options->cs_offload) {
1070  ODPH_ERR("Error: Checksum offloading supported only with IP\n");
1071  return -1;
1072  }
1073 
1074  /* Keep '--no_udp_checksum' only if UDP is the L4 protocol */
1075  if (test_options->l4_proto != L4_PROTO_UDP)
1076  test_options->calc_cs = 1;
1077 
1078  if (test_options->cs_offload)
1079  test_options->calc_cs = 0;
1080 
1081  if (req_len > pkt_len) {
1082  ODPH_ERR("Error: Headers do not fit into packet length of %" PRIu32 " bytes "
1083  "(min %" PRIu32 " bytes required)\n", pkt_len, req_len);
1084  return -1;
1085  }
1086 
1087  if (test_options->lso.enabled) {
1088  if (test_options->tx_mode != TX_MODE_COPY) {
1089  ODPH_ERR("Error: LSO supported only with copy transmit mode\n");
1090  return -1;
1091  }
1092 
1093  if (test_options->num_custom_l3 &&
1094  test_options->lso.param.lso_proto != ODP_LSO_PROTO_CUSTOM) {
1095  ODPH_ERR("Error: LSO and L3 protocol mismatch\n");
1096  return -1;
1097  }
1098 
1099  if (test_options->l4_proto != L4_PROTO_TCP &&
1100  test_options->lso.param.lso_proto == ODP_LSO_PROTO_TCP_IPV4) {
1101  ODPH_ERR("Error: LSO and L4 protocol mismatch\n");
1102  return -1;
1103  }
1104  }
1105  return 0;
1106 }
1107 
1108 static int set_num_cpu(test_global_t *global)
1109 {
1110  int ret;
1111  test_options_t *test_options = &global->test_options;
1112  int num_cpu = test_options->num_cpu;
1113 
1114  ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
1115 
1116  if (ret != num_cpu) {
1117  int cpu;
1118 
1119  /* Normally we want to use only worker threads */
1120  if (ret > 1) {
1121  ODPH_ERR("Error: Too many workers. Maximum supported %i.\n", ret);
1122  return -1;
1123  }
1124 
1125  /* When number of workers is very limited (e.g. ODP project CI),
1126  * we try to use any CPUs available. */
1127  ret = odp_cpumask_all_available(&global->cpumask);
1128  if (ret < num_cpu) {
1129  ODPH_ERR("Error: Not enough CPUs. Maximum supported %i.\n", ret);
1130  return -1;
1131  }
1132 
1133  /* Remove extra CPUs from the mask */
1134  cpu = odp_cpumask_first(&global->cpumask);
1135  while (ret > num_cpu) {
1136  odp_cpumask_clr(&global->cpumask, cpu);
1137  cpu = odp_cpumask_first(&global->cpumask);
1138  ret--;
1139  }
1140  }
1141 
1142  odp_barrier_init(&global->barrier, num_cpu + 1);
1143 
1144  return 0;
1145 }
1146 
1147 static int check_lso_capa(char *name, odp_pktio_capability_t *pktio_capa,
1148  test_options_t *opt, odp_pool_t pool)
1149 {
1150  odp_packet_t pkt;
1151  uint32_t max_segments;
1152  uint32_t pkt_payload;
1153  const uint32_t max_pkt_len = opt->use_rand_pkt_len ?
1154  opt->rand_pkt_len_max : opt->pkt_len;
1155 
1156  if (pktio_capa->lso.max_profiles < opt->num_pktio ||
1157  pktio_capa->lso.max_profiles_per_pktio == 0) {
1158  ODPH_ERR("Error (%s): Not enough LSO profiles (max %" PRIu32 ")\n", name,
1159  ODPH_MIN(pktio_capa->lso.max_profiles,
1160  pktio_capa->lso.max_profiles_per_pktio));
1161  return -1;
1162  }
1163 
1164  if (opt->lso.param.lso_proto == ODP_LSO_PROTO_TCP_IPV4 && !pktio_capa->lso.proto.tcp_ipv4) {
1165  ODPH_ERR("Error (%s): ODP_LSO_PROTO_TCP_IPV4 not supported\n", name);
1166  return -1;
1167  }
1168  if (opt->lso.param.lso_proto == ODP_LSO_PROTO_IPV4 && !pktio_capa->lso.proto.ipv4) {
1169  ODPH_ERR("Error (%s): ODP_LSO_PROTO_IPV4 not supported\n", name);
1170  return -1;
1171  }
1172  if (opt->lso.param.lso_proto == ODP_LSO_PROTO_CUSTOM && !pktio_capa->lso.proto.custom) {
1173  ODPH_ERR("Error (%s): ODP_LSO_PROTO_CUSTOM not supported\n", name);
1174  return -1;
1175  }
1176 
1177  /* Check number of segments in a max length packet */
1178  pkt = odp_packet_alloc(pool, max_pkt_len);
1179  if (pkt == ODP_PACKET_INVALID) {
1180  ODPH_ERR("Error (%s): Allocating test packet failed\n", name);
1181  return -1;
1182  }
1183 
1184  max_segments = odp_packet_num_segs(pkt);
1185  odp_packet_free(pkt);
1186 
1187  if (max_segments > pktio_capa->lso.max_packet_segments) {
1188  ODPH_ERR("Error (%s): Max LSO packet segments: %" PRIu32 "\n", name,
1189  pktio_capa->lso.max_packet_segments);
1190  return -1;
1191  }
1192 
1193  /* Check max number of LSO segments */
1194  pkt_payload = max_pkt_len - opt->lso.payload_offset;
1195  max_segments = (pkt_payload + opt->lso.max_payload_len - 1) /
1196  opt->lso.max_payload_len;
1197  if (max_segments > pktio_capa->lso.max_segments) {
1198  ODPH_ERR("Error (%s): Max LSO segments: %" PRIu32 "\n", name,
1199  pktio_capa->lso.max_segments);
1200  return -1;
1201  }
1202 
1203  if (opt->lso.max_payload_len > pktio_capa->lso.max_payload_len) {
1204  ODPH_ERR("Error (%s): Max LSO payload len: %" PRIu32 "\n", name,
1205  pktio_capa->lso.max_payload_len);
1206  return -1;
1207  }
1208 
1209  if (opt->lso.payload_offset > pktio_capa->lso.max_payload_offset) {
1210  ODPH_ERR("Error (%s): Max LSO payload offset: %" PRIu32 "\n", name,
1211  pktio_capa->lso.max_payload_len);
1212  return -1;
1213  }
1214 
1215  if (opt->lso.param.lso_proto == ODP_LSO_PROTO_CUSTOM) {
1216  if (opt->lso.param.custom.num_custom > pktio_capa->lso.max_num_custom) {
1217  ODPH_ERR("Error (%s): Max LSO custom fields: %" PRIu32 "\n", name,
1218  pktio_capa->lso.max_num_custom);
1219  return -1;
1220  }
1221 
1222  for (uint8_t i = 0; i < opt->lso.param.custom.num_custom; i++) {
1223  if (opt->lso.param.custom.field[i].mod_op == ODP_LSO_ADD_SEGMENT_NUM &&
1224  !pktio_capa->lso.mod_op.add_segment_num) {
1225  ODPH_ERR("Error (%s): ODP_LSO_ADD_SEGMENT_NUM not supported\n",
1226  name);
1227  return -1;
1228  }
1229  if (opt->lso.param.custom.field[i].mod_op == ODP_LSO_ADD_PAYLOAD_LEN &&
1230  !pktio_capa->lso.mod_op.add_payload_len) {
1231  ODPH_ERR("Error (%s): ODP_LSO_ADD_PAYLOAD_LEN not supported\n",
1232  name);
1233  return -1;
1234  }
1235  if (opt->lso.param.custom.field[i].mod_op == ODP_LSO_ADD_PAYLOAD_OFFSET &&
1236  !pktio_capa->lso.mod_op.add_payload_offset) {
1237  ODPH_ERR("Error (%s): ODP_LSO_ADD_PAYLOAD_OFFSET not supported\n",
1238  name);
1239  return -1;
1240  }
1241  }
1242  }
1243 
1244  return 0;
1245 }
1246 
1247 static int open_pktios(test_global_t *global)
1248 {
1249  odp_pool_capability_t pool_capa;
1250  odp_pktio_capability_t pktio_capa;
1251  odp_pool_param_t pool_param;
1252  odp_pool_t pool;
1253  odp_pktio_param_t pktio_param;
1254  odp_pktio_t pktio;
1255  odp_pktio_config_t pktio_config;
1256  odp_pktin_queue_param_t pktin_param;
1257  odp_pktout_queue_param_t pktout_param;
1258  char *name;
1259  uint32_t i, seg_len;
1260  int j, pktio_idx;
1261  test_options_t *test_options = &global->test_options;
1262  int num_rx = test_options->num_rx;
1263  int num_tx = test_options->num_tx;
1264  uint32_t num_pktio = test_options->num_pktio;
1265  uint32_t num_pkt = test_options->num_pkt;
1266  uint32_t pkt_len = test_options->use_rand_pkt_len ?
1267  test_options->rand_pkt_len_max : test_options->pkt_len;
1268 
1269  printf("\nODP packet generator\n");
1270  printf(" quit test after: %" PRIu64 " rounds\n",
1271  test_options->quit);
1272  printf(" num rx threads: %i\n", num_rx);
1273  printf(" num tx threads: %i\n", num_tx);
1274  printf(" num packets: %u\n", num_pkt);
1275  if (test_options->use_rand_pkt_len)
1276  printf(" packet length: %u-%u bytes, %u bins\n",
1277  test_options->rand_pkt_len_min,
1278  test_options->rand_pkt_len_max,
1279  test_options->rand_pkt_len_bins);
1280  else
1281  printf(" packet length: %u bytes\n", pkt_len);
1282  printf(" MTU: ");
1283  if (test_options->mtu)
1284  printf("%u bytes\n", test_options->mtu);
1285  else
1286  printf("interface default\n");
1287  printf(" packet input mode: %s\n", test_options->direct_rx ? "direct" : "scheduler");
1288  printf(" promisc mode: %s\n", test_options->promisc_mode ? "enabled" : "disabled");
1289  printf(" transmit mode: %i\n", test_options->tx_mode);
1290  printf(" measure latency: %s\n", test_options->calc_latency ? "enabled" : "disabled");
1291  printf(" UDP SW checksum: %s\n", test_options->calc_cs ? "enabled" : "disabled");
1292  printf(" Checksum offload: %s\n", test_options->cs_offload ? "enabled" : "disabled");
1293  printf(" payload filling: %s\n", test_options->fill_pl ? "enabled" : "disabled");
1294  printf(" tx burst size: %u\n", test_options->burst_size);
1295  printf(" tx bursts: %u\n", test_options->bursts);
1296  printf(" tx burst gap: %" PRIu64 " nsec\n",
1297  test_options->gap_nsec);
1298 
1299  if (test_options->lso.enabled) {
1300  printf(" LSO protocol: %s\n",
1301  test_options->lso.param.lso_proto == ODP_LSO_PROTO_IPV4 ?
1302  "ODP_LSO_PROTO_IPV4" :
1303  test_options->lso.param.lso_proto == ODP_LSO_PROTO_TCP_IPV4 ?
1304  "ODP_LSO_PROTO_TCP_IPV4" : "ODP_LSO_PROTO_CUSTOM");
1305  printf(" max payload offset: %" PRIu32 " bytes\n",
1306  test_options->lso.payload_offset);
1307  printf(" max payload len: %" PRIu32 " bytes\n",
1308  test_options->lso.max_payload_len);
1309 
1310  for (i = 0; i < test_options->lso.param.custom.num_custom; i++) {
1311  printf(" Custom operation %" PRIu32 ": %s\n", i + 1,
1312  test_options->lso.param.custom.field[i].mod_op ==
1314  "ODP_LSO_ADD_SEGMENT_NUM" :
1315  test_options->lso.param.custom.field[i].mod_op ==
1317  "ODP_LSO_ADD_PAYLOAD_LEN" : "ODP_LSO_ADD_PAYLOAD_OFFSET");
1318  printf(" offset: %" PRIu32 " bytes\n",
1319  test_options->lso.param.custom.field[i].offset);
1320  printf(" size: %" PRIu32 " bytes\n",
1321  test_options->lso.param.custom.field[i].size);
1322  }
1323  }
1324 
1325  printf(" clock resolution: %" PRIu64 " Hz\n", odp_time_local_res());
1326  for (i = 0; i < test_options->num_vlan; i++) {
1327  printf(" VLAN[%i]: %x:%x\n", i,
1328  test_options->vlan[i].tpid, test_options->vlan[i].tci);
1329  }
1330 
1331  printf(" L3 protocol: ");
1332 
1333  if (test_options->num_custom_l3) {
1334  printf("custom\n"
1335  " ether type: %x\n"
1336  " total length: %u\n"
1337  " fields:\n", test_options->custom_l3.eth_type,
1338  test_options->custom_l3.tot_len);
1339 
1340  for (i = 0; i < test_options->num_custom_l3; i++) {
1341  printf(" name: %s\n"
1342  " length: %u\n"
1343  " value: %" PRIx64 "\n"
1344  " diff: %" PRIi64 "\n\n",
1345  test_options->custom_l3.fields[i].name,
1346  test_options->custom_l3.fields[i].len,
1347  test_options->custom_l3.fields[i].value,
1348  test_options->custom_l3.fields[i].diff);
1349  }
1350  } else {
1351  printf("IPv4\n");
1352  printf(" IPv4 source: %s\n", test_options->ipv4_src_s);
1353  printf(" IPv4 destination: %s\n\n", test_options->ipv4_dst_s);
1354  }
1355 
1356  printf(" L4 protocol: %s\n",
1357  test_options->l4_proto == L4_PROTO_UDP ?
1358  "UDP" : test_options->l4_proto == L4_PROTO_TCP ? "TCP" : "none");
1359  printf(" source port: %u\n", test_options->src_port);
1360  printf(" destination port: %u\n", test_options->dst_port);
1361  printf(" src port count: %u\n", test_options->c_mode.src_port);
1362  printf(" dst port count: %u\n", test_options->c_mode.dst_port);
1363  printf(" num pktio: %u\n", num_pktio);
1364 
1365  printf(" interfaces names: ");
1366  for (i = 0; i < num_pktio; i++) {
1367  if (i > 0)
1368  printf(" ");
1369  printf("%s\n", test_options->pktio_name[i]);
1370  }
1371 
1372  printf(" destination MACs: ");
1373  for (i = 0; i < num_pktio; i++) {
1374  uint8_t *eth_dst = global->pktio[i].eth_dst.addr;
1375 
1376  if (i > 0)
1377  printf(" ");
1378  printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
1379  eth_dst[0], eth_dst[1], eth_dst[2],
1380  eth_dst[3], eth_dst[4], eth_dst[5]);
1381  }
1382  printf("\n");
1383 
1384  global->pool = ODP_POOL_INVALID;
1385 
1386  if (odp_pool_capability(&pool_capa)) {
1387  ODPH_ERR("Error: Pool capability failed.\n");
1388  return -1;
1389  }
1390 
1391  if (pool_capa.pkt.max_num &&
1392  num_pkt > pool_capa.pkt.max_num) {
1393  ODPH_ERR("Error: Too many packets. Max %u supported.\n", pool_capa.pkt.max_num);
1394  return -1;
1395  }
1396 
1397  if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len) {
1398  ODPH_ERR("Error: Too large packets. Max %u supported length.\n",
1399  pool_capa.pkt.max_len);
1400  return -1;
1401  }
1402 
1403  seg_len = test_options->hdr_len;
1404  if (pool_capa.pkt.max_seg_len &&
1405  seg_len > pool_capa.pkt.max_seg_len) {
1406  ODPH_ERR("Error: Max segment length is too small %u\n", pool_capa.pkt.max_seg_len);
1407  return -1;
1408  }
1409 
1410  /* Create pool */
1411  odp_pool_param_init(&pool_param);
1412  pool_param.type = ODP_POOL_PACKET;
1413  pool_param.pkt.num = num_pkt;
1414  pool_param.pkt.len = pkt_len;
1415  pool_param.pkt.seg_len = seg_len;
1416 
1417  pool = odp_pool_create("packet gen pool", &pool_param);
1418 
1419  if (pool == ODP_POOL_INVALID) {
1420  ODPH_ERR("Error: Pool create failed.\n");
1421  return -1;
1422  }
1423 
1424  global->pool = pool;
1425 
1426  odp_pktio_param_init(&pktio_param);
1427 
1428  pktio_param.in_mode = num_rx ? (test_options->direct_rx ?
1431 
1432  pktio_param.out_mode = num_tx ? ODP_PKTOUT_MODE_DIRECT : ODP_PKTOUT_MODE_DISABLED;
1433 
1434  for (i = 0; i < num_pktio; i++) {
1435  global->pktio[i].pktio = ODP_PKTIO_INVALID;
1436  global->pktio[i].lso_profile = ODP_LSO_PROFILE_INVALID;
1437  }
1438 
1439  /* Open and configure interfaces */
1440  for (i = 0; i < num_pktio; i++) {
1441  name = test_options->pktio_name[i];
1442  pktio = odp_pktio_open(name, pool, &pktio_param);
1443 
1444  if (pktio == ODP_PKTIO_INVALID) {
1445  ODPH_ERR("Error (%s): Pktio open failed.\n", name);
1446  return -1;
1447  }
1448 
1449  global->pktio[i].pktio = pktio;
1450 
1451  odp_pktio_print(pktio);
1452 
1453  pktio_idx = odp_pktio_index(pktio);
1454  if (pktio_idx < 0) {
1455  ODPH_ERR("Error (%s): Reading pktio index failed: %i\n", name, pktio_idx);
1456  return -1;
1457  }
1458  global->if_from_pktio_idx[pktio_idx] = i;
1459 
1460  if (odp_pktio_capability(pktio, &pktio_capa)) {
1461  ODPH_ERR("Error (%s): Pktio capability failed.\n", name);
1462  return -1;
1463  }
1464 
1465  if (num_rx > (int)pktio_capa.max_input_queues) {
1466  ODPH_ERR("Error (%s): Too many RX threads. Interface supports max %u input queues.\n",
1467  name, pktio_capa.max_input_queues);
1468  return -1;
1469  }
1470 
1471  if (num_tx > (int)pktio_capa.max_output_queues) {
1472  ODPH_ERR("Error (%s): Too many TX threads. Interface supports max %u output queues.\n",
1473  name, pktio_capa.max_output_queues);
1474  return -1;
1475  }
1476 
1477  if (odp_pktio_mac_addr(pktio,
1478  &global->pktio[i].eth_src.addr,
1479  ODPH_ETHADDR_LEN) != ODPH_ETHADDR_LEN) {
1480  ODPH_ERR("Error (%s): MAC address read failed.\n", name);
1481  return -1;
1482  }
1483 
1484  if (test_options->mtu) {
1485  uint32_t maxlen_input = pktio_capa.maxlen.max_input ? test_options->mtu : 0;
1486  uint32_t maxlen_output = pktio_capa.maxlen.max_output ?
1487  test_options->mtu : 0;
1488 
1489  if (!pktio_capa.set_op.op.maxlen) {
1490  ODPH_ERR("Error (%s): modifying interface MTU not supported.\n",
1491  name);
1492  return -1;
1493  }
1494 
1495  if (maxlen_input &&
1496  (maxlen_input < pktio_capa.maxlen.min_input ||
1497  maxlen_input > pktio_capa.maxlen.max_input)) {
1498  ODPH_ERR("Error (%s): unsupported MTU value %" PRIu32 " "
1499  "(min %" PRIu32 ", max %" PRIu32 ")\n", name, maxlen_input,
1500  pktio_capa.maxlen.min_input, pktio_capa.maxlen.max_input);
1501  return -1;
1502  }
1503  if (maxlen_output &&
1504  (maxlen_output < pktio_capa.maxlen.min_output ||
1505  maxlen_output > pktio_capa.maxlen.max_output)) {
1506  ODPH_ERR("Error (%s): unsupported MTU value %" PRIu32 " "
1507  "(min %" PRIu32 ", max %" PRIu32 ")\n", name,
1508  maxlen_output, pktio_capa.maxlen.min_output,
1509  pktio_capa.maxlen.max_output);
1510  return -1;
1511  }
1512 
1513  if (odp_pktio_maxlen_set(pktio, maxlen_input, maxlen_output)) {
1514  ODPH_ERR("Error (%s): setting MTU failed\n", name);
1515  return -1;
1516  }
1517  }
1518 
1519  if (test_options->tx_mode == TX_MODE_DF && pktio_capa.free_ctrl.dont_free == 0) {
1520  ODPH_ERR("Error (%s): Don't free mode not supported\n", name);
1521  return -1;
1522  }
1523 
1524  odp_pktio_config_init(&pktio_config);
1525 
1526  if (test_options->cs_offload) {
1527  if (test_options->l4_proto == L4_PROTO_UDP) {
1528  if (pktio_capa.config.pktin.bit.udp_chksum &&
1529  pktio_capa.config.pktin.bit.drop_udp_err) {
1530  pktio_config.pktin.bit.udp_chksum = 1;
1531  pktio_config.pktin.bit.drop_udp_err = 1;
1532  } else {
1533  ODPH_ERR("Warning (%s): UDP checksum offload at RX not "
1534  "properly supported, leaving it disabled.\n",
1535  name);
1536  }
1537 
1538  if (!pktio_capa.config.pktout.bit.udp_chksum) {
1539  ODPH_ERR("Error (%s): UDP checksum offload at TX not "
1540  "properly supported.\n", name);
1541  return -1;
1542  }
1543 
1544  pktio_config.pktout.bit.udp_chksum_ena = 1;
1545  pktio_config.pktout.bit.udp_chksum = 1;
1546  }
1547 
1548  if (test_options->l4_proto == L4_PROTO_TCP) {
1549  if (pktio_capa.config.pktin.bit.tcp_chksum &&
1550  pktio_capa.config.pktin.bit.drop_tcp_err) {
1551  pktio_config.pktin.bit.tcp_chksum = 1;
1552  pktio_config.pktin.bit.drop_tcp_err = 1;
1553  } else {
1554  ODPH_ERR("Warning (%s): TCP checksum offload at RX not "
1555  "properly supported, leaving it disabled.\n",
1556  name);
1557  }
1558 
1559  if (!pktio_capa.config.pktout.bit.tcp_chksum) {
1560  ODPH_ERR("Error (%s): TCP checksum offload at TX not "
1561  "properly supported.\n", name);
1562  return -1;
1563  }
1564 
1565  pktio_config.pktout.bit.tcp_chksum_ena = 1;
1566  pktio_config.pktout.bit.tcp_chksum = 1;
1567  }
1568  }
1569 
1570  pktio_config.parser.layer = ODP_PROTO_LAYER_ALL;
1571 
1572  if (test_options->lso.enabled) {
1573  if (check_lso_capa(name, &pktio_capa, test_options, pool))
1574  return -1;
1575 
1576  pktio_config.enable_lso = true;
1577  }
1578 
1579  if (odp_pktio_config(pktio, &pktio_config)) {
1580  ODPH_ERR("Error (%s): Pktio config failed.\n", name);
1581  return -1;
1582  }
1583 
1584  if (test_options->lso.enabled) {
1585  const odp_lso_profile_param_t *param = &test_options->lso.param;
1586 
1587  global->pktio[i].lso_profile = odp_lso_profile_create(pktio, param);
1588  if (global->pktio[i].lso_profile == ODP_LSO_PROFILE_INVALID) {
1589  ODPH_ERR("Error (%s): LSO profile create failed.\n", name);
1590  return -1;
1591  }
1592  }
1593 
1594  if (test_options->promisc_mode && odp_pktio_promisc_mode(pktio) != 1) {
1595  if (!pktio_capa.set_op.op.promisc_mode) {
1596  ODPH_ERR("Error (%s): promisc mode set not supported\n", name);
1597  return -1;
1598  }
1599 
1600  if (odp_pktio_promisc_mode_set(pktio, true)) {
1601  ODPH_ERR("Error (%s): promisc mode enable failed\n", name);
1602  return -1;
1603  }
1604  }
1605 
1606  odp_pktin_queue_param_init(&pktin_param);
1607 
1608  if (test_options->direct_rx) {
1609  pktin_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
1610  } else {
1614  }
1615 
1616  pktin_param.num_queues = num_rx;
1617 
1618  if (num_rx > 1) {
1619  pktin_param.hash_enable = 1;
1620  pktin_param.hash_proto.proto.ipv4_udp = 1;
1621  }
1622 
1623  if (odp_pktin_queue_config(pktio, &pktin_param)) {
1624  ODPH_ERR("Error (%s): Pktin config failed.\n", name);
1625  return -1;
1626  }
1627 
1628  odp_pktout_queue_param_init(&pktout_param);
1629  pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
1630  pktout_param.num_queues = num_tx;
1631 
1632  if (odp_pktout_queue_config(pktio, &pktout_param)) {
1633  ODPH_ERR("Error (%s): Pktout config failed.\n", name);
1634  return -1;
1635  }
1636 
1637  if (num_tx > 0) {
1638  odp_pktout_queue_t pktout[MAX_THREADS];
1639 
1640  if (odp_pktout_queue(pktio, pktout, num_tx) != num_tx) {
1641  ODPH_ERR("Error (%s): Pktout queue request failed.\n", name);
1642  return -1;
1643  }
1644 
1645  for (j = 0; j < num_tx; j++)
1646  global->pktio[i].pktout[j] = pktout[j];
1647  }
1648 
1649  if (num_rx > 0 && test_options->direct_rx) {
1650  odp_pktin_queue_t pktin[MAX_THREADS];
1651 
1652  if (odp_pktin_queue(pktio, pktin, num_rx) != num_rx) {
1653  ODPH_ERR("Error (%s): Pktin queue request failed.\n", name);
1654  return -1;
1655  }
1656 
1657  for (j = 0; j < num_rx; j++)
1658  global->pktio[i].pktin[j] = pktin[j];
1659  }
1660  }
1661 
1662  return 0;
1663 }
1664 
1665 static int print_link_info(odp_pktio_t pktio)
1666 {
1667  odp_pktio_link_info_t info;
1668 
1669  if (odp_pktio_link_info(pktio, &info)) {
1670  ODPH_ERR("Error: Pktio link info failed.\n");
1671  return -1;
1672  }
1673 
1674  printf(" autoneg %s\n",
1675  (info.autoneg == ODP_PKTIO_LINK_AUTONEG_ON ? "on" :
1676  (info.autoneg == ODP_PKTIO_LINK_AUTONEG_OFF ? "off" : "unknown")));
1677  printf(" duplex %s\n",
1678  (info.duplex == ODP_PKTIO_LINK_DUPLEX_HALF ? "half" :
1679  (info.duplex == ODP_PKTIO_LINK_DUPLEX_FULL ? "full" : "unknown")));
1680  printf(" media %s\n", info.media);
1681  printf(" pause_rx %s\n",
1682  (info.pause_rx == ODP_PKTIO_LINK_PAUSE_ON ? "on" :
1683  (info.pause_rx == ODP_PKTIO_LINK_PAUSE_OFF ? "off" : "unknown")));
1684  printf(" pause_tx %s\n",
1685  (info.pause_tx == ODP_PKTIO_LINK_PAUSE_ON ? "on" :
1686  (info.pause_tx == ODP_PKTIO_LINK_PAUSE_OFF ? "off" : "unknown")));
1687  printf(" speed(Mbit/s) %" PRIu32 "\n\n", info.speed);
1688 
1689  return 0;
1690 }
1691 
1692 static int start_pktios(test_global_t *global)
1693 {
1694  uint32_t i;
1695  test_options_t *test_options = &global->test_options;
1696  uint32_t num_pktio = test_options->num_pktio;
1697  uint32_t link_wait = 0;
1698 
1699  for (i = 0; i < num_pktio; i++) {
1700  if (odp_pktio_start(global->pktio[i].pktio)) {
1701  ODPH_ERR("Error (%s): Pktio start failed.\n", test_options->pktio_name[i]);
1702 
1703  return -1;
1704  }
1705 
1706  global->pktio[i].started = 1;
1707  }
1708 
1709  /* Wait until all links are up */
1710  for (i = 0; test_options->wait_sec && i < num_pktio; i++) {
1711  while (1) {
1712  odp_pktio_t pktio = global->pktio[i].pktio;
1713 
1715  printf("pktio:%s\n", test_options->pktio_name[i]);
1716  if (print_link_info(pktio)) {
1717  ODPH_ERR("Error (%s): Printing link info failed.\n",
1718  test_options->pktio_name[i]);
1719  return -1;
1720  }
1721  break;
1722  }
1723  link_wait++;
1724  if (link_wait > test_options->wait_sec) {
1725  ODPH_ERR("Error (%s): Pktio link down.\n",
1726  test_options->pktio_name[i]);
1727  return -1;
1728  }
1730  }
1731  }
1732 
1733  if (test_options->wait_start_sec)
1734  odp_time_wait_ns(test_options->wait_start_sec * ODP_TIME_SEC_IN_NS);
1735 
1736  return 0;
1737 }
1738 
1739 static int stop_pktios(test_global_t *global)
1740 {
1741  uint32_t i;
1742  odp_pktio_t pktio;
1743  int ret = 0;
1744  test_options_t *test_options = &global->test_options;
1745  uint32_t num_pktio = test_options->num_pktio;
1746 
1747  for (i = 0; i < num_pktio; i++) {
1748  pktio = global->pktio[i].pktio;
1749 
1750  if (pktio == ODP_PKTIO_INVALID || global->pktio[i].started == 0)
1751  continue;
1752 
1753  if (odp_pktio_stop(pktio)) {
1754  ODPH_ERR("Error (%s): Pktio stop failed.\n", test_options->pktio_name[i]);
1755  ret = -1;
1756  }
1757  }
1758 
1759  return ret;
1760 }
1761 
1762 static int close_pktios(test_global_t *global)
1763 {
1764  uint32_t i;
1765  odp_pktio_t pktio;
1766  test_options_t *test_options = &global->test_options;
1767  uint32_t num_pktio = test_options->num_pktio;
1768  int ret = 0;
1769 
1770  for (i = 0; i < num_pktio; i++) {
1771  pktio = global->pktio[i].pktio;
1772 
1773  if (pktio == ODP_PKTIO_INVALID)
1774  continue;
1775 
1776  if (global->pktio[i].lso_profile != ODP_LSO_PROFILE_INVALID &&
1777  odp_lso_profile_destroy(global->pktio[i].lso_profile)) {
1778  ODPH_ERR("Error (%s): LSO profile destroy failed.\n",
1779  test_options->pktio_name[i]);
1780  ret = -1;
1781  }
1782 
1783  if (odp_pktio_close(pktio)) {
1784  ODPH_ERR("Error (%s): Pktio close failed.\n", test_options->pktio_name[i]);
1785  ret = -1;
1786  }
1787  }
1788 
1789  if (global->pool != ODP_POOL_INVALID &&
1790  odp_pool_destroy(global->pool)) {
1791  ODPH_ERR("Error: Pool destroy failed.\n");
1792  ret = -1;
1793  }
1794 
1795  return ret;
1796 }
1797 
1798 static inline void get_timestamp(odp_packet_t pkt, uint32_t ts_off, rx_lat_data_t *lat_data,
1799  odp_time_t rx_ts)
1800 {
1801  ts_data_t ts_data;
1802  uint64_t nsec;
1803 
1804  if (odp_unlikely(odp_packet_copy_to_mem(pkt, ts_off, sizeof(ts_data), &ts_data) < 0 ||
1805  ts_data.magic != TS_MAGIC))
1806  return;
1807 
1808  nsec = odp_time_diff_ns(rx_ts, ts_data.tx_ts);
1809 
1810  if (nsec < lat_data->min)
1811  lat_data->min = nsec;
1812 
1813  if (nsec > lat_data->max)
1814  lat_data->max = nsec;
1815 
1816  lat_data->nsec += nsec;
1817  lat_data->packets++;
1818 }
1819 
1820 static int rx_thread(void *arg)
1821 {
1822  int i, thr, num;
1823  uint32_t exit_test;
1824  uint64_t bytes;
1825  odp_time_t t1, t2, exit_time;
1826  thread_arg_t *thread_arg = arg;
1827  test_global_t *global = thread_arg->global;
1828  int direct_rx = global->test_options.direct_rx;
1829  int periodic_stat = global->test_options.update_msec ? 1 : 0;
1830  uint64_t rx_timeouts = 0;
1831  uint64_t rx_packets = 0;
1832  uint64_t rx_bytes = 0;
1833  uint64_t nsec = 0;
1834  int ret = 0;
1835  int clock_started = 0;
1836  int exit_timer_started = 0;
1837  int paused = 0;
1838  const int max_num = 32;
1839  int pktin = 0;
1840  int num_pktio = global->test_options.num_pktio;
1841  odp_pktin_queue_t pktin_queue[num_pktio];
1842  odp_packet_t pkt[max_num];
1843  uint32_t ts_off = global->test_options.calc_latency ? global->test_options.hdr_len : 0;
1844  odp_time_t rx_ts = ODP_TIME_NULL;
1845  rx_lat_data_t rx_lat_data = { .nsec = 0, .min = UINT64_MAX, .max = 0, .packets = 0 };
1846 
1847  thr = odp_thread_id();
1848  global->stat[thr].thread_type = RX_THREAD;
1849 
1850  if (direct_rx) {
1851  for (i = 0; i < num_pktio; i++)
1852  pktin_queue[i] = thread_arg->pktin[i];
1853  }
1854 
1855  /* Start all workers at the same time */
1856  odp_barrier_wait(&global->barrier);
1857 
1858  while (1) {
1859  if (direct_rx) {
1860  num = odp_pktin_recv(pktin_queue[pktin], pkt, max_num);
1861 
1862  if (odp_unlikely(num < 0)) {
1863  ODPH_ERR("pktin (%i) recv failed: %i\n", pktin, num);
1864  ret = -1;
1865  num = 0;
1866  break;
1867  }
1868 
1869  pktin++;
1870  if (pktin >= num_pktio)
1871  pktin = 0;
1872  } else {
1873  odp_event_t ev[max_num];
1874 
1875  num = odp_schedule_multi_no_wait(NULL, ev, max_num);
1876 
1877  if (num)
1878  odp_packet_from_event_multi(pkt, ev, num);
1879  }
1880 
1881  if (ts_off && num)
1882  rx_ts = odp_time_global_strict();
1883 
1884  exit_test = odp_atomic_load_u32(&global->exit_test);
1885  if (exit_test) {
1886  /* Wait 1 second for possible in flight packets sent by the tx threads */
1887  if (exit_timer_started == 0) {
1888  exit_time = odp_time_local();
1889  t2 = exit_time;
1890  exit_timer_started = 1;
1891  } else if (odp_time_diff_ns(odp_time_local(), exit_time) >
1893  if (direct_rx == 0 && paused == 0) {
1895  paused = 1;
1896  } else if (num == 0) {
1897  /* Exit main loop after (schedule paused and) no more
1898  * packets received */
1899  break;
1900  }
1901  }
1902  /* Use last received packet as stop time and don't increase rx_timeouts
1903  * counter since tx threads have already been stopped */
1904  if (num)
1905  t2 = odp_time_local();
1906  else
1907  continue;
1908  }
1909 
1910  if (num == 0) {
1911  if (direct_rx == 0)
1912  rx_timeouts++;
1913 
1914  continue;
1915  }
1916 
1917  if (!clock_started) {
1918  t1 = odp_time_local();
1919  clock_started = 1;
1920  }
1921 
1922  bytes = 0;
1923  for (i = 0; i < num; i++) {
1924  bytes += odp_packet_len(pkt[i]);
1925 
1926  if (ts_off)
1927  get_timestamp(pkt[i], ts_off, &rx_lat_data, rx_ts);
1928  }
1929 
1930  rx_packets += num;
1931  rx_bytes += bytes;
1932 
1933  if (odp_unlikely(periodic_stat)) {
1934  /* All packets from the same queue are from the same pktio interface */
1935  int index = odp_packet_input_index(pkt[0]);
1936 
1937  if (index >= 0) {
1938  int if_idx = global->if_from_pktio_idx[index];
1939 
1940  global->stat[thr].pktio[if_idx].rx_packets += num;
1941  }
1942  }
1943 
1944  odp_packet_free_multi(pkt, num);
1945  }
1946 
1947  if (clock_started)
1948  nsec = odp_time_diff_ns(t2, t1);
1949 
1950  /* Update stats*/
1951  global->stat[thr].time_nsec = nsec;
1952  global->stat[thr].rx_timeouts = rx_timeouts;
1953  global->stat[thr].rx_packets = rx_packets;
1954  global->stat[thr].rx_bytes = rx_bytes;
1955  global->stat[thr].rx_lat_nsec = rx_lat_data.nsec;
1956  global->stat[thr].rx_lat_min_nsec = rx_lat_data.min;
1957  global->stat[thr].rx_lat_max_nsec = rx_lat_data.max;
1958  global->stat[thr].rx_lat_packets = rx_lat_data.packets;
1959 
1960  return ret;
1961 }
1962 
1963 static void drain_scheduler(test_global_t *global)
1964 {
1965  odp_event_t ev;
1966  uint64_t wait_time;
1967 
1968  if (!global->test_options.num_rx)
1969  return;
1970 
1971  wait_time = odp_schedule_wait_time(100 * ODP_TIME_MSEC_IN_NS);
1972 
1973  while ((ev = odp_schedule(NULL, wait_time)) != ODP_EVENT_INVALID) {
1974  global->drained++;
1975  odp_event_free(ev);
1976  }
1977 }
1978 
1979 static void drain_direct_input(test_global_t *global)
1980 {
1981  odp_pktin_queue_t pktin;
1982  odp_packet_t pkt;
1983  int i, j;
1984  int num_pktio = global->test_options.num_pktio;
1985  int num_rx = global->test_options.num_rx;
1986 
1987  for (i = 0; i < num_pktio; i++) {
1988  for (j = 0; j < num_rx; j++) {
1989  pktin = global->pktio[i].pktin[j];
1990 
1991  while (odp_pktin_recv(pktin, &pkt, 1) == 1) {
1992  global->drained++;
1993  odp_packet_free(pkt);
1994  }
1995  }
1996  }
1997 }
1998 
1999 static inline uint8_t *copy_field(uint8_t *dst, uint8_t *src, uint32_t len)
2000 {
2001  return (uint8_t *)memcpy(dst, src, len) + len;
2002 }
2003 
2004 static int init_packets(test_global_t *global, int pktio,
2005  odp_packet_t packet[], uint32_t num, uint16_t seq)
2006 {
2007  odp_packet_t pkt;
2008  uint32_t i, j, pkt_len, seg_len, payload_len, l2_len;
2009  void *data;
2010  uint8_t *u8;
2011  odph_ethhdr_t *eth;
2012  odph_ipv4hdr_t *ip;
2013  uint16_t tpid;
2014  test_options_t *test_options = &global->test_options;
2015  const int proto = test_options->l4_proto;
2016  uint32_t num_vlan = test_options->num_vlan;
2017  uint32_t num_custom_l3 = test_options->num_custom_l3;
2018  uint32_t hdr_len = test_options->hdr_len;
2019  uint16_t src_port = test_options->src_port;
2020  uint16_t dst_port = test_options->dst_port;
2021  uint32_t src_cnt = 0;
2022  uint32_t dst_cnt = 0;
2023  uint32_t tcp_seqnum = 0x1234;
2024  uint64_t value;
2025  odph_vlanhdr_t *vlan = NULL; /* Fixes bogus compiler warning */
2026  hdr_field_t *c_hdr = NULL;
2027 
2028  if (num_vlan > MAX_VLANS)
2029  num_vlan = MAX_VLANS;
2030 
2031  for (i = 0; i < num; i++) {
2032  pkt = packet[i];
2033  pkt_len = odp_packet_len(pkt);
2034  seg_len = odp_packet_seg_len(pkt);
2035  data = odp_packet_data(pkt);
2036  payload_len = pkt_len - hdr_len;
2037 
2038  if (seg_len < hdr_len) {
2039  ODPH_ERR("Error: First segment too short %u\n", seg_len);
2040  return -1;
2041  }
2042 
2043  /* Ethernet */
2044  eth = data;
2045  memcpy(eth->dst.addr, global->pktio[pktio].eth_dst.addr, 6);
2046  memcpy(eth->src.addr, global->pktio[pktio].eth_src.addr, 6);
2047  eth->type = odp_cpu_to_be_16(test_options->eth_type);
2048  l2_len = ODPH_ETHHDR_LEN;
2049  odp_packet_has_eth_set(pkt, 1);
2050 
2051  /* VLAN(s) */
2052  if (num_vlan) {
2053  tpid = test_options->vlan[0].tpid;
2054  eth->type = odp_cpu_to_be_16(tpid);
2055 
2056  if (tpid == ETH_TYPE_QINQ)
2058  else
2059  odp_packet_has_vlan_set(pkt, 1);
2060  }
2061 
2062  for (j = 0; j < num_vlan; j++) {
2063  vlan = (odph_vlanhdr_t *)((uint8_t *)data + l2_len);
2064  vlan->tci = odp_cpu_to_be_16(test_options->vlan[j].tci);
2065  if (j < num_vlan - 1) {
2066  tpid = test_options->vlan[j + 1].tpid;
2067  vlan->type = odp_cpu_to_be_16(tpid);
2068  }
2069 
2070  l2_len += ODPH_VLANHDR_LEN;
2071  }
2072 
2073  if (num_vlan)
2074  vlan->type = odp_cpu_to_be_16(test_options->eth_type);
2075 
2076  odp_packet_l3_offset_set(pkt, l2_len);
2077 
2078  /* L3 */
2079  if (num_custom_l3) {
2080  /* Custom */
2081  u8 = (uint8_t *)data + l2_len;
2082 
2083  for (j = 0; j < num_custom_l3; j++) {
2084  c_hdr = &test_options->custom_l3.fields[j];
2085  value = bswap(c_hdr->value, c_hdr->len);
2086  u8 = copy_field(u8, (uint8_t *)&value, c_hdr->len);
2087  c_hdr->value += c_hdr->diff;
2088  }
2089  } else {
2090  /* IPv4 */
2091  ip = (odph_ipv4hdr_t *)((uint8_t *)data + l2_len);
2092  memset(ip, 0, ODPH_IPV4HDR_LEN);
2093  ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
2094  ip->tot_len = odp_cpu_to_be_16(pkt_len - l2_len);
2095  ip->id = odp_cpu_to_be_16(seq + i);
2096  ip->ttl = 64;
2097  /* Use experimental protocol number if L4 proto is none. */
2098  ip->proto = proto == L4_PROTO_UDP ?
2099  ODPH_IPPROTO_UDP : proto == L4_PROTO_TCP ?
2100  ODPH_IPPROTO_TCP : 0xFE;
2101  ip->src_addr = odp_cpu_to_be_32(test_options->ipv4_src);
2102  ip->dst_addr = odp_cpu_to_be_32(test_options->ipv4_dst);
2103  ip->chksum = ~odp_chksum_ones_comp16(ip, ODPH_IPV4HDR_LEN);
2104  odp_packet_has_ipv4_set(pkt, 1);
2105 
2106  u8 = ((uint8_t *)data + l2_len + ODPH_IPV4HDR_LEN);
2107  }
2108 
2109  odp_packet_l4_offset_set(pkt, l2_len + test_options->l3_len);
2110 
2111  if (proto == L4_PROTO_TCP) {
2112  odph_tcphdr_t *tcp = (odph_tcphdr_t *)u8;
2113 
2114  memset(tcp, 0, ODPH_TCPHDR_LEN);
2115  tcp->src_port = odp_cpu_to_be_16(src_port);
2116  tcp->dst_port = odp_cpu_to_be_16(dst_port);
2117  tcp->seq_no = odp_cpu_to_be_32(tcp_seqnum);
2118  tcp->ack_no = odp_cpu_to_be_32(0x12345678);
2119  tcp->window = odp_cpu_to_be_16(0x4000);
2120  tcp->hl = 5;
2121  tcp->ack = 1;
2122  tcp_seqnum += payload_len;
2123  odp_packet_has_tcp_set(pkt, 1);
2124  } else if (proto == L4_PROTO_UDP) {
2125  odph_udphdr_t *udp = (odph_udphdr_t *)u8;
2126 
2127  memset(udp, 0, ODPH_UDPHDR_LEN);
2128  udp->src_port = odp_cpu_to_be_16(src_port);
2129  udp->dst_port = odp_cpu_to_be_16(dst_port);
2130  udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
2131  udp->chksum = 0;
2132  odp_packet_has_udp_set(pkt, 1);
2133  }
2134 
2135  u8 = data;
2136  u8 += hdr_len;
2137 
2138  if (test_options->fill_pl) {
2139  /* Init payload until the end of the first segment */
2140  for (j = 0; j < seg_len - hdr_len; j++)
2141  u8[j] = j;
2142  }
2143 
2144  /* Insert checksum (TCP checksum is updated before TX) */
2145  if (proto == L4_PROTO_UDP && !test_options->calc_latency && test_options->calc_cs)
2146  odph_udp_chksum_set(pkt);
2147 
2148  /* Increment port numbers */
2149  if (test_options->c_mode.src_port) {
2150  src_cnt++;
2151  if (src_cnt < test_options->c_mode.src_port) {
2152  src_port++;
2153  } else {
2154  src_port = test_options->src_port;
2155  src_cnt = 0;
2156  }
2157  }
2158  if (test_options->c_mode.dst_port) {
2159  dst_cnt++;
2160  if (dst_cnt < test_options->c_mode.dst_port) {
2161  dst_port++;
2162  } else {
2163  dst_port = test_options->dst_port;
2164  dst_cnt = 0;
2165  }
2166  }
2167  }
2168 
2169  return 0;
2170 }
2171 
2172 static inline void update_tcp_hdr(odp_packet_t pkt, odp_packet_t base_pkt, uint32_t hdr_len,
2173  odp_bool_t calc_cs)
2174 {
2175  odph_tcphdr_t *tcp = odp_packet_l4_ptr(pkt, NULL);
2176  odph_tcphdr_t *tcp_base = odp_packet_l4_ptr(base_pkt, NULL);
2177  uint32_t prev_seqnum = odp_be_to_cpu_32(tcp_base->seq_no);
2178 
2179  tcp->seq_no = odp_cpu_to_be_32(prev_seqnum + (odp_packet_len(pkt) - hdr_len));
2180 
2181  /* Last used sequence number is stored in the base packet */
2182  tcp_base->seq_no = tcp->seq_no;
2183 
2184  if (calc_cs)
2185  odph_tcp_chksum_set(pkt);
2186 }
2187 
2188 static inline int update_rand_data(uint8_t *data, uint32_t data_len)
2189 {
2190  uint32_t generated = 0;
2191  uint32_t retries = 0;
2192 
2193  while (generated < data_len) {
2194  int32_t ret = odp_random_data(data, data_len - generated, ODP_RANDOM_BASIC);
2195 
2196  if (odp_unlikely(ret < 0)) {
2197  ODPH_ERR("Error: odp_random_data() failed: %" PRId32 "\n", ret);
2198  return -1;
2199  } else if (odp_unlikely(ret == 0)) {
2200  retries++;
2201  if (odp_unlikely(retries > MAX_RAND_RETRIES)) {
2202  ODPH_ERR("Error: Failed to create random data\n");
2203  return -1;
2204  }
2205  continue;
2206  }
2207  data += ret;
2208  generated += ret;
2209  }
2210  return 0;
2211 }
2212 
2213 static int init_global_data(test_global_t *global)
2214 {
2215  memset(global, 0, sizeof(test_global_t));
2216  odp_atomic_init_u32(&global->exit_test, 0);
2217 
2218  for (int i = 0; i < MAX_THREADS; i++) {
2219  uint8_t *rand_data = (uint8_t *)global->rand_data[i];
2220 
2221  if (odp_unlikely(update_rand_data(rand_data, RAND_16BIT_WORDS * 2)))
2222  return -1;
2223 
2224  global->thread_arg[i].global = global;
2225  }
2226  return 0;
2227 }
2228 
2229 static inline void set_timestamp(odp_packet_t pkt, uint32_t ts_off)
2230 {
2231  const ts_data_t ts_data = { .magic = TS_MAGIC, .tx_ts = odp_time_global_strict() };
2232 
2233  (void)odp_packet_copy_from_mem(pkt, ts_off, sizeof(ts_data), &ts_data);
2234 }
2235 
2236 static int alloc_packets(odp_pool_t pool, odp_packet_t *pkt_tbl, uint32_t num,
2237  test_global_t *global)
2238 {
2239  uint32_t i, pkt_len;
2240  test_options_t *test_options = &global->test_options;
2241  uint32_t num_bins = global->num_bins;
2242 
2243  pkt_len = test_options->pkt_len;
2244 
2245  for (i = 0; i < num; i++) {
2246  if (num_bins)
2247  pkt_len = global->len_bin[i % num_bins];
2248 
2249  pkt_tbl[i] = odp_packet_alloc(pool, pkt_len);
2250  if (pkt_tbl[i] == ODP_PACKET_INVALID) {
2251  ODPH_ERR("Error: Alloc of %uB packet failed\n", pkt_len);
2252  break;
2253  }
2254  }
2255 
2256  if (i == 0)
2257  return -1;
2258 
2259  if (i != num) {
2260  odp_packet_free_multi(pkt_tbl, i);
2261  return -1;
2262  }
2263 
2264  return 0;
2265 }
2266 
2267 static inline uint32_t form_burst(odp_packet_t out_pkt[], uint32_t burst_size, uint32_t num_bins,
2268  uint32_t burst, odp_packet_t *pkt_tbl, odp_pool_t pool,
2269  int tx_mode, odp_bool_t calc_latency, uint32_t hdr_len,
2270  odp_bool_t calc_cs, uint64_t *total_bytes, uint8_t l4_proto,
2271  const uint16_t *rand_data)
2272 {
2273  uint32_t i, idx;
2274  odp_packet_t pkt;
2275  static __thread int rand_idx;
2276  uint64_t bytes = 0;
2277 
2278  idx = burst * burst_size;
2279  if (num_bins)
2280  idx = burst * burst_size * num_bins;
2281 
2282  for (i = 0; i < burst_size; i++) {
2283  if (num_bins) {
2284  uint32_t bin;
2285 
2286  if (odp_unlikely(rand_idx >= RAND_16BIT_WORDS))
2287  rand_idx = 0;
2288 
2289  /* Select random length bin */
2290  bin = rand_data[rand_idx++] % num_bins;
2291  pkt = pkt_tbl[idx + bin];
2292  idx += num_bins;
2293  } else {
2294  pkt = pkt_tbl[idx];
2295  idx++;
2296  }
2297 
2298  if (tx_mode == TX_MODE_DF) {
2299  out_pkt[i] = pkt;
2300  } else if (tx_mode == TX_MODE_REF) {
2301  out_pkt[i] = odp_packet_ref_static(pkt);
2302 
2303  if (odp_unlikely(out_pkt[i] == ODP_PACKET_INVALID))
2304  break;
2305  } else {
2306  out_pkt[i] = odp_packet_copy(pkt, pool);
2307 
2308  if (odp_unlikely(out_pkt[i] == ODP_PACKET_INVALID))
2309  break;
2310 
2311  if (calc_latency)
2312  set_timestamp(out_pkt[i], hdr_len);
2313 
2314  if (l4_proto == L4_PROTO_TCP)
2315  update_tcp_hdr(out_pkt[i], pkt, hdr_len, calc_cs);
2316  else if (l4_proto == L4_PROTO_UDP && calc_latency && calc_cs)
2317  odph_udp_chksum_set(out_pkt[i]);
2318  }
2319 
2320  bytes += odp_packet_len(out_pkt[i]);
2321  }
2322 
2323  *total_bytes = bytes;
2324 
2325  return i;
2326 }
2327 
2328 static inline int send_burst(odp_pktout_queue_t pktout, odp_packet_t pkt[],
2329  uint32_t num, int tx_mode, uint64_t *drop_bytes,
2330  const odp_packet_lso_opt_t *lso_opt ODP_UNUSED)
2331 {
2332  int ret;
2333  uint32_t sent;
2334  uint64_t bytes = 0;
2335 
2336  ret = odp_pktout_send(pktout, pkt, num);
2337 
2338  sent = ret;
2339  if (odp_unlikely(ret < 0))
2340  sent = 0;
2341 
2342  if (odp_unlikely(sent != num)) {
2343  uint32_t i;
2344  uint32_t num_drop = num - sent;
2345 
2346  for (i = sent; i < num; i++)
2347  bytes += odp_packet_len(pkt[i]);
2348 
2349  if (tx_mode != TX_MODE_DF)
2350  odp_packet_free_multi(&pkt[sent], num_drop);
2351  }
2352 
2353  *drop_bytes = bytes;
2354 
2355  return ret;
2356 }
2357 
2358 static inline int send_burst_lso(odp_pktout_queue_t pktout, odp_packet_t pkt[], uint32_t num,
2359  int tx_mode ODP_UNUSED, uint64_t *drop_bytes,
2360  const odp_packet_lso_opt_t *lso_opt)
2361 {
2362  int ret;
2363  uint32_t sent;
2364  uint64_t bytes = 0;
2365 
2366  ret = odp_pktout_send_lso(pktout, pkt, num, lso_opt);
2367 
2368  sent = ret;
2369  if (odp_unlikely(ret < 0))
2370  sent = 0;
2371 
2372  if (odp_unlikely(sent != num)) {
2373  uint32_t i;
2374  uint32_t num_drop = num - sent;
2375 
2376  for (i = sent; i < num; i++)
2377  bytes += odp_packet_len(pkt[i]);
2378 
2379  odp_packet_free_multi(&pkt[sent], num_drop);
2380  }
2381 
2382  *drop_bytes = bytes;
2383 
2384  return ret;
2385 }
2386 
2387 static int tx_thread(void *arg)
2388 {
2389  int i, tx_thr;
2390  uint32_t exit_test, num_alloc, j;
2391  odp_time_t t1, t2, next_tmo;
2392  uint64_t diff_ns, t1_nsec;
2393  odp_packet_t *pkt_tbl;
2394  thread_arg_t *thread_arg = arg;
2395  test_global_t *global = thread_arg->global;
2396  test_options_t *test_options = &global->test_options;
2397  int periodic_stat = test_options->update_msec ? 1 : 0;
2398  odp_pool_t pool = global->pool;
2399  uint64_t gap_nsec = test_options->gap_nsec;
2400  uint64_t quit = test_options->quit;
2401  uint64_t tx_timeouts = 0;
2402  uint64_t tx_bytes = 0;
2403  uint64_t tx_packets = 0;
2404  uint64_t tx_drops = 0;
2405  int ret = 0;
2406  const int thr = odp_thread_id();
2407  const uint32_t hdr_len = test_options->hdr_len;
2408  const uint32_t burst_size = test_options->burst_size;
2409  const uint32_t bursts = test_options->bursts;
2410  const uint32_t num_tx = test_options->num_tx;
2411  const uint16_t *rand_data = global->rand_data[thr];
2412  const uint8_t l4_proto = test_options->l4_proto;
2413  const int tx_mode = test_options->tx_mode;
2414  const odp_bool_t calc_cs = test_options->calc_cs;
2415  const odp_bool_t calc_latency = test_options->calc_latency;
2416  int num_pktio = test_options->num_pktio;
2417  odp_pktout_queue_t pktout[num_pktio];
2418  odp_packet_lso_opt_t lso_opt[num_pktio];
2419  uint32_t tot_packets = 0;
2420  uint32_t num_bins = global->num_bins;
2421  const send_fn_t send_burst_fn = test_options->lso.enabled ? send_burst_lso : send_burst;
2422 
2423  tx_thr = thread_arg->tx_thr;
2424  global->stat[thr].thread_type = TX_THREAD;
2425 
2426  num_alloc = global->num_tx_pkt;
2427  if (num_bins)
2428  num_alloc = global->num_tx_pkt * num_bins;
2429 
2430  for (i = 0; i < num_pktio; i++) {
2431  int seq = i * num_alloc;
2432 
2433  pktout[i] = thread_arg->pktout[i];
2434  pkt_tbl = thread_arg->packet[i];
2435 
2436  lso_opt[i].lso_profile = thread_arg->lso_profile[i];
2437  lso_opt[i].payload_offset = test_options->lso.payload_offset;
2438  lso_opt[i].max_payload_len = test_options->lso.max_payload_len;
2439 
2440  if (alloc_packets(pool, pkt_tbl, num_alloc, global)) {
2441  ret = -1;
2442  break;
2443  }
2444 
2445  tot_packets += num_alloc;
2446 
2447  if (init_packets(global, i, pkt_tbl, num_alloc, seq)) {
2448  ret = -1;
2449  break;
2450  }
2451 
2452  if (tx_mode == TX_MODE_DF) {
2453  for (j = 0; j < num_alloc; j++)
2454  odp_packet_free_ctrl_set(pkt_tbl[j],
2456  }
2457  }
2458 
2459  /* Start all workers at the same time */
2460  odp_barrier_wait(&global->barrier);
2461 
2462  t1 = odp_time_local();
2463 
2464  /* Start TX burst at different per thread offset */
2465  t1_nsec = odp_time_to_ns(t1) + gap_nsec + (tx_thr * gap_nsec / num_tx);
2466 
2467  while (ret == 0) {
2468  exit_test = odp_atomic_load_u32(&global->exit_test);
2469  if (exit_test)
2470  break;
2471 
2472  if (quit && tx_timeouts >= quit) {
2473  odp_atomic_inc_u32(&global->exit_test);
2474  break;
2475  }
2476 
2477  if (gap_nsec) {
2478  uint64_t nsec = t1_nsec + tx_timeouts * gap_nsec;
2479 
2480  next_tmo = odp_time_local_from_ns(nsec);
2481  odp_time_wait_until(next_tmo);
2482  }
2483  tx_timeouts++;
2484 
2485  /* Send bursts to each pktio */
2486  for (i = 0; i < num_pktio; i++) {
2487  uint32_t num;
2488  int sent;
2489  uint64_t total_bytes, drop_bytes;
2490  odp_packet_t pkt[burst_size];
2491 
2492  pkt_tbl = thread_arg->packet[i];
2493 
2494  for (j = 0; j < bursts; j++) {
2495  num = form_burst(pkt, burst_size, num_bins, j, pkt_tbl, pool,
2496  tx_mode, calc_latency, hdr_len, calc_cs,
2497  &total_bytes, l4_proto, rand_data);
2498 
2499  if (odp_unlikely(num == 0)) {
2500  tx_drops += burst_size;
2501  continue;
2502  }
2503 
2504  sent = send_burst_fn(pktout[i], pkt, num, tx_mode, &drop_bytes,
2505  &lso_opt[i]);
2506 
2507  if (odp_unlikely(sent < 0)) {
2508  ret = -1;
2509  tx_drops += burst_size;
2510  break;
2511  }
2512 
2513  tx_bytes += total_bytes - drop_bytes;
2514  tx_packets += sent;
2515  if (odp_unlikely(sent < (int)burst_size))
2516  tx_drops += burst_size - sent;
2517 
2518  if (odp_unlikely(periodic_stat))
2519  global->stat[thr].pktio[i].tx_packets += sent;
2520  }
2521  }
2522  }
2523 
2524  t2 = odp_time_local();
2525  diff_ns = odp_time_diff_ns(t2, t1);
2526 
2527  for (i = 0; i < num_pktio; i++) {
2528  pkt_tbl = thread_arg->packet[i];
2529 
2530  if (tot_packets == 0)
2531  break;
2532 
2533  odp_packet_free_multi(pkt_tbl, num_alloc);
2534  tot_packets -= num_alloc;
2535  }
2536 
2537  /* Update stats */
2538  global->stat[thr].time_nsec = diff_ns;
2539  global->stat[thr].tx_timeouts = tx_timeouts;
2540  global->stat[thr].tx_bytes = tx_bytes;
2541  global->stat[thr].tx_packets = tx_packets;
2542  global->stat[thr].tx_drops = tx_drops;
2543 
2544  return ret;
2545 }
2546 
2547 static int start_workers(test_global_t *global, odp_instance_t instance)
2548 {
2549  odph_thread_common_param_t thr_common;
2550  int i, j, ret, tx_thr;
2551  test_options_t *test_options = &global->test_options;
2552  int num_pktio = test_options->num_pktio;
2553  int num_rx = test_options->num_rx;
2554  int num_cpu = test_options->num_cpu;
2555  odph_thread_param_t thr_param[num_cpu];
2556 
2557  memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
2558  odph_thread_common_param_init(&thr_common);
2559 
2560  thr_common.instance = instance;
2561  thr_common.cpumask = &global->cpumask;
2562 
2563  /* Receive threads */
2564  for (i = 0; i < num_rx; i++) {
2565  /* In direct mode, dedicate a pktin queue per pktio interface (per RX thread) */
2566  for (j = 0; test_options->direct_rx && j < num_pktio; j++)
2567  global->thread_arg[i].pktin[j] = global->pktio[j].pktin[i];
2568 
2569  odph_thread_param_init(&thr_param[i]);
2570  thr_param[i].start = rx_thread;
2571  thr_param[i].arg = &global->thread_arg[i];
2572  thr_param[i].thr_type = ODP_THREAD_WORKER;
2573  }
2574 
2575  /* Transmit threads */
2576  tx_thr = 0;
2577  for (i = num_rx; i < num_cpu; i++) {
2578  for (j = 0; j < num_pktio; j++) {
2579  odp_pktout_queue_t pktout;
2580 
2581  global->thread_arg[i].tx_thr = tx_thr;
2582 
2583  /* Dedicate a pktout queue per pktio interface
2584  * (per TX thread) */
2585  pktout = global->pktio[j].pktout[tx_thr];
2586  global->thread_arg[i].pktout[j] = pktout;
2587 
2588  global->thread_arg[i].lso_profile[j] = global->pktio[j].lso_profile;
2589  }
2590 
2591  odph_thread_param_init(&thr_param[i]);
2592  thr_param[i].start = tx_thread;
2593  thr_param[i].arg = &global->thread_arg[i];
2594  thr_param[i].thr_type = ODP_THREAD_WORKER;
2595  tx_thr++;
2596  }
2597 
2598  ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
2599  num_cpu);
2600 
2601  if (ret != num_cpu) {
2602  ODPH_ERR("Error: thread create failed %i\n", ret);
2603  return -1;
2604  }
2605 
2606  return 0;
2607 }
2608 
2609 static void print_periodic_stat(test_global_t *global, uint64_t nsec)
2610 {
2611  int i, j;
2612  int num_pktio = global->test_options.num_pktio;
2613  double sec = nsec / 1000000000.0;
2614  uint64_t num_tx[num_pktio];
2615  uint64_t num_rx[num_pktio];
2616 
2617  for (i = 0; i < num_pktio; i++) {
2618  num_tx[i] = 0;
2619  num_rx[i] = 0;
2620 
2621  for (j = 0; j < MAX_THREADS; j++) {
2622  if (global->stat[j].thread_type == RX_THREAD)
2623  num_rx[i] += global->stat[j].pktio[i].rx_packets;
2624  else if (global->stat[j].thread_type == TX_THREAD)
2625  num_tx[i] += global->stat[j].pktio[i].tx_packets;
2626  }
2627  }
2628  if (global->test_options.num_tx) {
2629  printf(" TX: %12.6fs", sec);
2630  for (i = 0; i < num_pktio; i++)
2631  printf(" %10" PRIu64 "", num_tx[i]);
2632  printf("\n");
2633  }
2634 
2635  if (global->test_options.num_rx) {
2636  printf(" RX: %12.6fs", sec);
2637  for (i = 0; i < num_pktio; i++)
2638  printf(" %10" PRIu64 "", num_rx[i]);
2639  printf("\n");
2640  }
2641 }
2642 
2643 static void periodic_print_loop(test_global_t *global)
2644 {
2645  odp_time_t t1, t2;
2646  uint64_t nsec;
2647  int i;
2648  int num_pktio = global->test_options.num_pktio;
2649 
2650  printf("\n\nPackets per interface\n");
2651  printf(" Dir Time");
2652  for (i = 0; i < num_pktio; i++)
2653  printf(" %10i", i);
2654 
2655  printf("\n -----------------");
2656  for (i = 0; i < num_pktio; i++)
2657  printf("-----------");
2658 
2659  printf("\n");
2660 
2661  t1 = odp_time_local();
2662  while (odp_atomic_load_u32(&global->exit_test) == 0) {
2663  usleep(1000 * global->test_options.update_msec);
2664  t2 = odp_time_local();
2665  nsec = odp_time_diff_ns(t2, t1);
2666  print_periodic_stat(global, nsec);
2667  }
2668 }
2669 
2670 static void print_humanised_time(double time_nsec)
2671 {
2672  if (time_nsec > ODP_TIME_SEC_IN_NS)
2673  printf("%.2f s\n", time_nsec / ODP_TIME_SEC_IN_NS);
2674  else if (time_nsec > ODP_TIME_MSEC_IN_NS)
2675  printf("%.2f ms\n", time_nsec / ODP_TIME_MSEC_IN_NS);
2676  else if (time_nsec > ODP_TIME_USEC_IN_NS)
2677  printf("%.2f us\n", time_nsec / ODP_TIME_USEC_IN_NS);
2678  else
2679  printf("%.0f ns\n", time_nsec);
2680 }
2681 
2682 static void print_humanised_latency(double lat_nsec, double lat_min_nsec, double lat_max_nsec)
2683 {
2684  printf(" rx ave packet latency: ");
2685  print_humanised_time(lat_nsec);
2686  printf(" rx min packet latency: ");
2687  print_humanised_time(lat_min_nsec);
2688  printf(" rx max packet latency: ");
2689  print_humanised_time(lat_max_nsec);
2690 }
2691 
2692 static int print_final_stat(test_global_t *global)
2693 {
2694  int i, num_thr;
2695  double rx_mbit_per_sec, tx_mbit_per_sec;
2696  test_options_t *test_options = &global->test_options;
2697  int num_rx = test_options->num_rx;
2698  int num_tx = test_options->num_tx;
2699  uint64_t rx_nsec_sum = 0;
2700  uint64_t rx_pkt_sum = 0;
2701  uint64_t rx_byte_sum = 0;
2702  uint64_t rx_tmo_sum = 0;
2703  uint64_t rx_lat_nsec_sum = 0;
2704  uint64_t rx_lat_min_nsec = UINT64_MAX;
2705  uint64_t rx_lat_max_nsec = 0;
2706  uint64_t rx_lat_pkt_sum = 0;
2707  uint64_t tx_nsec_sum = 0;
2708  uint64_t tx_pkt_sum = 0;
2709  uint64_t tx_byte_sum = 0;
2710  uint64_t tx_drop_sum = 0;
2711  uint64_t tx_tmo_sum = 0;
2712  double rx_pkt_ave = 0.0;
2713  double rx_pkt_per_sec = 0.0;
2714  double rx_byte_per_sec = 0.0;
2715  double rx_pkt_len = 0.0;
2716  double rx_sec = 0.0;
2717  double rx_ave_lat_nsec = 0.0;
2718  double tx_pkt_per_sec = 0.0;
2719  double tx_byte_per_sec = 0.0;
2720  double tx_sec = 0.0;
2721 
2722  printf("\nRESULTS PER THREAD\n");
2723  printf(" rx thread:\n");
2724  printf(" 1 2 3 4 5 6 7 8\n");
2725  printf(" ---------------------------------------------------------------------------------------\n");
2726  printf(" ");
2727 
2728  num_thr = 0;
2729  for (i = 0; i < MAX_THREADS; i++) {
2730  if (global->stat[i].thread_type != RX_THREAD)
2731  continue;
2732 
2733  if (num_thr && (num_thr % 8) == 0)
2734  printf("\n ");
2735 
2736  printf("%10" PRIu64 " ", global->stat[i].rx_packets);
2737  num_thr++;
2738  }
2739 
2740  printf("\n\n");
2741 
2742  printf(" tx thread:\n");
2743  printf(" 1 2 3 4 5 6 7 8\n");
2744  printf(" ---------------------------------------------------------------------------------------\n");
2745  printf(" ");
2746 
2747  num_thr = 0;
2748  for (i = 0; i < MAX_THREADS; i++) {
2749  if (global->stat[i].thread_type != TX_THREAD)
2750  continue;
2751 
2752  if (num_thr && (num_thr % 8) == 0)
2753  printf("\n ");
2754 
2755  printf("%10" PRIu64 " ", global->stat[i].tx_packets);
2756  num_thr++;
2757  }
2758 
2759  printf("\n\n");
2760 
2761  for (i = 0; i < MAX_THREADS; i++) {
2762  if (global->stat[i].thread_type == RX_THREAD) {
2763  rx_tmo_sum += global->stat[i].rx_timeouts;
2764  rx_pkt_sum += global->stat[i].rx_packets;
2765  rx_byte_sum += global->stat[i].rx_bytes;
2766  rx_nsec_sum += global->stat[i].time_nsec;
2767  rx_lat_nsec_sum += global->stat[i].rx_lat_nsec;
2768  rx_lat_pkt_sum += global->stat[i].rx_lat_packets;
2769 
2770  if (global->stat[i].rx_lat_min_nsec < rx_lat_min_nsec)
2771  rx_lat_min_nsec = global->stat[i].rx_lat_min_nsec;
2772 
2773  if (global->stat[i].rx_lat_max_nsec > rx_lat_max_nsec)
2774  rx_lat_max_nsec = global->stat[i].rx_lat_max_nsec;
2775  } else if (global->stat[i].thread_type == TX_THREAD) {
2776  tx_tmo_sum += global->stat[i].tx_timeouts;
2777  tx_pkt_sum += global->stat[i].tx_packets;
2778  tx_byte_sum += global->stat[i].tx_bytes;
2779  tx_drop_sum += global->stat[i].tx_drops;
2780  tx_nsec_sum += global->stat[i].time_nsec;
2781  }
2782  }
2783 
2784  if (num_rx)
2785  rx_pkt_ave = (double)rx_pkt_sum / num_rx;
2786  rx_sec = rx_nsec_sum / 1000000000.0;
2787  tx_sec = tx_nsec_sum / 1000000000.0;
2788 
2789  /* Packets and bytes per thread per sec */
2790  if (rx_nsec_sum) {
2791  rx_pkt_per_sec = (1000000000.0 * (double)rx_pkt_sum) /
2792  (double)rx_nsec_sum;
2793 
2794  rx_byte_per_sec = 1000000000.0;
2795  rx_byte_per_sec *= (rx_byte_sum + 24 * rx_pkt_sum);
2796  rx_byte_per_sec /= (double)rx_nsec_sum;
2797  }
2798 
2799  if (tx_nsec_sum) {
2800  tx_pkt_per_sec = (1000000000.0 * (double)tx_pkt_sum) /
2801  (double)tx_nsec_sum;
2802 
2803  tx_byte_per_sec = 1000000000.0;
2804  tx_byte_per_sec *= (tx_byte_sum + 24 * tx_pkt_sum);
2805  tx_byte_per_sec /= (double)tx_nsec_sum;
2806  }
2807 
2808  /* Total Mbit/s */
2809  rx_mbit_per_sec = (num_rx * 8 * rx_byte_per_sec) / 1000000.0;
2810  tx_mbit_per_sec = (num_tx * 8 * tx_byte_per_sec) / 1000000.0;
2811 
2812  if (rx_pkt_sum)
2813  rx_pkt_len = (double)rx_byte_sum / rx_pkt_sum;
2814 
2815  if (rx_lat_pkt_sum)
2816  rx_ave_lat_nsec = (double)rx_lat_nsec_sum / rx_lat_pkt_sum;
2817 
2818  printf("TOTAL (%i rx and %i tx threads)\n", num_rx, num_tx);
2819  printf(" rx timeouts: %" PRIu64 "\n", rx_tmo_sum);
2820  printf(" rx time spent (sec): %.3f\n", rx_sec);
2821  printf(" rx packets: %" PRIu64 "\n", rx_pkt_sum);
2822  printf(" rx packets drained: %" PRIu64 "\n", global->drained);
2823  printf(" rx packets per thr: %.1f\n", rx_pkt_ave);
2824  printf(" rx packets per thr per sec: %.1f\n", rx_pkt_per_sec);
2825  printf(" rx packets per sec: %.1f\n", num_rx * rx_pkt_per_sec);
2826  printf(" rx ave packet len: %.1f\n", rx_pkt_len);
2827 
2828  if (rx_lat_pkt_sum)
2829  print_humanised_latency(rx_ave_lat_nsec, rx_lat_min_nsec, rx_lat_max_nsec);
2830 
2831  printf(" rx Mbit/s: %.1f\n", rx_mbit_per_sec);
2832  printf("\n");
2833  printf(" tx timeouts: %" PRIu64 "\n", tx_tmo_sum);
2834  printf(" tx time spent (sec): %.3f\n", tx_sec);
2835  printf(" tx packets: %" PRIu64 "\n", tx_pkt_sum);
2836  printf(" tx dropped packets: %" PRIu64 "\n", tx_drop_sum);
2837  printf(" tx packets per thr per sec: %.1f\n", tx_pkt_per_sec);
2838  printf(" tx packets per sec: %.1f\n", num_tx * tx_pkt_per_sec);
2839  printf(" tx Mbit/s: %.1f\n", tx_mbit_per_sec);
2840  printf("\n");
2841 
2842  if (rx_pkt_sum < MIN_RX_PACKETS_CI)
2843  return -1;
2844 
2845  return 0;
2846 }
2847 
2848 static void sig_handler(int signo)
2849 {
2850  (void)signo;
2851 
2852  if (test_global == NULL)
2853  return;
2854 
2855  odp_atomic_add_u32(&test_global->exit_test, 1);
2856 }
2857 
2858 int main(int argc, char **argv)
2859 {
2860  odph_helper_options_t helper_options;
2861  odp_instance_t instance;
2862  odp_init_t init;
2863  test_global_t *global;
2864  odp_shm_t shm;
2865  int ret = 0;
2866 
2867  signal(SIGINT, sig_handler);
2868 
2869  /* Let helper collect its own arguments (e.g. --odph_proc) */
2870  argc = odph_parse_options(argc, argv);
2871  if (odph_options(&helper_options)) {
2872  ODPH_ERR("Error: reading ODP helper options failed.\n");
2873  exit(EXIT_FAILURE);
2874  }
2875 
2876  /* List features not to be used */
2877  odp_init_param_init(&init);
2878  init.not_used.feat.cls = 1;
2879  init.not_used.feat.compress = 1;
2880  init.not_used.feat.crypto = 1;
2881  init.not_used.feat.ipsec = 1;
2882  init.not_used.feat.timer = 1;
2883  init.not_used.feat.tm = 1;
2884 
2885  init.mem_model = helper_options.mem_model;
2886 
2887  /* Init ODP before calling anything else */
2888  if (odp_init_global(&instance, &init, NULL)) {
2889  ODPH_ERR("Error: Global init failed.\n");
2890  return 1;
2891  }
2892 
2893  /* Init this thread */
2894  if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
2895  ODPH_ERR("Error: Local init failed.\n");
2896  return 1;
2897  }
2898 
2899  shm = odp_shm_reserve("packet_gen_global", sizeof(test_global_t),
2900  ODP_CACHE_LINE_SIZE, 0);
2901 
2902  if (shm == ODP_SHM_INVALID) {
2903  ODPH_ERR("Error: SHM reserve failed.\n");
2904  return 1;
2905  }
2906 
2907  global = odp_shm_addr(shm);
2908  test_global = global;
2909 
2910  if (init_global_data(global)) {
2911  ret = 1;
2912  goto term;
2913  }
2914 
2915  if (parse_options(argc, argv, global)) {
2916  ret = 1;
2917  goto term;
2918  }
2919 
2921 
2922  /* Avoid all scheduler API calls in direct input mode */
2923  if (global->test_options.direct_rx == 0)
2924  odp_schedule_config(NULL);
2925 
2926  if (set_num_cpu(global)) {
2927  ret = 1;
2928  goto term;
2929  }
2930 
2931  if (open_pktios(global)) {
2932  ret = 1;
2933  goto term;
2934  }
2935 
2936  if (start_pktios(global)) {
2937  ret = 1;
2938  goto term;
2939  }
2940 
2941  /* Start worker threads */
2942  start_workers(global, instance);
2943 
2944  /* Wait until workers have started. */
2945  odp_barrier_wait(&global->barrier);
2946 
2947  /* Periodic statistics printing */
2948  if (global->test_options.update_msec)
2949  periodic_print_loop(global);
2950 
2951  /* Wait workers to exit */
2952  odph_thread_join(global->thread_tbl,
2953  global->test_options.num_cpu);
2954 
2955  if (stop_pktios(global))
2956  ret = 1;
2957 
2958  if (global->test_options.direct_rx)
2959  drain_direct_input(global);
2960  else
2961  drain_scheduler(global);
2962 
2963  if (close_pktios(global))
2964  ret = 1;
2965 
2966  if (print_final_stat(global))
2967  ret = 2;
2968 
2969 term:
2970  if (odp_shm_free(shm)) {
2971  ODPH_ERR("Error: SHM free failed.\n");
2972  return 1;
2973  }
2974 
2975  if (odp_term_local()) {
2976  ODPH_ERR("Error: term local failed.\n");
2977  return 1;
2978  }
2979 
2980  if (odp_term_global(instance)) {
2981  ODPH_ERR("Error: term global failed.\n");
2982  return 1;
2983  }
2984 
2985  return ret;
2986 }
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
void odp_atomic_add_u32(odp_atomic_u32_t *atom, uint32_t val)
Add to atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_inc_u32(odp_atomic_u32_t *atom)
Increment atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
uint16_t odp_chksum_ones_comp16(const void *data, uint32_t data_len)
Ones' complement sum of 16-bit words.
#define ODP_PACKED
Defines type/struct to be packed.
#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
odp_u16be_t odp_cpu_to_be_16(uint16_t cpu16)
Convert cpu native uint16_t to 16bit big endian.
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition: spec/hints.h:54
odp_u32be_t odp_cpu_to_be_32(uint32_t cpu32)
Convert cpu native uint32_t to 32bit big endian.
#define ODP_BIG_ENDIAN
Big endian byte order.
uint32_t odp_be_to_cpu_32(odp_u32be_t be32)
Convert 32bit big endian to cpu native uint32_t.
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.
int odp_cpumask_all_available(odp_cpumask_t *mask)
Report all the available CPUs.
void odp_cpumask_clr(odp_cpumask_t *mask, int cpu)
Remove CPU from mask.
void odp_event_free(odp_event_t event)
Free 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.
#define ODP_STATIC_ASSERT(cond, msg)
Compile time assertion macro.
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.
odp_lso_profile_t odp_lso_profile_create(odp_pktio_t pktio, const odp_lso_profile_param_t *param)
Create LSO profile.
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_link_info(odp_pktio_t pktio, odp_pktio_link_info_t *info)
Retrieve information about packet IO link status.
void odp_lso_profile_param_init(odp_lso_profile_param_t *param)
Initialize LSO profile parameters.
#define ODP_LSO_PROFILE_INVALID
Invalid LSO profile handle.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
#define ODP_LSO_MAX_CUSTOM
Maximum number of custom LSO fields supported by ODP API.
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.
int odp_lso_profile_destroy(odp_lso_profile_t lso_profile)
Destroy LSO profile.
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_pktout_send_lso(odp_pktout_queue_t queue, const odp_packet_t packet[], int num, const odp_packet_lso_opt_t *lso_opt)
Send packets with segmentation offload.
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_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_link_status_t odp_pktio_link_status(odp_pktio_t pktio)
Determine pktio link is up or down 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_LSO_PROTO_IPV4
LSO performs IPv4 fragmentation.
@ ODP_LSO_PROTO_TCP_IPV4
LSO performs TCP segmentation on top of IPv4.
@ ODP_LSO_PROTO_CUSTOM
Custom protocol.
@ ODP_PKTOUT_MODE_DIRECT
Direct packet output on the interface.
@ ODP_PKTOUT_MODE_DISABLED
Application will never send to this interface.
@ ODP_PKTIO_LINK_DUPLEX_HALF
Half duplex mode.
@ ODP_PKTIO_LINK_DUPLEX_FULL
Full duplex mode.
@ ODP_PKTIO_LINK_AUTONEG_OFF
Autonegotiation disabled.
@ ODP_PKTIO_LINK_AUTONEG_ON
Autonegotiation enabled.
@ ODP_LSO_ADD_SEGMENT_NUM
Add current segment number.
@ ODP_LSO_ADD_PAYLOAD_OFFSET
Add number of payload bytes in all previous segments.
@ ODP_LSO_ADD_PAYLOAD_LEN
Add number of payload bytes in the segment.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIO_LINK_PAUSE_OFF
No flow control.
@ ODP_PKTIO_LINK_PAUSE_ON
Pause frame flow control enabled.
@ ODP_PKTIN_MODE_DIRECT
Direct packet input from the interface.
@ ODP_PKTIN_MODE_DISABLED
Application will never receive from this interface.
@ ODP_PKTIN_MODE_SCHED
Packet input through scheduler and scheduled event queues.
@ ODP_PKTIO_LINK_STATUS_UP
Link status is up.
void odp_packet_from_event_multi(odp_packet_t pkt[], const odp_event_t ev[], int num)
Convert multiple packet events to packet handles.
void odp_packet_has_vlan_qinq_set(odp_packet_t pkt, int val)
Set flag for VLAN QinQ (stacked VLAN)
int odp_packet_l3_offset_set(odp_packet_t pkt, uint32_t offset)
Set layer 3 start offset.
int odp_packet_input_index(odp_packet_t pkt)
Packet input interface index.
uint32_t odp_packet_seg_len(odp_packet_t pkt)
Packet data length following the data pointer.
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.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
int odp_packet_l4_offset_set(odp_packet_t pkt, uint32_t offset)
Set layer 4 start offset.
void odp_packet_has_eth_set(odp_packet_t pkt, int val)
Set flag for Ethernet header.
uint32_t odp_packet_len(odp_packet_t pkt)
Packet data length.
odp_packet_t odp_packet_alloc(odp_pool_t pool, uint32_t len)
Allocate a packet from a packet pool.
void odp_packet_has_udp_set(odp_packet_t pkt, int val)
Set flag for UDP.
void odp_packet_free(odp_packet_t pkt)
Free packet.
void odp_packet_free_ctrl_set(odp_packet_t pkt, odp_packet_free_ctrl_t ctrl)
Set packet free control option.
void odp_packet_has_tcp_set(odp_packet_t pkt, int val)
Set flag for TCP.
int odp_packet_copy_from_mem(odp_packet_t pkt, uint32_t offset, uint32_t len, const void *src)
Copy data from memory to packet.
void * odp_packet_l4_ptr(odp_packet_t pkt, uint32_t *len)
Layer 4 start pointer.
#define ODP_PACKET_INVALID
Invalid packet.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
int odp_packet_copy_to_mem(odp_packet_t pkt, uint32_t offset, uint32_t len, void *dst)
Copy data from packet to memory.
odp_packet_t odp_packet_ref_static(odp_packet_t pkt)
Create a static reference to a packet.
void odp_packet_has_vlan_set(odp_packet_t pkt, int val)
Set flag for VLAN.
void odp_packet_has_ipv4_set(odp_packet_t pkt, int val)
Set flag for IPv4.
@ ODP_PACKET_FREE_CTRL_DONT_FREE
Don't free packet after processing it.
@ ODP_PROTO_LAYER_ALL
All layers.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
int32_t odp_random_data(uint8_t *buf, uint32_t len, odp_random_kind_t kind)
Generate random byte data.
@ ODP_RANDOM_BASIC
Basic random, presumably pseudo-random generated by SW.
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_default_prio(void)
Default scheduling priority level.
void odp_schedule_pause(void)
Pause scheduling.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
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.
int odp_thread_id(void)
Get thread identifier.
@ 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_SEC_IN_NS
A second in nanoseconds.
void odp_time_wait_until(odp_time_t time)
Wait until the specified (wall clock) time has been reached.
odp_time_t odp_time_local_from_ns(uint64_t ns)
Convert nanoseconds to local time.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_NULL
Zero time stamp.
uint64_t odp_time_local_res(void)
Local time resolution in hertz.
odp_time_t odp_time_global_strict(void)
Current global time (strict)
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
#define ODP_TIME_USEC_IN_NS
A microsecond in nanoseconds.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
uint32_t max_payload_len
Maximum payload length per an LSO generated packet (in bytes).
uint32_t max_packet_segments
Maximum number of segments in an input packet.
uint32_t max_segments
Maximum number of segments an LSO operation may create.
uint32_t max_profiles
Maximum number of LSO profiles.
uint32_t max_profiles_per_pktio
Maximum number of LSO profiles per packet IO interface.
uint16_t add_payload_len
ODP_LSO_ADD_PAYLOAD_LEN support.
uint8_t max_num_custom
Maximum number of custom fields supported per LSO profile.
struct odp_lso_capability_t::@105 mod_op
Supported LSO custom modification options.
uint32_t tcp_ipv4
ODP_LSO_PROTO_TCP_IPV4 support.
uint16_t add_segment_num
ODP_LSO_ADD_SEGMENT_NUM support.
uint32_t ipv4
ODP_LSO_PROTO_IPV4 support.
uint32_t max_payload_offset
Maximum supported offset to the packet payload (in bytes).
struct odp_lso_capability_t::@106 proto
Supported LSO protocol options.
uint32_t custom
ODP_LSO_PROTO_CUSTOM support.
uint16_t add_payload_offset
ODP_LSO_ADD_PAYLOAD_OFFSET support.
odp_lso_profile_t lso_profile
LSO profile handle.
uint32_t payload_offset
LSO payload offset.
uint32_t max_payload_len
Maximum payload length in an LSO segment.
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.
struct odp_pktio_capability_t::@109 free_ctrl
Supported packet free control options.
odp_pktio_set_op_t set_op
Supported set operations.
uint32_t max_output
Maximum valid value for 'maxlen_output'.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_input
Maximum valid value for 'maxlen_input'.
odp_pktio_config_t config
Supported pktio configuration options.
uint32_t min_output
Minimum valid value for 'maxlen_output'.
uint32_t max_output_queues
Maximum number of output queues.
uint32_t dont_free
Packet free control option ODP_PACKET_FREE_CTRL_DONT_FREE support with odp_packet_free_ctrl_set().
uint32_t min_input
Minimum valid value for 'maxlen_input'.
odp_lso_capability_t lso
LSO capabilities.
struct odp_pktio_capability_t::@107 maxlen
Supported frame lengths for odp_pktio_maxlen_set()
Packet IO configuration options.
odp_pktout_config_opt_t pktout
Packet output configuration options bit field.
odp_bool_t enable_lso
Enable Large Send Offload (LSO)
odp_pktio_parser_config_t parser
Packet input parser configuration.
odp_pktin_config_opt_t pktin
Packet input configuration options bit field.
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.
struct odp_pool_capability_t::@122 pkt
Packet pool capabilities
uint32_t max_num
Maximum number of buffers of any size.
uint32_t max_seg_len
Maximum packet segment data length in bytes.
uint32_t max_len
Maximum packet data length in bytes.
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.
odp_schedule_param_t sched
Scheduler parameters.
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::@148 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()
uint64_t drop_udp_err
Drop packets with a UDP error on packet input.
uint64_t udp_chksum
Check UDP checksum on packet input.
uint64_t tcp_chksum
Check TCP checksum on packet input.
struct odp_pktin_config_opt_t::@100 bit
Option flags.
uint64_t drop_tcp_err
Drop packets with a TCP error on packet input.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.
struct odp_pktin_hash_proto_t::@99 proto
Protocol header fields for hashing.
struct odp_pktio_set_op_t::@104 op
Operation flags.
uint32_t maxlen
Maximum frame length.
uint32_t promisc_mode
Promiscuous mode.
struct odp_pktout_config_opt_t::@101 bit
Option flags for packet output.
uint64_t tcp_chksum
Insert TCP checksum on packet by default.
uint64_t tcp_chksum_ena
Enable TCP checksum insertion.
uint64_t udp_chksum
Insert UDP checksum on packet by default.
uint64_t udp_chksum_ena
Enable UDP checksum insertion.