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