API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_pktio_perf.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2015-2018 Linaro Limited
3 */
4
25#include <odp_api.h>
26
27#include <odp/helper/odph_api.h>
28
29#include <getopt.h>
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <inttypes.h>
34
35#define TEST_SKIP 77
36
37#define PKT_BUF_NUM (32 * 1024)
38#define MAX_NUM_IFACES 2
39#define TEST_HDR_MAGIC 0x92749451
40#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
41#define BATCH_LEN_MAX 32
42
43/* Packet rate at which to start when using binary search */
44#define RATE_SEARCH_INITIAL_PPS 1000000
45
46/* When using the binary search method to determine the maximum
47 * achievable packet rate, this value specifies how close the pass
48 * and fail measurements must be before the test is terminated. */
49#define RATE_SEARCH_ACCURACY_PPS 100000
50
51/* Amount of time to wait, in nanoseconds, between the transmitter(s)
52 * completing and the receiver(s) being shutdown. Any packets not
53 * received by this time will be assumed to have been lost. */
54#define SHUTDOWN_DELAY_NS (ODP_TIME_MSEC_IN_NS * 100)
55
56/* Number of duration units in a second. */
57#define T_SCALE 10
58
59/* Default test duration in T_SCALE units */
60#define DEFAULT_DURATION 1
61
62#define PKT_HDR_LEN (sizeof(pkt_head_t) + ODPH_UDPHDR_LEN + \
63 ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN)
64
66typedef struct {
67 unsigned int cpu_count; /* CPU count */
68 int num_tx_workers;/* Number of CPUs to use for transmit */
69 int duration; /* Time to run each iteration of the test for */
70 uint32_t tx_batch_len; /* Number of packets to send in a single
71 batch */
72 int schedule; /* 1: receive packets via scheduler
73 0: receive packets via direct deq */
74 uint32_t rx_batch_len; /* Number of packets to receive in a single
75 batch */
76 uint64_t pps; /* Attempted packet rate */
77 int verbose; /* Print verbose information, such as per
78 thread statistics */
79 unsigned pkt_len; /* Packet payload length in bytes (not
80 including headers) */
81 int search; /* Set implicitly when pps is not configured.
82 Perform a search at different packet rates
83 to determine the maximum rate at which no
84 packet loss occurs. */
85
86 char *if_str;
87 const char *ifaces[MAX_NUM_IFACES];
88 int num_ifaces;
89} test_args_t;
90
91typedef struct ODP_ALIGNED_CACHE {
92 uint64_t rx_cnt; /* Valid packets received */
93 uint64_t rx_ignore; /* Ignored packets */
94} pkt_rx_stats_t;
95
96typedef struct ODP_ALIGNED_CACHE {
97 uint64_t tx_cnt; /* Packets transmitted */
98 uint64_t alloc_failures;/* Packet allocation failures */
99 uint64_t enq_failures; /* Enqueue failures */
100 odp_time_t idle_ticks; /* Idle ticks count in TX loop */
101} pkt_tx_stats_t;
102
103/* Test global variables */
104typedef struct {
105 odp_instance_t instance;
106 test_args_t args;
107 odp_barrier_t rx_barrier;
108 odp_barrier_t tx_barrier;
109 odp_pktio_t pktio_tx;
110 odp_pktio_t pktio_rx;
111 /* Pool from which transmitted packets are allocated */
112 odp_pool_t transmit_pkt_pool;
113 pkt_rx_stats_t *rx_stats;
114 pkt_tx_stats_t *tx_stats;
115 uint8_t src_mac[ODPH_ETHADDR_LEN];
116 uint8_t dst_mac[ODPH_ETHADDR_LEN];
117 uint32_t rx_stats_size;
118 uint32_t tx_stats_size;
119 /* Indicate to the receivers to shutdown */
120 odp_atomic_u32_t shutdown;
121 /* Sequence number of IP packets */
123} test_globals_t;
124
125/* Status of max rate search */
126typedef struct {
127 uint64_t pps_curr; /* Current attempted PPS */
128 uint64_t pps_pass; /* Highest passing PPS */
129 uint64_t pps_fail; /* Lowest failing PPS */
130 int warmup; /* Warmup stage - ignore results */
131} test_status_t;
132
133/* Thread specific arguments */
134typedef struct {
135 int batch_len; /* Number of packets per transmit batch */
136 int duration; /* Run duration in scaled time units */
137 uint64_t pps; /* Packets per second for this thread */
138} thread_args_t;
139
140typedef struct {
141 odp_u32be_t magic; /* Packet header magic number */
142} pkt_head_t;
143
144/* Application global data */
145static test_globals_t *gbl_args;
146
147/*
148 * Generate a single test packet for transmission.
149 */
150static odp_packet_t pktio_create_packet(uint32_t seq)
151{
152 odp_packet_t pkt;
153 odph_ethhdr_t *eth;
154 odph_ipv4hdr_t *ip;
155 odph_udphdr_t *udp;
156 char *buf;
157 uint32_t offset;
158 pkt_head_t pkt_hdr;
159 size_t payload_len;
160
161 payload_len = sizeof(pkt_hdr) + gbl_args->args.pkt_len;
162
163 pkt = odp_packet_alloc(gbl_args->transmit_pkt_pool,
164 payload_len + ODPH_UDPHDR_LEN +
165 ODPH_IPV4HDR_LEN + ODPH_ETHHDR_LEN);
166
167 if (pkt == ODP_PACKET_INVALID)
168 return ODP_PACKET_INVALID;
169
170 buf = odp_packet_data(pkt);
171
172 /* Ethernet */
173 offset = 0;
174 odp_packet_l2_offset_set(pkt, offset);
175 eth = (odph_ethhdr_t *)buf;
176 memcpy(eth->src.addr, gbl_args->src_mac, ODPH_ETHADDR_LEN);
177 memcpy(eth->dst.addr, gbl_args->dst_mac, ODPH_ETHADDR_LEN);
178 eth->type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
179
180 /* IP */
181 offset += ODPH_ETHHDR_LEN;
182 odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
183 ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
184 ip->dst_addr = odp_cpu_to_be_32(0);
185 ip->src_addr = odp_cpu_to_be_32(0);
186 ip->ver_ihl = ODPH_IPV4 << 4 | ODPH_IPV4HDR_IHL_MIN;
187 ip->tot_len = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN +
188 ODPH_IPV4HDR_LEN);
189 ip->ttl = 128;
190 ip->proto = ODPH_IPPROTO_UDP;
191 ip->id = odp_cpu_to_be_16(seq);
192 ip->chksum = 0;
193 odph_ipv4_csum_update(pkt);
194
195 /* UDP */
196 offset += ODPH_IPV4HDR_LEN;
197 odp_packet_l4_offset_set(pkt, offset);
198 udp = (odph_udphdr_t *)(buf + offset);
199 udp->src_port = odp_cpu_to_be_16(0);
200 udp->dst_port = odp_cpu_to_be_16(0);
201 udp->length = odp_cpu_to_be_16(payload_len + ODPH_UDPHDR_LEN);
202 udp->chksum = 0;
203
204 /* payload */
205 offset += ODPH_UDPHDR_LEN;
206 pkt_hdr.magic = TEST_HDR_MAGIC;
207 if (odp_packet_copy_from_mem(pkt, offset, sizeof(pkt_hdr),
208 &pkt_hdr) != 0)
209 ODPH_ABORT("Failed to generate test packet.\n");
210
211 return pkt;
212}
213
214/*
215 * Check if a packet payload contains test payload magic number.
216 */
217static int pktio_pkt_has_magic(odp_packet_t pkt)
218{
219 size_t l4_off;
220 pkt_head_t pkt_hdr;
221
222 l4_off = ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN;
223 int ret = odp_packet_copy_to_mem(pkt,
224 l4_off + ODPH_UDPHDR_LEN,
225 sizeof(pkt_hdr), &pkt_hdr);
226
227 if (ret != 0)
228 return 0;
229
230 if (pkt_hdr.magic == TEST_HDR_MAGIC)
231 return 1;
232
233 return 0;
234}
235
236/*
237 * Allocate packets for transmission.
238 */
239static int alloc_packets(odp_packet_t *pkt_tbl, int num_pkts)
240{
241 int n;
242 uint16_t seq;
243
244 seq = odp_atomic_fetch_add_u32(&gbl_args->ip_seq, num_pkts);
245 for (n = 0; n < num_pkts; ++n) {
246 pkt_tbl[n] = pktio_create_packet(seq + n);
247 if (pkt_tbl[n] == ODP_PACKET_INVALID)
248 break;
249 }
250
251 return n;
252}
253
254static int send_packets(odp_pktout_queue_t pktout,
255 odp_packet_t *pkt_tbl, unsigned pkts)
256{
257 unsigned tx_drops;
258 unsigned sent = 0;
259
260 if (pkts == 0)
261 return 0;
262
263 while (sent < pkts) {
264 int ret;
265
266 ret = odp_pktout_send(pktout, &pkt_tbl[sent], pkts - sent);
267
268 if (odp_likely(ret > 0))
269 sent += ret;
270 else
271 break;
272 }
273
274 tx_drops = pkts - sent;
275
276 if (odp_unlikely(tx_drops))
277 odp_packet_free_multi(&pkt_tbl[sent], tx_drops);
278
279 return sent;
280}
281
282/*
283 * Main packet transmit routine. Transmit packets at a fixed rate for
284 * specified length of time.
285 */
286static int run_thread_tx(void *arg)
287{
288 test_globals_t *globals;
289 int thr_id;
290 odp_pktout_queue_t pktout;
291 pkt_tx_stats_t *stats;
292 odp_time_t cur_time, send_time_end, send_duration;
293 odp_time_t burst_gap_end, burst_gap;
294 uint32_t batch_len;
295 int unsent_pkts = 0;
296 odp_packet_t tx_packet[BATCH_LEN_MAX];
297 odp_time_t idle_start = ODP_TIME_NULL;
298
299 thread_args_t *targs = arg;
300
301 batch_len = targs->batch_len;
302
303 if (batch_len > BATCH_LEN_MAX)
304 batch_len = BATCH_LEN_MAX;
305
306 thr_id = odp_thread_id();
307
308 globals = odp_shm_addr(odp_shm_lookup("test_globals"));
309 stats = &globals->tx_stats[thr_id];
310
311 if (odp_pktout_queue(globals->pktio_tx, &pktout, 1) != 1)
312 ODPH_ABORT("Failed to get output queue for thread %d\n",
313 thr_id);
314
315 burst_gap = odp_time_local_from_ns(
316 ODP_TIME_SEC_IN_NS / (targs->pps / targs->batch_len));
317 send_duration =
318 odp_time_local_from_ns(targs->duration * ODP_TIME_SEC_IN_NS / T_SCALE);
319
320 odp_barrier_wait(&globals->tx_barrier);
321
322 cur_time = odp_time_local();
323 send_time_end = odp_time_sum(cur_time, send_duration);
324 burst_gap_end = cur_time;
325 while (odp_time_cmp(send_time_end, cur_time) > 0) {
326 unsigned alloc_cnt = 0, tx_cnt;
327
328 if (odp_time_cmp(burst_gap_end, cur_time) > 0) {
329 cur_time = odp_time_local();
330 if (!odp_time_cmp(idle_start, ODP_TIME_NULL))
331 idle_start = cur_time;
332 continue;
333 }
334
335 if (odp_time_cmp(idle_start, ODP_TIME_NULL) > 0) {
336 odp_time_t diff = odp_time_diff(cur_time, idle_start);
337
338 stats->idle_ticks =
339 odp_time_sum(diff, stats->idle_ticks);
340
341 idle_start = ODP_TIME_NULL;
342 }
343
344 burst_gap_end = odp_time_sum(burst_gap_end, burst_gap);
345
346 alloc_cnt = alloc_packets(tx_packet, batch_len - unsent_pkts);
347 if (alloc_cnt != batch_len)
348 stats->alloc_failures++;
349
350 tx_cnt = send_packets(pktout, tx_packet, alloc_cnt);
351 unsent_pkts = alloc_cnt - tx_cnt;
352 stats->enq_failures += unsent_pkts;
353 stats->tx_cnt += tx_cnt;
354
355 cur_time = odp_time_local();
356 }
357
358 if (gbl_args->args.verbose)
359 printf(" %02d: TxPkts %-8" PRIu64 " EnqFail %-6" PRIu64
360 " AllocFail %-6" PRIu64 " Idle %" PRIu64 "ms\n",
361 thr_id, stats->tx_cnt, stats->enq_failures,
362 stats->alloc_failures,
363 odp_time_to_ns(stats->idle_ticks) /
364 (uint64_t)ODP_TIME_MSEC_IN_NS);
365
366 return 0;
367}
368
369static int receive_packets(odp_queue_t queue,
370 odp_event_t *event_tbl, unsigned num_pkts)
371{
372 int n_ev = 0;
373
374 if (num_pkts == 0)
375 return 0;
376
377 if (queue != ODP_QUEUE_INVALID) {
378 if (num_pkts == 1) {
379 event_tbl[0] = odp_queue_deq(queue);
380 n_ev = event_tbl[0] != ODP_EVENT_INVALID;
381 } else {
382 n_ev = odp_queue_deq_multi(queue, event_tbl, num_pkts);
383 }
384 } else {
385 if (num_pkts == 1) {
386 event_tbl[0] = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
387 n_ev = event_tbl[0] != ODP_EVENT_INVALID;
388 } else {
390 event_tbl, num_pkts);
391 }
392 }
393 return n_ev;
394}
395
396static int run_thread_rx(void *arg)
397{
398 test_globals_t *globals;
399 int thr_id, batch_len;
401 odp_packet_t pkt;
402
403 thread_args_t *targs = arg;
404
405 batch_len = targs->batch_len;
406
407 if (batch_len > BATCH_LEN_MAX)
408 batch_len = BATCH_LEN_MAX;
409
410 thr_id = odp_thread_id();
411
412 globals = odp_shm_addr(odp_shm_lookup("test_globals"));
413
414 pkt_rx_stats_t *stats = &globals->rx_stats[thr_id];
415
416 if (gbl_args->args.schedule == 0) {
417 if (odp_pktin_event_queue(globals->pktio_rx, &queue, 1) != 1)
418 ODPH_ABORT("No input queue.\n");
419 }
420
421 odp_barrier_wait(&globals->rx_barrier);
422 while (1) {
423 odp_event_t ev[BATCH_LEN_MAX];
424 int i, n_ev;
425
426 n_ev = receive_packets(queue, ev, batch_len);
427
428 for (i = 0; i < n_ev; ++i) {
429 if (odp_event_type(ev[i]) == ODP_EVENT_PACKET) {
430 pkt = odp_packet_from_event(ev[i]);
431 if (pktio_pkt_has_magic(pkt))
432 stats->rx_cnt++;
433 else
434 stats->rx_ignore++;
435 }
436 odp_event_free(ev[i]);
437 }
438 if (n_ev == 0 && odp_atomic_load_u32(&gbl_args->shutdown))
439 break;
440 }
441
442 return 0;
443}
444
445/*
446 * Process the results from a single fixed rate test run to determine whether
447 * it passed or failed. Pass criteria are that the requested transmit packet
448 * rate was achieved and that all of the transmitted packets were received.
449 */
450static int process_results(uint64_t expected_tx_cnt,
451 test_status_t *status)
452{
453 int fail = 0;
454 uint64_t drops = 0;
455 uint64_t rx_pkts = 0;
456 uint64_t tx_pkts = 0;
457 uint64_t attempted_pps;
458 int i;
459 char str[512];
460 int len = 0;
461
462 for (i = 0; i < odp_thread_count_max(); ++i) {
463 rx_pkts += gbl_args->rx_stats[i].rx_cnt;
464 tx_pkts += gbl_args->tx_stats[i].tx_cnt;
465 }
466
467 if (rx_pkts == 0) {
468 ODPH_ERR("no packets received\n");
469 return -1;
470 }
471
472 if (tx_pkts < (expected_tx_cnt - (expected_tx_cnt / 100))) {
473 /* failed to transmit packets at (99% of) requested rate */
474 fail = 1;
475 } else if (tx_pkts > rx_pkts) {
476 /* failed to receive all of the transmitted packets */
477 fail = 1;
478 drops = tx_pkts - rx_pkts;
479 }
480
481 attempted_pps = status->pps_curr;
482
483 len += snprintf(&str[len], sizeof(str) - 1 - len,
484 "PPS: %-8" PRIu64 " ", attempted_pps);
485 len += snprintf(&str[len], sizeof(str) - 1 - len,
486 "Succeeded: %-4s ", fail ? "No" : "Yes");
487 len += snprintf(&str[len], sizeof(str) - 1 - len,
488 "TxPkts: %-8" PRIu64 " ", tx_pkts);
489 len += snprintf(&str[len], sizeof(str) - 1 - len,
490 "RxPkts: %-8" PRIu64 " ", rx_pkts);
491 len += snprintf(&str[len], sizeof(str) - 1 - len,
492 "DropPkts: %-8" PRIu64 " ", drops);
493 printf("%s\n", str);
494
495 if (gbl_args->args.search == 0) {
496 printf("Result: %s\n", fail ? "FAILED" : "PASSED");
497 return fail ? -1 : 0;
498 }
499
500 if (fail && (status->pps_fail == 0 ||
501 attempted_pps < status->pps_fail)) {
502 status->pps_fail = attempted_pps;
503 } else if (attempted_pps > status->pps_pass) {
504 status->pps_pass = attempted_pps;
505 }
506
507 if (status->pps_fail == 0) {
508 /* ramping up, double the previously attempted pps */
509 status->pps_curr *= 2;
510 } else {
511 /* set the new target to half way between the upper and lower
512 * limits */
513 status->pps_curr = status->pps_pass +
514 ((status->pps_fail - status->pps_pass) / 2);
515 }
516
517 /* stop once the pass and fail measurements are within range */
518 if ((status->pps_fail - status->pps_pass) < RATE_SEARCH_ACCURACY_PPS) {
519 unsigned pkt_len = gbl_args->args.pkt_len + PKT_HDR_LEN;
520 int mbps = (pkt_len * status->pps_pass * 8) / 1024 / 1024;
521
522 printf("Maximum packet rate: %" PRIu64 " PPS (%d Mbps)\n",
523 status->pps_pass, mbps);
524
525 return 0;
526 }
527
528 return 1;
529}
530
531static int setup_txrx_masks(odp_cpumask_t *thd_mask_tx,
532 odp_cpumask_t *thd_mask_rx)
533{
534 odp_cpumask_t cpumask;
535 int num_workers, num_tx_workers, num_rx_workers;
536 int i, cpu;
537
538 num_workers =
540 gbl_args->args.cpu_count);
541 if (num_workers < 2) {
542 ODPH_ERR("Need at least two cores\n");
543 return TEST_SKIP;
544 }
545
546 if (num_workers > MAX_WORKERS) {
547 ODPH_DBG("Worker count limited to MAX_WORKERS define (=%d)\n",
548 MAX_WORKERS);
549 num_workers = MAX_WORKERS;
550 }
551
552 if (gbl_args->args.num_tx_workers) {
553 if (gbl_args->args.num_tx_workers > (num_workers - 1)) {
554 ODPH_ERR("Invalid TX worker count\n");
555 return -1;
556 }
557 num_tx_workers = gbl_args->args.num_tx_workers;
558 } else {
559 /* default is to split the available cores evenly into TX and
560 * RX workers, favour TX for odd core count */
561 num_tx_workers = (num_workers + 1) / 2;
562 }
563
564 odp_cpumask_zero(thd_mask_tx);
565 odp_cpumask_zero(thd_mask_rx);
566
567 cpu = odp_cpumask_first(&cpumask);
568 for (i = 0; i < num_workers; ++i) {
569 if (i < num_tx_workers)
570 odp_cpumask_set(thd_mask_tx, cpu);
571 else
572 odp_cpumask_set(thd_mask_rx, cpu);
573 cpu = odp_cpumask_next(&cpumask, cpu);
574 }
575
576 num_rx_workers = odp_cpumask_count(thd_mask_rx);
577
578 odp_barrier_init(&gbl_args->rx_barrier, num_rx_workers + 1);
579 odp_barrier_init(&gbl_args->tx_barrier, num_tx_workers + 1);
580
581 return 0;
582}
583
584/*
585 * Run a single instance of the throughput test. When attempting to determine
586 * the maximum packet rate this will be invoked multiple times with the only
587 * difference between runs being the target PPS rate.
588 */
589static int run_test_single(odp_cpumask_t *thd_mask_tx,
590 odp_cpumask_t *thd_mask_rx,
591 test_status_t *status)
592{
593 odph_thread_t thread_tbl[MAX_WORKERS];
594 odph_thread_common_param_t thr_common;
595 odph_thread_param_t thr_param;
596 thread_args_t args_tx, args_rx;
597 uint64_t expected_tx_cnt;
598 int num_tx_workers, num_rx_workers;
599
600 odp_atomic_store_u32(&gbl_args->shutdown, 0);
601
602 memset(thread_tbl, 0, sizeof(thread_tbl));
603 memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size);
604 memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size);
605
606 expected_tx_cnt = status->pps_curr * gbl_args->args.duration / T_SCALE;
607
608 /* start receiver threads first */
609
610 num_rx_workers = odp_cpumask_count(thd_mask_rx);
611 args_rx.batch_len = gbl_args->args.rx_batch_len;
612
613 odph_thread_common_param_init(&thr_common);
614 thr_common.instance = gbl_args->instance;
615 thr_common.cpumask = thd_mask_rx;
616 thr_common.share_param = 1;
617
618 odph_thread_param_init(&thr_param);
619 thr_param.start = run_thread_rx;
620 thr_param.arg = &args_rx;
621 thr_param.thr_type = ODP_THREAD_WORKER;
622
623 odph_thread_create(thread_tbl, &thr_common, &thr_param, num_rx_workers);
624 odp_barrier_wait(&gbl_args->rx_barrier);
625
626 /* then start transmitters */
627
628 num_tx_workers = odp_cpumask_count(thd_mask_tx);
629 args_tx.pps = status->pps_curr / num_tx_workers;
630 args_tx.duration = gbl_args->args.duration;
631 args_tx.batch_len = gbl_args->args.tx_batch_len;
632
633 odph_thread_common_param_init(&thr_common);
634 thr_common.instance = gbl_args->instance;
635 thr_common.cpumask = thd_mask_tx;
636 thr_common.share_param = 1;
637
638 odph_thread_param_init(&thr_param);
639 thr_param.start = run_thread_tx;
640 thr_param.arg = &args_tx;
641 thr_param.thr_type = ODP_THREAD_WORKER;
642
643 odph_thread_create(&thread_tbl[num_rx_workers], &thr_common, &thr_param, num_tx_workers);
644 odp_barrier_wait(&gbl_args->tx_barrier);
645
646 /* wait for transmitter threads to terminate */
647 odph_thread_join(&thread_tbl[num_rx_workers], num_tx_workers);
648
649 /* delay to allow transmitted packets to reach the receivers */
650 odp_time_wait_ns(SHUTDOWN_DELAY_NS);
651
652 /* indicate to the receivers to exit */
653 odp_atomic_store_u32(&gbl_args->shutdown, 1);
654
655 /* wait for receivers */
656 odph_thread_join(thread_tbl, num_rx_workers);
657
658 if (!status->warmup)
659 return process_results(expected_tx_cnt, status);
660
661 return 1;
662}
663
664static int run_test(void)
665{
666 int ret;
667 int i;
668 odp_cpumask_t txmask, rxmask;
669 test_status_t status = {
670 .pps_curr = gbl_args->args.pps,
671 .pps_pass = 0,
672 .pps_fail = 0,
673 .warmup = 1,
674 };
675
676 ret = setup_txrx_masks(&txmask, &rxmask);
677 if (ret)
678 return ret;
679
680 printf("Starting test with params:\n");
681 printf("\tTransmit workers: \t%d\n", odp_cpumask_count(&txmask));
682 printf("\tReceive workers: \t%d\n", odp_cpumask_count(&rxmask));
683 printf("\tDuration (seconds): \t%d\n", gbl_args->args.duration);
684 printf("\tTransmit batch length:\t%" PRIu32 "\n",
685 gbl_args->args.tx_batch_len);
686 printf("\tReceive batch length: \t%" PRIu32 "\n",
687 gbl_args->args.rx_batch_len);
688 printf("\tPacket receive method:\t%s\n",
689 gbl_args->args.schedule ? "schedule" : "plain");
690 printf("\tInterface(s): \t");
691 for (i = 0; i < gbl_args->args.num_ifaces; ++i)
692 printf("%s ", gbl_args->args.ifaces[i]);
693 printf("\n");
694
695 /* first time just run the test but throw away the results */
696 run_test_single(&txmask, &rxmask, &status);
697 status.warmup = 0;
698
699 while (1) {
700 ret = run_test_single(&txmask, &rxmask, &status);
701 if (ret <= 0)
702 break;
703 }
704
705 return ret;
706}
707
708static odp_pktio_t create_pktio(const char *iface, int schedule)
709{
710 odp_pool_t pool;
711 odp_pktio_t pktio;
712 char pool_name[ODP_POOL_NAME_LEN];
713 odp_pool_param_t params;
714 odp_pktio_param_t pktio_param;
715
716 odp_pool_param_init(&params);
717 params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len;
718 params.pkt.seg_len = params.pkt.len;
719 params.pkt.num = PKT_BUF_NUM;
720 params.type = ODP_POOL_PACKET;
721
722 snprintf(pool_name, sizeof(pool_name), "pkt_pool_%s", iface);
723 pool = odp_pool_create(pool_name, &params);
724 if (pool == ODP_POOL_INVALID)
725 return ODP_PKTIO_INVALID;
726
727 odp_pktio_param_init(&pktio_param);
728
729 if (schedule)
730 pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
731 else
732 pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
733
734 pktio = odp_pktio_open(iface, pool, &pktio_param);
735
736 return pktio;
737}
738
739static int test_init(void)
740{
741 odp_pool_param_t params;
742 const char *iface;
743 int schedule;
745
746 odp_pool_param_init(&params);
747 params.pkt.len = PKT_HDR_LEN + gbl_args->args.pkt_len;
748 params.pkt.seg_len = params.pkt.len;
749 params.pkt.num = PKT_BUF_NUM;
750 params.type = ODP_POOL_PACKET;
751
752 gbl_args->transmit_pkt_pool = odp_pool_create("pkt_pool_transmit",
753 &params);
754 if (gbl_args->transmit_pkt_pool == ODP_POOL_INVALID)
755 ODPH_ABORT("Failed to create transmit pool\n");
756
757 odp_atomic_init_u32(&gbl_args->ip_seq, 0);
758 odp_atomic_init_u32(&gbl_args->shutdown, 0);
759
760 iface = gbl_args->args.ifaces[0];
761 schedule = gbl_args->args.schedule;
762
763 if (schedule)
765
766 /* create pktios and associate input/output queues */
767 gbl_args->pktio_tx = create_pktio(iface, schedule);
768 if (gbl_args->args.num_ifaces > 1) {
769 iface = gbl_args->args.ifaces[1];
770 gbl_args->pktio_rx = create_pktio(iface, schedule);
771 } else {
772 gbl_args->pktio_rx = gbl_args->pktio_tx;
773 }
774
775 odp_pktio_mac_addr(gbl_args->pktio_tx, gbl_args->src_mac,
776 ODPH_ETHADDR_LEN);
777 odp_pktio_mac_addr(gbl_args->pktio_rx, gbl_args->dst_mac,
778 ODPH_ETHADDR_LEN);
779
780 if (gbl_args->pktio_rx == ODP_PKTIO_INVALID ||
781 gbl_args->pktio_tx == ODP_PKTIO_INVALID) {
782 ODPH_ERR("failed to open pktio\n");
783 return -1;
784 }
785
786 /* Create single queue with default parameters */
787 if (odp_pktout_queue_config(gbl_args->pktio_tx, NULL)) {
788 ODPH_ERR("failed to configure pktio_tx queue\n");
789 return -1;
790 }
791
792 /* Configure also input side (with defaults) */
793 if (odp_pktin_queue_config(gbl_args->pktio_tx, NULL)) {
794 ODPH_ERR("failed to configure pktio_tx queue\n");
795 return -1;
796 }
797
798 /* Disable packet parsing as this is done in the driver where it
799 * affects scalability.
800 */
803 odp_pktio_config(gbl_args->pktio_rx, &cfg);
804
805 if (gbl_args->args.num_ifaces > 1) {
806 if (odp_pktout_queue_config(gbl_args->pktio_rx, NULL)) {
807 ODPH_ERR("failed to configure pktio_rx queue\n");
808 return -1;
809 }
810
811 if (odp_pktin_queue_config(gbl_args->pktio_rx, NULL)) {
812 ODPH_ERR("failed to configure pktio_rx queue\n");
813 return -1;
814 }
815 }
816
817 if (odp_pktio_start(gbl_args->pktio_tx) != 0)
818 return -1;
819 if (gbl_args->args.num_ifaces > 1 &&
820 odp_pktio_start(gbl_args->pktio_rx))
821 return -1;
822
823 return 0;
824}
825
826static int empty_inq(odp_pktio_t pktio)
827{
829 odp_event_t ev;
830 odp_queue_type_t q_type;
831
832 if (odp_pktin_event_queue(pktio, &queue, 1) != 1)
833 return -1;
834
835 q_type = odp_queue_type(queue);
836
837 /* flush any pending events */
838 while (1) {
839 if (q_type == ODP_QUEUE_TYPE_PLAIN)
840 ev = odp_queue_deq(queue);
841 else
843
844 if (ev != ODP_EVENT_INVALID)
845 odp_event_free(ev);
846 else
847 break;
848 }
849
850 return 0;
851}
852
853static int test_term(void)
854{
855 char pool_name[ODP_POOL_NAME_LEN];
856 odp_pool_t pool;
857 int i;
858 int ret = 0;
859
860 if (gbl_args->pktio_tx != gbl_args->pktio_rx) {
861 if (odp_pktio_stop(gbl_args->pktio_tx)) {
862 ODPH_ERR("Failed to stop pktio_tx\n");
863 return -1;
864 }
865
866 if (odp_pktio_close(gbl_args->pktio_tx)) {
867 ODPH_ERR("Failed to close pktio_tx\n");
868 ret = -1;
869 }
870 }
871
872 empty_inq(gbl_args->pktio_rx);
873
874 if (odp_pktio_stop(gbl_args->pktio_rx)) {
875 ODPH_ERR("Failed to stop pktio_rx\n");
876 return -1;
877 }
878
879 if (odp_pktio_close(gbl_args->pktio_rx) != 0) {
880 ODPH_ERR("Failed to close pktio_rx\n");
881 ret = -1;
882 }
883
884 for (i = 0; i < gbl_args->args.num_ifaces; ++i) {
885 snprintf(pool_name, sizeof(pool_name),
886 "pkt_pool_%s", gbl_args->args.ifaces[i]);
887 pool = odp_pool_lookup(pool_name);
888 if (pool == ODP_POOL_INVALID)
889 continue;
890
891 if (odp_pool_destroy(pool) != 0) {
892 ODPH_ERR("Failed to destroy pool %s\n", pool_name);
893 ret = -1;
894 }
895 }
896
897 if (odp_pool_destroy(gbl_args->transmit_pkt_pool) != 0) {
898 ODPH_ERR("Failed to destroy transmit pool\n");
899 ret = -1;
900 }
901
902 free(gbl_args->args.if_str);
903
904 if (odp_shm_free(odp_shm_lookup("test_globals")) != 0) {
905 ODPH_ERR("Failed to free test_globals\n");
906 ret = -1;
907 }
908 if (odp_shm_free(odp_shm_lookup("test_globals.rx_stats")) != 0) {
909 ODPH_ERR("Failed to free test_globals.rx_stats\n");
910 ret = -1;
911 }
912 if (odp_shm_free(odp_shm_lookup("test_globals.tx_stats")) != 0) {
913 ODPH_ERR("Failed to free test_globals.tx_stats\n");
914 ret = -1;
915 }
916
917 return ret;
918}
919
920static void usage(void)
921{
922 printf("\nUsage: odp_pktio_perf [options]\n\n");
923 printf(" -c, --count <number> CPU count, 0=all available, default=2\n");
924 printf(" -t, --txcount <number> Number of CPUs to use for TX\n");
925 printf(" default: cpu_count+1/2\n");
926 printf(" -b, --txbatch <length> Number of packets per TX batch\n");
927 printf(" default: %d\n", BATCH_LEN_MAX);
928 printf(" -p, --plain Plain input queue for packet RX\n");
929 printf(" default: disabled (use scheduler)\n");
930 printf(" -R, --rxbatch <length> Number of packets per RX batch\n");
931 printf(" default: %d\n", BATCH_LEN_MAX);
932 printf(" -l, --length <length> Additional payload length in bytes\n");
933 printf(" default: 0\n");
934 printf(" -r, --rate <number> Attempted packet rate in PPS\n");
935 printf(" -i, --interface <list> List of interface names to use\n");
936 printf(" -d, --duration <secs> Duration of each test iteration\n");
937 printf(" -v, --verbose Print verbose information\n");
938 printf(" -h, --help This help\n");
939 printf("\n");
940}
941
942static void parse_args(int argc, char *argv[], test_args_t *args)
943{
944 int opt;
945
946 static const struct option longopts[] = {
947 {"count", required_argument, NULL, 'c'},
948 {"txcount", required_argument, NULL, 't'},
949 {"txbatch", required_argument, NULL, 'b'},
950 {"plain", no_argument, NULL, 'p'},
951 {"rxbatch", required_argument, NULL, 'R'},
952 {"length", required_argument, NULL, 'l'},
953 {"rate", required_argument, NULL, 'r'},
954 {"interface", required_argument, NULL, 'i'},
955 {"duration", required_argument, NULL, 'd'},
956 {"verbose", no_argument, NULL, 'v'},
957 {"help", no_argument, NULL, 'h'},
958 {NULL, 0, NULL, 0}
959 };
960
961 static const char *shortopts = "+c:t:b:pR:l:r:i:d:vh";
962
963 args->cpu_count = 2;
964 args->num_tx_workers = 0; /* defaults to cpu_count+1/2 */
965 args->tx_batch_len = BATCH_LEN_MAX;
966 args->rx_batch_len = BATCH_LEN_MAX;
967 args->duration = DEFAULT_DURATION;
968 args->pps = RATE_SEARCH_INITIAL_PPS;
969 args->search = 1;
970 args->schedule = 1;
971 args->verbose = 0;
972
973 while (1) {
974 opt = getopt_long(argc, argv, shortopts,
975 longopts, NULL);
976
977 if (opt == -1)
978 break;
979
980 switch (opt) {
981 case 'h':
982 usage();
983 exit(EXIT_SUCCESS);
984 case 'c':
985 args->cpu_count = atoi(optarg);
986 break;
987 case 't':
988 args->num_tx_workers = atoi(optarg);
989 break;
990 case 'd':
991 args->duration = atoi(optarg) * T_SCALE;
992 break;
993 case 'r':
994 args->pps = atoi(optarg);
995 args->search = 0;
996 args->verbose = 1;
997 break;
998 case 'i':
999 {
1000 char *token;
1001
1002 args->if_str = malloc(strlen(optarg) + 1);
1003
1004 if (!args->if_str)
1005 ODPH_ABORT("Failed to alloc iface storage\n");
1006
1007 strcpy(args->if_str, optarg);
1008 for (token = strtok(args->if_str, ",");
1009 token != NULL && args->num_ifaces < MAX_NUM_IFACES;
1010 token = strtok(NULL, ","))
1011 args->ifaces[args->num_ifaces++] = token;
1012 }
1013 break;
1014 case 'p':
1015 args->schedule = 0;
1016 break;
1017 case 'b':
1018 args->tx_batch_len = atoi(optarg);
1019 break;
1020 case 'R':
1021 args->rx_batch_len = atoi(optarg);
1022 break;
1023 case 'v':
1024 args->verbose = 1;
1025 break;
1026 case 'l':
1027 args->pkt_len = atoi(optarg);
1028 break;
1029 }
1030 }
1031
1032 if (args->num_ifaces == 0) {
1033 args->ifaces[0] = "loop";
1034 args->num_ifaces = 1;
1035 }
1036}
1037
1038int main(int argc, char **argv)
1039{
1040 int ret;
1041 odp_shm_t shm;
1042 int max_thrs;
1043 odph_helper_options_t helper_options;
1044 odp_instance_t instance;
1045 odp_init_t init_param;
1046
1047 /* Let helper collect its own arguments (e.g. --odph_proc) */
1048 argc = odph_parse_options(argc, argv);
1049 if (odph_options(&helper_options)) {
1050 ODPH_ERR("Error: reading ODP helper options failed.\n");
1051 exit(EXIT_FAILURE);
1052 }
1053
1054 odp_init_param_init(&init_param);
1055 init_param.mem_model = helper_options.mem_model;
1056
1057 if (odp_init_global(&instance, &init_param, NULL) != 0)
1058 ODPH_ABORT("Failed global init.\n");
1059
1060 if (odp_init_local(instance, ODP_THREAD_CONTROL) != 0)
1061 ODPH_ABORT("Failed local init.\n");
1062
1064
1065 shm = odp_shm_reserve("test_globals",
1066 sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
1067 if (shm == ODP_SHM_INVALID)
1068 ODPH_ABORT("Shared memory reserve failed.\n");
1069
1070 gbl_args = odp_shm_addr(shm);
1071 if (gbl_args == NULL)
1072 ODPH_ABORT("Shared memory reserve failed.\n");
1073 memset(gbl_args, 0, sizeof(test_globals_t));
1074
1075 max_thrs = odp_thread_count_max();
1076
1077 gbl_args->instance = instance;
1078 gbl_args->rx_stats_size = max_thrs * sizeof(pkt_rx_stats_t);
1079 gbl_args->tx_stats_size = max_thrs * sizeof(pkt_tx_stats_t);
1080
1081 shm = odp_shm_reserve("test_globals.rx_stats",
1082 gbl_args->rx_stats_size,
1083 ODP_CACHE_LINE_SIZE, 0);
1084 if (shm == ODP_SHM_INVALID)
1085 ODPH_ABORT("Shared memory reserve failed.\n");
1086
1087 gbl_args->rx_stats = odp_shm_addr(shm);
1088
1089 if (gbl_args->rx_stats == NULL)
1090 ODPH_ABORT("Shared memory reserve failed.\n");
1091
1092 memset(gbl_args->rx_stats, 0, gbl_args->rx_stats_size);
1093
1094 shm = odp_shm_reserve("test_globals.tx_stats",
1095 gbl_args->tx_stats_size,
1096 ODP_CACHE_LINE_SIZE, 0);
1097 if (shm == ODP_SHM_INVALID)
1098 ODPH_ABORT("Shared memory reserve failed.\n");
1099
1100 gbl_args->tx_stats = odp_shm_addr(shm);
1101
1102 if (gbl_args->tx_stats == NULL)
1103 ODPH_ABORT("Shared memory reserve failed.\n");
1104
1105 memset(gbl_args->tx_stats, 0, gbl_args->tx_stats_size);
1106
1107 parse_args(argc, argv, &gbl_args->args);
1108
1109 ret = test_init();
1110
1111 if (ret == 0) {
1112 ret = run_test();
1113 test_term();
1114 }
1115
1117 odp_term_global(instance);
1118
1119 return ret;
1120}
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.
uint32_t odp_atomic_fetch_add_u32(odp_atomic_u32_t *atom, uint32_t val)
Fetch and add to 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_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
uint32_t odp_u32be_t
unsigned 32bit big endian
odp_u16be_t odp_cpu_to_be_16(uint16_t cpu16)
Convert cpu native uint16_t to 16bit big endian.
#define odp_likely(x)
Branch likely taken.
Definition spec/hints.h:59
odp_u32be_t odp_cpu_to_be_32(uint32_t cpu32)
Convert cpu native uint32_t to 32bit big endian.
void odp_cpumask_set(odp_cpumask_t *mask, int cpu)
Add CPU to mask.
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_next(const odp_cpumask_t *mask, int cpu)
Find next set CPU in mask.
void odp_cpumask_zero(odp_cpumask_t *mask)
Clear entire CPU mask.
int odp_cpumask_count(const odp_cpumask_t *mask)
Count number of CPUs set in mask.
void odp_event_free(odp_event_t event)
Free event.
odp_event_type_t odp_event_type(odp_event_t event)
Event type of an 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.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
int odp_pktio_mac_addr(odp_pktio_t pktio, void *mac_addr, int size)
Get the default MAC address of a packet IO interface.
void odp_pktio_param_init(odp_pktio_param_t *param)
Initialize pktio params.
int odp_pktio_close(odp_pktio_t pktio)
Close a packet IO interface.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
int odp_pktin_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
Event queues for packet input.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_config(odp_pktio_t pktio, const odp_pktio_config_t *config)
Configure packet IO interface options.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
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_PKTIN_MODE_QUEUE
Packet input through plain event queues.
@ ODP_PKTIN_MODE_SCHED
Packet input through scheduler and scheduled event queues.
int odp_packet_l3_offset_set(odp_packet_t pkt, uint32_t offset)
Set layer 3 start offset.
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.
odp_packet_t odp_packet_alloc(odp_pool_t pool, uint32_t len)
Allocate a packet from a packet pool.
int odp_packet_l2_offset_set(odp_packet_t pkt, uint32_t offset)
Set layer 2 start offset.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
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.
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_PROTO_LAYER_NONE
No layers.
#define ODP_POOL_NAME_LEN
Maximum pool name length, including the null character.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
odp_pool_t odp_pool_lookup(const char *name)
Find a pool by name.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_event_t odp_queue_deq(odp_queue_t queue)
Dequeue an event from a queue.
odp_queue_type_t
Queue type.
odp_queue_type_t odp_queue_type(odp_queue_t queue)
Queue type.
int odp_queue_deq_multi(odp_queue_t queue, odp_event_t events[], int num)
Dequeue multiple events from a queue.
@ ODP_QUEUE_TYPE_PLAIN
Plain queue.
int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[], int num)
Schedule multiple events.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
odp_shm_t odp_shm_lookup(const char *name)
Lookup for a block of shared memory.
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.
void odp_sys_info_print(void)
Print system info.
int odp_thread_count_max(void)
Maximum thread count.
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.
odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
Time difference.
odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2)
Time sum.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
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.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
int odp_time_cmp(odp_time_t t2, odp_time_t t1)
Compare two times.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Packet IO configuration options.
odp_pktio_parser_config_t parser
Packet input parser configuration.
Packet IO parameters.
odp_pktin_mode_t in_mode
Packet input mode.
odp_proto_layer_t layer
Protocol parsing level in packet input.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...