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