API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_sched_pktio.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018 Linaro Limited
3 * Copyright (c) 2025-2026 Nokia
4 */
5
14#ifndef _GNU_SOURCE
15#define _GNU_SOURCE /* for sigaction and pthread_sigmask */
16#endif
17
18#include <stdio.h>
19#include <string.h>
20#include <signal.h>
21#include <stdlib.h>
22#include <stdint.h>
23#include <inttypes.h>
24#include <pthread.h>
25#include <time.h>
26
27#include <odp_api.h>
28#include <odp/helper/odph_api.h>
29#include <pktio_common.h>
30
31#define DEBUG_PRINT 0
32#define MAX_WORKERS 64
33#define MAX_PKTIOS (ODP_PKTIO_MAX_INDEX + 1)
34#define MAX_PKTIO_NAME 31
35#define MAX_PKTIO_QUEUES MAX_WORKERS
36#define MAX_PIPE_STAGES 64
37#define MAX_PIPE_QUEUES 1024
38#define MAX_PKT_LEN 1514
39#define MAX_PKT_NUM (16 * 1024)
40#define MIN_PKT_SEG_LEN 64
41#define TEST_PASSED_LIMIT 5000
42#define SCHED_MODE_PARAL 1
43#define SCHED_MODE_ATOMIC 2
44#define SCHED_MODE_ORDER 3
45
46typedef struct test_options_t {
47 long int timeout_us;
48 int sched_mode;
49 int num_worker;
50 int num_pktio;
51 int num_pktio_queue;
52 int burst_size;
53 int pipe_stages;
54 int pipe_queues;
55 uint32_t pipe_queue_size;
56 uint8_t collect_stat;
57 uint32_t wait_sec;
58 char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1];
59
60} test_options_t;
61
62typedef struct {
63 int worker_id;
64 void *test_global_ptr;
65} worker_arg_t;
66
67typedef struct ODP_ALIGNED_CACHE {
68 uint64_t rx_pkt;
69 uint64_t tx_pkt;
70 uint64_t pipe_pkt;
71 uint64_t tx_drop;
72 uint64_t pipe_drop;
73 uint64_t tmo;
74} worker_stat_t;
75
76typedef struct pktin_queue_context_t {
77 /* Queue context must start with stage and idx */
78 uint16_t stage;
79 uint16_t queue_idx;
80
81 uint8_t dst_pktio;
82 uint8_t dst_queue;
83 uint8_t src_pktio;
84 uint8_t src_queue;
85 odp_pktout_queue_t dst_pktout;
86} pktin_queue_context_t;
87
88typedef struct pipe_queue_context_t {
89 /* Queue context must start with stage and idx. */
90 uint16_t stage;
91 uint16_t queue_idx;
92} pipe_queue_context_t;
93
94typedef struct {
95 odp_atomic_u32_t stop_workers;
96 odp_barrier_t worker_start;
97
98 test_options_t opt;
99
100 int max_workers;
101 odp_cpumask_t cpumask;
102 odp_instance_t instance;
103
104 int worker_cpu[MAX_WORKERS];
105
106 odp_pool_t pool;
107 uint32_t pkt_len;
108 uint32_t pkt_num;
109
110 struct {
111 odp_pktio_t pktio;
112 int pktio_index;
113 int started;
114 odph_ethaddr_t my_addr;
115 odp_queue_t input_queue[MAX_PKTIO_QUEUES];
116 odp_pktout_queue_t pktout[MAX_PKTIO_QUEUES];
117 pktin_queue_context_t queue_context[MAX_PKTIO_QUEUES];
118
119 } pktio[MAX_PKTIOS];
120
121 struct {
122 odp_timer_pool_t timer_pool;
123 odp_pool_t timeout_pool;
124 uint64_t timeout_tick;
125 odp_timer_t timer[MAX_PKTIOS][MAX_PKTIO_QUEUES];
126
127 } timer;
128
129 struct {
130 odp_queue_t queue[MAX_PIPE_QUEUES];
131 } pipe_queue[MAX_PKTIOS][MAX_PIPE_STAGES];
132
133 struct {
134 pipe_queue_context_t ctx;
135 } pipe_queue_ctx[MAX_PIPE_STAGES][MAX_PIPE_QUEUES];
136
137 worker_arg_t worker_arg[MAX_WORKERS];
138
139 worker_stat_t worker_stat[MAX_WORKERS];
140 uint64_t rx_pkt_sum;
141 uint64_t tx_pkt_sum;
142
143 odp_schedule_config_t schedule_config;
144
145} test_global_t;
146
148enum longopt_only {
149 OPT_WAIT_LINK = 256
150};
151
152static test_global_t *test_global;
153
154static volatile sig_atomic_t sigint_received;
155
156static inline void set_dst_eth_addr(odph_ethaddr_t *eth_addr, int index)
157{
158 eth_addr->addr[0] = 0x02;
159 eth_addr->addr[1] = 0;
160 eth_addr->addr[2] = 0;
161 eth_addr->addr[3] = 0;
162 eth_addr->addr[4] = 0;
163 eth_addr->addr[5] = index;
164}
165
166static inline void fill_eth_addr(odp_packet_t pkt[], int num,
167 test_global_t *test_global, int out)
168{
169 odph_ethhdr_t *eth;
170 int i;
171
172 for (i = 0; i < num; ++i) {
173 eth = odp_packet_data(pkt[i]);
174
175 eth->src = test_global->pktio[out].my_addr;
176 set_dst_eth_addr(&eth->dst, out);
177 }
178}
179
180static inline void send_packets(test_global_t *test_global,
181 odp_packet_t pkt[], int num_pkt,
182 int output, odp_pktout_queue_t pktout,
183 int worker_id)
184{
185 int sent, drop;
186
187 fill_eth_addr(pkt, num_pkt, test_global, output);
188
189 sent = odp_pktout_send(pktout, pkt, num_pkt);
190
191 if (odp_unlikely(sent < 0))
192 sent = 0;
193
194 drop = num_pkt - sent;
195
196 if (odp_unlikely(drop > 0))
197 odp_packet_free_multi(&pkt[sent], drop);
198
199 if (odp_unlikely(test_global->opt.collect_stat)) {
200 test_global->worker_stat[worker_id].tx_pkt += sent;
201 test_global->worker_stat[worker_id].tx_drop += drop;
202 }
203}
204
205static int worker_thread_direct(void *arg)
206{
207 int num_pkt, out;
208 odp_pktout_queue_t pktout;
209 odp_queue_t queue;
210 pktin_queue_context_t *queue_context;
211 worker_arg_t *worker_arg = arg;
212 test_global_t *test_global = worker_arg->test_global_ptr;
213 int worker_id = worker_arg->worker_id;
214 int burst_size = test_global->opt.burst_size;
215
216 printf("Worker %i started\n", worker_id);
217
218 /* Wait for other workers to start */
219 odp_barrier_wait(&test_global->worker_start);
220
221 while (1) {
222 odp_event_t ev[burst_size];
223 odp_packet_t pkt[burst_size];
224
225 if (odp_atomic_load_u32(&test_global->stop_workers))
226 break;
227
228 num_pkt = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
229 ev, burst_size);
230
231 if (num_pkt <= 0)
232 continue;
233
234 queue_context = odp_queue_context(queue);
235
236 if (DEBUG_PRINT)
237 printf("worker %i: [%i/%i] -> [%i/%i], %i packets\n",
238 worker_id,
239 queue_context->src_pktio,
240 queue_context->src_queue,
241 queue_context->dst_pktio,
242 queue_context->dst_queue, num_pkt);
243
244 odp_packet_from_event_multi(pkt, ev, num_pkt);
245
246 pktout = queue_context->dst_pktout;
247 out = queue_context->dst_pktio;
248
249 send_packets(test_global, pkt, num_pkt, out, pktout, worker_id);
250
251 if (odp_unlikely(test_global->opt.collect_stat))
252 test_global->worker_stat[worker_id].rx_pkt += num_pkt;
253 }
254
255 /*
256 * Free prefetched packets before exiting worker thread as
257 * such packets can block main thread event cleanup or
258 * cause buffer leak.
259 */
261 while (1) {
262 odp_event_t ev;
263
265 if (ev == ODP_EVENT_INVALID)
266 break;
267 odp_event_free(ev);
268 }
269
270 /* Non-prefetched events in scheduler are cleaned up by main thread */
271 printf("Worker %i stopped\n", worker_id);
272
273 return 0;
274}
275
276static inline void enqueue_events(odp_queue_t dst_queue, odp_event_t ev[],
277 int num, int worker_id)
278{
279 int sent, drop;
280
281 sent = odp_queue_enq_multi(dst_queue, ev, num);
282
283 if (odp_unlikely(sent < 0))
284 sent = 0;
285
286 drop = num - sent;
287
288 if (odp_unlikely(drop))
289 odp_event_free_multi(&ev[sent], drop);
290
291 if (odp_unlikely(test_global->opt.collect_stat))
292 test_global->worker_stat[worker_id].pipe_drop += drop;
293}
294
295static inline odp_queue_t next_queue(test_global_t *test_global, int input,
296 uint16_t stage, uint16_t queue_idx)
297{
298 return test_global->pipe_queue[input][stage].queue[queue_idx];
299}
300
301static int worker_thread_pipeline(void *arg)
302{
303 int i, num_pkt, input, output, output_queue;
304 odp_queue_t queue, dst_queue;
305 odp_pktout_queue_t pktout;
306 pipe_queue_context_t *pipe_context;
307 uint16_t stage, queue_idx;
308 worker_arg_t *worker_arg = arg;
309 test_global_t *test_global = worker_arg->test_global_ptr;
310 int worker_id = worker_arg->worker_id;
311 int pipe_stages = test_global->opt.pipe_stages;
312 int pipe_queues = test_global->opt.pipe_queues;
313 int num_pktio = test_global->opt.num_pktio;
314 int num_pktio_queue = test_global->opt.num_pktio_queue;
315 int burst_size = test_global->opt.burst_size;
316
317 printf("Worker %i started\n", worker_id);
318
319 /* Wait for other workers to start */
320 odp_barrier_wait(&test_global->worker_start);
321
322 while (1) {
323 odp_event_t ev[burst_size];
324 odp_packet_t pkt[burst_size];
325
326 if (odp_atomic_load_u32(&test_global->stop_workers))
327 break;
328
329 num_pkt = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
330 ev, burst_size);
331
332 if (num_pkt <= 0)
333 continue;
334
335 pipe_context = odp_queue_context(queue);
336 stage = pipe_context->stage;
337 queue_idx = pipe_context->queue_idx;
338
339 /* A queue is connected to a single input interface. All
340 * packets from a queue are from the same interface. */
342
343 if (DEBUG_PRINT)
344 printf("worker %i: stage %u, idx %u, %i packets\n",
345 worker_id, stage, queue_idx, num_pkt);
346
347 if (stage == 0) {
348 if (odp_unlikely(test_global->opt.collect_stat))
349 test_global->worker_stat[worker_id].rx_pkt +=
350 num_pkt;
351
352 /* The first stage (packet input). Forward packet flows
353 * into first pipeline queues. */
354 if (pipe_queues > num_pktio_queue) {
355 /* More pipeline queues than input queues.
356 * Use flow hash to spread flows into pipeline
357 * queues. */
358 odp_packet_t p;
359 worker_stat_t *stat;
360 uint32_t hash;
361 uint16_t idx;
362 int drop = 0;
363
364 stat = &test_global->worker_stat[worker_id];
365
366 for (i = 0; i < num_pkt; i++) {
367 p = odp_packet_from_event(ev[i]);
368 hash = odp_packet_flow_hash(p);
369 idx = queue_idx;
370
372 idx = hash % pipe_queues;
373
374 dst_queue = next_queue(test_global,
375 input, stage,
376 idx);
377
378 if (odp_queue_enq(dst_queue, ev[i])) {
379 odp_event_free(ev[i]);
380 drop++;
381 }
382 }
383
384 if (odp_unlikely(test_global->opt.collect_stat))
385 stat->pipe_drop += drop;
386 } else {
387 queue_idx = queue_idx % pipe_queues;
388 dst_queue = next_queue(test_global, input,
389 stage, queue_idx);
390
391 enqueue_events(dst_queue, ev, num_pkt,
392 worker_id);
393 }
394 continue;
395 }
396
397 if (stage < pipe_stages) {
398 /* Middle stages */
399 dst_queue = next_queue(test_global, input, stage,
400 queue_idx);
401 enqueue_events(dst_queue, ev, num_pkt, worker_id);
402
403 if (odp_unlikely(test_global->opt.collect_stat))
404 test_global->worker_stat[worker_id].pipe_pkt +=
405 num_pkt;
406
407 continue;
408 }
409
410 /* The last stage, send packets out */
411 odp_packet_from_event_multi(pkt, ev, num_pkt);
412
413 /* If single interface loopback, otherwise forward to the next
414 * interface. */
415 output = (input + 1) % num_pktio;
416 output_queue = queue_idx % num_pktio_queue;
417 pktout = test_global->pktio[output].pktout[output_queue];
418
419 send_packets(test_global, pkt, num_pkt, output, pktout,
420 worker_id);
421 }
422
423 printf("Worker %i stopped\n", worker_id);
424
425 return 0;
426}
427
428static int worker_thread_timers(void *arg)
429{
430 int num, num_pkt, out, tmos, i, src_pktio, src_queue;
431 odp_pktout_queue_t pktout;
432 odp_queue_t queue;
433 pktin_queue_context_t *queue_context;
434 odp_timer_t timer;
436 odp_timer_start_t start_param;
437 worker_arg_t *worker_arg = arg;
438 test_global_t *test_global = worker_arg->test_global_ptr;
439 int worker_id = worker_arg->worker_id;
440 int burst_size = test_global->opt.burst_size;
441 uint64_t tick = test_global->timer.timeout_tick;
442
443 printf("Worker (timers) %i started\n", worker_id);
444
445 /* Wait for other workers to start */
446 odp_barrier_wait(&test_global->worker_start);
447
448 while (1) {
449 odp_event_t ev[burst_size];
450 odp_packet_t pkt[burst_size];
451
452 if (odp_atomic_load_u32(&test_global->stop_workers))
453 break;
454
456 ev, burst_size);
457
458 if (num <= 0)
459 continue;
460
461 tmos = 0;
462 queue_context = odp_queue_context(queue);
463 src_pktio = queue_context->src_pktio;
464 src_queue = queue_context->src_queue;
465 timer = test_global->timer.timer[src_pktio][src_queue];
466 start_param.tick_type = ODP_TIMER_TICK_REL;
467 start_param.tick = tick;
468 start_param.tmo_ev = ODP_EVENT_INVALID;
469
470 for (i = 0; i < num; i++) {
471 if (odp_unlikely(odp_event_type(ev[i]) ==
472 ODP_EVENT_TIMEOUT)) {
473 tmos++;
474
475 start_param.tmo_ev = ev[i];
476 ret = odp_timer_start(timer, &start_param);
477
478 if (odp_unlikely(ret != ODP_TIMER_SUCCESS)) {
479 /* Should never happen. Timeout event
480 * has been received, timer should be
481 * ready to be set again. */
482 printf("Expired timer reset failed "
483 "%i\n", ret);
484 odp_event_free(ev[i]);
485 }
486
487 if (odp_unlikely(tmos > 1)) {
488 /* Should never happen */
489 printf("Too many timeouts\n");
490 }
491 } else {
492 pkt[i - tmos] = odp_packet_from_event(ev[i]);
493 }
494 }
495
496 if (tmos == 0) {
497 /* Reset timer with existing timeout event */
498 ret = odp_timer_restart(timer, &start_param);
499
500 if (odp_unlikely(ret != ODP_TIMER_SUCCESS &&
501 ret != ODP_TIMER_FAIL)) {
502 /* Tick period is too short or long. Normally,
503 * reset either succeeds or fails due to timer
504 * expiration, in which case timeout event will
505 * be received soon and reset will be done
506 * then. */
507 printf("Timer reset failed %i\n", ret);
508 }
509 }
510
511 num_pkt = num - tmos;
512
513 if (DEBUG_PRINT)
514 printf("worker %i: [%i/%i] -> [%i/%i], %i packets "
515 "%i timeouts\n",
516 worker_id,
517 queue_context->src_pktio,
518 queue_context->src_queue,
519 queue_context->dst_pktio,
520 queue_context->dst_queue, num_pkt, tmos);
521
522 if (odp_unlikely(test_global->opt.collect_stat && tmos))
523 test_global->worker_stat[worker_id].tmo += tmos;
524
525 if (odp_unlikely(num_pkt == 0))
526 continue;
527
528 pktout = queue_context->dst_pktout;
529 out = queue_context->dst_pktio;
530
531 send_packets(test_global, pkt, num_pkt, out, pktout, worker_id);
532
533 if (odp_unlikely(test_global->opt.collect_stat))
534 test_global->worker_stat[worker_id].rx_pkt += num_pkt;
535 }
536
537 printf("Worker %i stopped\n", worker_id);
538
539 return 0;
540}
541
542static void sig_handler(int signo)
543{
544 (void)signo;
545
546 sigint_received = 1;
547}
548
549static int setup_sig_handler(void)
550{
551 struct sigaction action;
552
553 memset(&action, 0, sizeof(action));
554 action.sa_handler = sig_handler;
555
556 if (sigemptyset(&action.sa_mask))
557 return -1;
558 if (sigaction(SIGINT, &action, NULL))
559 return -1;
560 return 0;
561}
562
563/* Get rid of path in filename - only for unix-type paths using '/' */
564#define NO_PATH(x) (strrchr((x), '/') ? strrchr((x), '/') + 1 : (x))
565
566static void print_usage(const char *progname)
567{
568 printf("\n"
569 "Scheduler with packet IO test application.\n"
570 "\n"
571 "Usage: %s [options]\n"
572 "\n"
573 "OPTIONS:\n"
574 " -i, --interface <name> Packet IO interfaces (comma-separated, no spaces)\n"
575 " -c, --num_cpu <number> Worker thread count. Default: 1\n"
576 " -q, --num_queue <number> Number of pktio queues. Default: Worker thread count\n"
577 " -b, --burst <number> Maximum number of events requested from scheduler. Default: 32\n"
578 " -t, --timeout <number> Flow inactivity timeout (in usec) per packet. Default: 0 (don't use timers)\n"
579 " --pipe-stages <number> Number of pipeline stages per interface\n"
580 " --pipe-queues <number> Number of queues per pipeline stage\n"
581 " --pipe-queue-size <num> Number of events a pipeline queue must be able to store. Default 256.\n"
582 " -m, --sched_mode <mode> Scheduler synchronization mode for all queues. 1: parallel, 2: atomic, 3: ordered. Default: 2\n"
583 " --wait_link <sec> Wait up to <sec> seconds for network links to be up.\n"
584 " Default: 0 (don't check link status)\n"
585 " -s, --stat Collect statistics.\n"
586 " -h, --help Display help and exit.\n\n",
587 NO_PATH(progname));
588}
589
590static int parse_options(int argc, char *argv[], test_options_t *test_options)
591{
592 int i, opt;
593 char *name, *str;
594 int len, str_len, sched_mode;
595 const struct option longopts[] = {
596 {"interface", required_argument, NULL, 'i'},
597 {"num_cpu", required_argument, NULL, 'c'},
598 {"num_queue", required_argument, NULL, 'q'},
599 {"burst", required_argument, NULL, 'b'},
600 {"timeout", required_argument, NULL, 't'},
601 {"sched_mode", required_argument, NULL, 'm'},
602 {"wait_link", required_argument, NULL, OPT_WAIT_LINK},
603 {"pipe-stages", required_argument, NULL, 0},
604 {"pipe-queues", required_argument, NULL, 1},
605 {"pipe-queue-size", required_argument, NULL, 2},
606 {"stat", no_argument, NULL, 's'},
607 {"help", no_argument, NULL, 'h'},
608 {NULL, 0, NULL, 0}
609 };
610 const char *shortopts = "+i:c:q:b:t:m:sh";
611 int ret = 0;
612
613 memset(test_options, 0, sizeof(test_options_t));
614
615 test_options->sched_mode = SCHED_MODE_ATOMIC;
616 test_options->num_worker = 1;
617 test_options->burst_size = 32;
618 test_options->pipe_queue_size = 256;
619
620 while (1) {
621 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
622
623 if (opt == -1)
624 break; /* No more options */
625
626 switch (opt) {
627 case 0:
628 test_options->pipe_stages = atoi(optarg);
629 break;
630 case 1:
631 test_options->pipe_queues = atoi(optarg);
632 break;
633 case 2:
634 test_options->pipe_queue_size = atoi(optarg);
635 break;
636 case 'i':
637 i = 0;
638 str = optarg;
639 str_len = strlen(str);
640
641 while (str_len > 0) {
642 len = strcspn(str, ",");
643 str_len -= len + 1;
644
645 if (i == MAX_PKTIOS) {
646 printf("Error: Too many interfaces\n");
647 ret = -1;
648 break;
649 }
650
651 if (len > MAX_PKTIO_NAME) {
652 printf("Error: Too long interface name %s\n",
653 str);
654 ret = -1;
655 break;
656 }
657
658 name = test_options->pktio_name[i];
659 memcpy(name, str, len);
660 str += len + 1;
661 i++;
662 }
663
664 test_options->num_pktio = i;
665
666 break;
667 case 'c':
668 test_options->num_worker = atoi(optarg);
669 break;
670 case 'q':
671 test_options->num_pktio_queue = atoi(optarg);
672 break;
673 case 'b':
674 test_options->burst_size = atoi(optarg);
675 break;
676 case 't':
677 test_options->timeout_us = atol(optarg);
678 break;
679 case 'm':
680 test_options->sched_mode = atoi(optarg);
681 break;
682 case OPT_WAIT_LINK:
683 test_options->wait_sec = atoi(optarg);
684 break;
685 case 's':
686 test_options->collect_stat = 1;
687 break;
688 case 'h':
689 print_usage(argv[0]);
690 ret = -1;
691 break;
692 default:
693 ret = -1;
694 break;
695 }
696 }
697
698 if (test_options->timeout_us && test_options->pipe_stages) {
699 printf("Error: Cannot run timeout and pipeline tests simultaneously\n");
700 ret = -1;
701 }
702
703 if (test_options->pipe_stages > MAX_PIPE_STAGES) {
704 printf("Error: Too many pipeline stages\n");
705 ret = -1;
706 }
707
708 if (test_options->pipe_queues > MAX_PIPE_QUEUES) {
709 printf("Error: Too many queues per pipeline stage\n");
710 ret = -1;
711 }
712
713 if (test_options->num_pktio == 0) {
714 printf("Error: At least one pktio interface needed.\n");
715 ret = -1;
716 }
717
718 sched_mode = test_options->sched_mode;
719 if (sched_mode != SCHED_MODE_PARAL &&
720 sched_mode != SCHED_MODE_ATOMIC &&
721 sched_mode != SCHED_MODE_ORDER) {
722 printf("Error: Bad scheduler mode: %i\n", sched_mode);
723 ret = -1;
724 }
725
726 if (test_options->num_pktio_queue == 0)
727 test_options->num_pktio_queue = test_options->num_worker;
728
729 return ret;
730}
731
732static odp_schedule_sync_t sched_sync_mode(test_global_t *test_global)
733{
734 switch (test_global->opt.sched_mode) {
735 case SCHED_MODE_PARAL:
737 case SCHED_MODE_ATOMIC:
739 case SCHED_MODE_ORDER:
741 default:
742 return -1;
743 }
744}
745
746static int config_setup(test_global_t *test_global)
747{
748 int i, cpu;
749 odp_pool_capability_t pool_capa;
750 uint32_t pkt_len, pkt_num;
751 odp_cpumask_t *cpumask = &test_global->cpumask;
752
753 test_global->max_workers = odp_cpumask_default_worker(cpumask, 0);
754
755 if (test_global->opt.num_worker > test_global->max_workers ||
756 test_global->opt.num_worker > MAX_WORKERS) {
757 printf("Error: Too many workers %i.\n",
758 test_global->opt.num_worker);
759 return -1;
760 }
761
762 cpu = odp_cpumask_first(cpumask);
763 for (i = 0; i < test_global->opt.num_worker; ++i) {
764 test_global->worker_cpu[i] = cpu;
765 cpu = odp_cpumask_next(cpumask, cpu);
766 }
767
768 odp_schedule_config_init(&test_global->schedule_config);
769 odp_schedule_config(&test_global->schedule_config);
770
771 if (odp_pool_capability(&pool_capa)) {
772 printf("Error: Pool capability failed.\n");
773 return -1;
774 }
775
776 pkt_len = MAX_PKT_LEN;
777 pkt_num = MAX_PKT_NUM;
778
779 if (pool_capa.pkt.max_len && pkt_len > pool_capa.pkt.max_len)
780 pkt_len = pool_capa.pkt.max_len;
781
782 if (pool_capa.pkt.max_num && pkt_num > pool_capa.pkt.max_num) {
783 pkt_num = pool_capa.pkt.max_num;
784 printf("Warning: Pool size rounded down to %u\n", pkt_num);
785 }
786
787 test_global->pkt_len = pkt_len;
788 test_global->pkt_num = pkt_num;
789
790 return 0;
791}
792
793static void print_config(test_global_t *test_global)
794{
795 char cpumask_str[ODP_CPUMASK_STR_SIZE];
796 int i;
797
798 odp_cpumask_to_str(&test_global->cpumask, cpumask_str,
800
801 printf("\n"
802 "Test configuration:\n"
803 " max workers: %i\n"
804 " available worker cpus: %s\n"
805 " num workers: %i\n"
806 " worker cpus: ",
807 test_global->max_workers,
808 cpumask_str,
809 test_global->opt.num_worker);
810
811 for (i = 0; i < test_global->opt.num_worker; i++)
812 printf(" %i", test_global->worker_cpu[i]);
813
814 printf("\n"
815 " num interfaces: %i\n"
816 " interface names: ", test_global->opt.num_pktio);
817
818 for (i = 0; i < test_global->opt.num_pktio; i++)
819 printf(" %s", test_global->opt.pktio_name[i]);
820
821 printf("\n"
822 " queues per interface: %i\n",
823 test_global->opt.num_pktio_queue);
824
825 printf(" burst size: %u\n", test_global->opt.burst_size);
826 printf(" collect statistics: %u\n", test_global->opt.collect_stat);
827 printf(" timeout usec: %li\n", test_global->opt.timeout_us);
828
829 printf("\n");
830}
831
832static void print_stat(test_global_t *test_global, uint64_t nsec)
833{
834 int i;
835 uint64_t rx, tx, pipe, drop, tmo;
836 uint64_t rx_sum = 0;
837 uint64_t tx_sum = 0;
838 uint64_t pipe_sum = 0;
839 uint64_t tmo_sum = 0;
840 double sec = 0.0;
841
842 printf("\nTest statistics\n");
843 printf(" worker rx_pkt tx_pkt pipe dropped tmo\n");
844
845 for (i = 0; i < test_global->opt.num_worker; i++) {
846 rx = test_global->worker_stat[i].rx_pkt;
847 tx = test_global->worker_stat[i].tx_pkt;
848 pipe = test_global->worker_stat[i].pipe_pkt;
849 tmo = test_global->worker_stat[i].tmo;
850 rx_sum += rx;
851 tx_sum += tx;
852 pipe_sum += pipe;
853 tmo_sum += tmo;
854 drop = test_global->worker_stat[i].tx_drop +
855 test_global->worker_stat[i].pipe_drop;
856
857 printf(" %6i %16" PRIu64 " %16" PRIu64 " %16" PRIu64 " %16"
858 PRIu64 " %16" PRIu64 "\n", i, rx, tx, pipe, drop, tmo);
859 }
860
861 test_global->rx_pkt_sum = rx_sum;
862 test_global->tx_pkt_sum = tx_sum;
863 drop = rx_sum - tx_sum;
864
865 printf(" ------------------------------------------------------------------------------------\n");
866 printf(" total %16" PRIu64 " %16" PRIu64 " %16" PRIu64 " %16"
867 PRIu64 " %16" PRIu64 "\n\n", rx_sum, tx_sum, pipe_sum, drop,
868 tmo_sum);
869
870 sec = nsec / 1000000000.0;
871 printf(" Total test time: %.2f sec\n", sec);
872 printf(" Rx packet rate: %.2f pps\n", rx_sum / sec);
873 printf(" Tx packet rate: %.2f pps\n", tx_sum / sec);
874 printf(" Drop rate: %.2f pps\n", drop / sec);
875 printf(" Timeout rate: %.2f per sec\n\n", tmo_sum / sec);
876}
877
878static int open_pktios(test_global_t *test_global)
879{
880 odp_pool_param_t pool_param;
881 odp_pktio_param_t pktio_param;
882 odp_pool_t pool;
883 odp_pktio_t pktio;
884 odp_pktio_capability_t pktio_capa;
885 odp_pktio_config_t pktio_config;
886 odp_pktin_queue_param_t pktin_param;
887 odp_pktout_queue_param_t pktout_param;
888 odp_schedule_sync_t sched_sync;
889 uint32_t num_queue, j;
890 char *name;
891 int i, num_pktio, ret;
892
893 num_pktio = test_global->opt.num_pktio;
894 num_queue = test_global->opt.num_pktio_queue;
895
896 odp_pool_param_init(&pool_param);
897 pool_param.pkt.seg_len = MIN_PKT_SEG_LEN;
898 pool_param.pkt.len = test_global->pkt_len;
899 pool_param.pkt.num = test_global->pkt_num;
900 pool_param.type = ODP_POOL_PACKET;
901
902 pool = odp_pool_create("packet pool", &pool_param);
903
904 test_global->pool = pool;
905
906 if (pool == ODP_POOL_INVALID) {
907 printf("Error: Pool create.\n");
908 return -1;
909 }
910
911 odp_pktio_param_init(&pktio_param);
912 pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
913 pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
914
915 sched_sync = sched_sync_mode(test_global);
916
917 for (i = 0; i < num_pktio; i++)
918 test_global->pktio[i].pktio = ODP_PKTIO_INVALID;
919
920 /* Open and configure interfaces */
921 for (i = 0; i < num_pktio; i++) {
922 name = test_global->opt.pktio_name[i];
923 pktio = odp_pktio_open(name, pool, &pktio_param);
924
925 if (pktio == ODP_PKTIO_INVALID) {
926 printf("Error (%s): Pktio open failed.\n", name);
927 return -1;
928 }
929
930 test_global->pktio[i].pktio = pktio;
931 test_global->pktio[i].pktio_index = odp_pktio_index(pktio);
932
933 ret = odp_pktio_mac_addr(pktio,
934 test_global->pktio[i].my_addr.addr,
935 ODPH_ETHADDR_LEN);
936 if (ret != ODPH_ETHADDR_LEN) {
937 printf("Error (%s): Bad MAC address len.\n", name);
938 return -1;
939 }
940
941 odp_pktio_print(pktio);
942
943 if (odp_pktio_capability(pktio, &pktio_capa)) {
944 printf("Error (%s): Pktio capa failed.\n", name);
945 return -1;
946 }
947
948 if (num_queue > pktio_capa.max_input_queues) {
949 printf("Error (%s): Too many input queues: %u\n",
950 name, num_queue);
951 return -1;
952 }
953
954 if (num_queue > pktio_capa.max_output_queues) {
955 printf("Error (%s): Too many output queues: %u\n",
956 name, num_queue);
957 return -1;
958 }
959
960 odp_pktio_config_init(&pktio_config);
961 pktio_config.parser.layer = ODP_PROTO_LAYER_NONE;
962
963 odp_pktio_config(pktio, &pktio_config);
964
965 odp_pktin_queue_param_init(&pktin_param);
966
968 pktin_param.queue_param.sched.sync = sched_sync;
970
971 if (num_queue > 1) {
972 pktin_param.hash_enable = 1;
973 pktin_param.hash_proto.proto.ipv4_udp = 1;
974 }
975
976 pktin_param.num_queues = num_queue;
977
978 if (odp_pktin_queue_config(pktio, &pktin_param)) {
979 printf("Error (%s): Pktin config failed.\n", name);
980 return -1;
981 }
982
983 if (odp_pktin_event_queue(pktio,
984 test_global->pktio[i].input_queue,
985 num_queue) != (int)num_queue) {
986 printf("Error (%s): Input queue query failed.\n", name);
987 return -1;
988 }
989
990 for (j = 0; j < num_queue; j++) {
991 odp_queue_t queue;
992 void *ctx;
993 uint32_t len = sizeof(pktin_queue_context_t);
994
995 queue = test_global->pktio[i].input_queue[j];
996 ctx = &test_global->pktio[i].queue_context[j];
997
998 if (odp_queue_context_set(queue, ctx, len)) {
999 printf("Error (%s): Queue ctx set failed.\n",
1000 name);
1001 return -1;
1002 }
1003 }
1004
1005 odp_pktout_queue_param_init(&pktout_param);
1006 pktout_param.num_queues = num_queue;
1007 pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
1008
1009 if (test_global->opt.pipe_stages)
1010 pktout_param.op_mode = ODP_PKTIO_OP_MT;
1011
1012 if (odp_pktout_queue_config(pktio, &pktout_param)) {
1013 printf("Error (%s): Pktout config failed.\n", name);
1014 return -1;
1015 }
1016
1017 if (odp_pktout_queue(pktio,
1018 test_global->pktio[i].pktout,
1019 num_queue) != (int)num_queue) {
1020 printf("Error (%s): Output queue query failed.\n",
1021 name);
1022 return -1;
1023 }
1024 }
1025
1026 return 0;
1027}
1028
1029static void link_pktios(test_global_t *test_global)
1030{
1031 int i, num_pktio, input, output;
1032 int num_queue;
1033 odp_pktout_queue_t pktout;
1034 pktin_queue_context_t *ctx;
1035
1036 num_pktio = test_global->opt.num_pktio;
1037 num_queue = test_global->opt.num_pktio_queue;
1038
1039 printf("Forwarding table (pktio indexes)\n");
1040
1041 /* If single interface loopback, otherwise forward to the next
1042 * interface. */
1043 for (input = 0; input < num_pktio; input++) {
1044 output = (input + 1) % num_pktio;
1045 printf(" input %i, output %i\n", input, output);
1046
1047 for (i = 0; i < num_queue; i++) {
1048 ctx = &test_global->pktio[input].queue_context[i];
1049 pktout = test_global->pktio[output].pktout[i];
1050 ctx->stage = 0;
1051 ctx->queue_idx = i;
1052 ctx->dst_pktout = pktout;
1053 ctx->dst_pktio = output;
1054 ctx->dst_queue = i;
1055 ctx->src_pktio = input;
1056 ctx->src_queue = i;
1057 }
1058 }
1059
1060 printf("\n");
1061}
1062
1063static int start_pktios(test_global_t *test_global)
1064{
1065 test_options_t *test_options = &test_global->opt;
1066 uint32_t num_pktio = test_options->num_pktio;
1067 odp_pktio_t pktios[MAX_PKTIOS];
1068 const char *names[MAX_PKTIOS];
1069 uint32_t i;
1070
1071 for (i = 0; i < num_pktio; i++) {
1072 if (odp_pktio_start(test_global->pktio[i].pktio)) {
1073 ODPH_ERR("Error (%s): Pktio start failed.\n", test_options->pktio_name[i]);
1074 return -1;
1075 }
1076
1077 test_global->pktio[i].started = 1;
1078 pktios[i] = test_global->pktio[i].pktio;
1079 names[i] = test_options->pktio_name[i];
1080 }
1081
1082 /* Wait until all links are up */
1083 if (test_options->wait_sec &&
1084 pktio_common_check_link_status_wait(pktios, names, num_pktio,
1085 test_options->wait_sec) == -1)
1086 return -1;
1087
1088 pktio_common_print_link_info_multi(pktios, names, num_pktio);
1089
1090 return 0;
1091}
1092
1093static int stop_pktios(test_global_t *test_global)
1094{
1095 odp_pktio_t pktio;
1096 int i, ret = 0;
1097
1098 for (i = 0; i < test_global->opt.num_pktio; i++) {
1099 pktio = test_global->pktio[i].pktio;
1100
1101 if (pktio == ODP_PKTIO_INVALID ||
1102 test_global->pktio[i].started == 0)
1103 continue;
1104
1105 if (odp_pktio_stop(pktio)) {
1106 printf("Error (%s): Pktio stop failed.\n",
1107 test_global->opt.pktio_name[i]);
1108 ret = -1;
1109 }
1110 }
1111
1112 return ret;
1113}
1114
1115static void empty_queues(uint64_t wait_ns)
1116{
1117 odp_event_t ev;
1118 uint64_t wait_time = odp_schedule_wait_time(wait_ns);
1119
1120 /* Drop all events from all queues */
1121 while (1) {
1122 ev = odp_schedule(NULL, wait_time);
1123
1124 if (ev == ODP_EVENT_INVALID)
1125 break;
1126
1127 odp_event_free(ev);
1128 }
1129}
1130
1131static int close_pktios(test_global_t *test_global)
1132{
1133 odp_pktio_t pktio;
1134 odp_pool_t pool;
1135 int i, ret = 0;
1136
1137 for (i = 0; i < test_global->opt.num_pktio; i++) {
1138 pktio = test_global->pktio[i].pktio;
1139
1140 if (pktio == ODP_PKTIO_INVALID)
1141 continue;
1142
1143 if (odp_pktio_close(pktio)) {
1144 printf("Error (%s): Pktio close failed.\n",
1145 test_global->opt.pktio_name[i]);
1146 ret = -1;
1147 }
1148 }
1149
1150 pool = test_global->pool;
1151
1152 if (pool == ODP_POOL_INVALID)
1153 return ret;
1154
1155 if (odp_pool_destroy(pool)) {
1156 printf("Error: Pool destroy failed.\n");
1157 ret = -1;
1158 }
1159
1160 return ret;
1161}
1162
1163static int create_pipeline_queues(test_global_t *test_global)
1164{
1165 int i, j, k, num_pktio, stages, queues, ctx_size;
1166 pipe_queue_context_t *ctx;
1167 odp_queue_param_t queue_param;
1168 odp_schedule_sync_t sched_sync;
1169 int ret = 0;
1170
1171 num_pktio = test_global->opt.num_pktio;
1172 stages = test_global->opt.pipe_stages;
1173 queues = test_global->opt.pipe_queues;
1174 sched_sync = sched_sync_mode(test_global);
1175
1176 odp_queue_param_init(&queue_param);
1177 queue_param.type = ODP_QUEUE_TYPE_SCHED;
1178 queue_param.sched.prio = odp_schedule_default_prio();
1179 queue_param.sched.sync = sched_sync;
1180 queue_param.sched.group = ODP_SCHED_GROUP_ALL;
1181
1182 queue_param.size = test_global->opt.pipe_queue_size;
1183 if (test_global->schedule_config.queue_size &&
1184 queue_param.size > test_global->schedule_config.queue_size) {
1185 printf("Error: Pipeline queue max size is %u\n",
1186 test_global->schedule_config.queue_size);
1187 return -1;
1188 }
1189
1190 ctx_size = sizeof(pipe_queue_context_t);
1191
1192 for (i = 0; i < stages; i++) {
1193 for (j = 0; j < queues; j++) {
1194 ctx = &test_global->pipe_queue_ctx[i][j].ctx;
1195
1196 /* packet input is stage 0 */
1197 ctx->stage = i + 1;
1198 ctx->queue_idx = j;
1199 }
1200 }
1201
1202 for (k = 0; k < num_pktio; k++) {
1203 for (i = 0; i < stages; i++) {
1204 for (j = 0; j < queues; j++) {
1205 odp_queue_t q;
1206
1207 q = odp_queue_create(NULL, &queue_param);
1208 test_global->pipe_queue[k][i].queue[j] = q;
1209
1210 if (q == ODP_QUEUE_INVALID) {
1211 printf("Error: Queue create failed [%i] %i/%i\n",
1212 k, i, j);
1213 ret = -1;
1214 break;
1215 }
1216
1217 ctx = &test_global->pipe_queue_ctx[i][j].ctx;
1218
1219 if (odp_queue_context_set(q, ctx, ctx_size)) {
1220 printf("Error: Queue ctx set failed [%i] %i/%i\n",
1221 k, i, j);
1222 ret = -1;
1223 break;
1224 }
1225 }
1226 }
1227 }
1228
1229 return ret;
1230}
1231
1232static void destroy_pipeline_queues(test_global_t *test_global)
1233{
1234 int i, j, k, num_pktio, stages, queues;
1235 odp_queue_t queue;
1236
1237 num_pktio = test_global->opt.num_pktio;
1238 stages = test_global->opt.pipe_stages;
1239 queues = test_global->opt.pipe_queues;
1240
1241 for (k = 0; k < num_pktio; k++) {
1242 for (i = 0; i < stages; i++) {
1243 for (j = 0; j < queues; j++) {
1244 queue = test_global->pipe_queue[k][i].queue[j];
1245
1246 if (queue == ODP_QUEUE_INVALID) {
1247 printf("Error: Bad queue handle [%i] %i/%i\n",
1248 k, i, j);
1249 return;
1250 }
1251
1252 if (odp_queue_destroy(queue)) {
1253 printf("Error: Queue destroy failed [%i] %i/%i\n",
1254 k, i, j);
1255 return;
1256 }
1257 }
1258 }
1259 }
1260}
1261
1262static int create_timers(test_global_t *test_global)
1263{
1264 int num_timer, num_pktio, num_queue, i, j;
1265 odp_pool_t pool;
1266 odp_pool_param_t pool_param;
1267 odp_timer_pool_t timer_pool;
1268 odp_timer_pool_param_t timer_param;
1269 odp_timer_capability_t timer_capa;
1270 odp_timer_t timer;
1271 odp_queue_t queue;
1272 uint64_t res_ns, tick;
1273 uint64_t timeout_ns = 1000 * test_global->opt.timeout_us;
1274
1275 num_pktio = test_global->opt.num_pktio;
1276 num_queue = test_global->opt.num_pktio_queue;
1277 num_timer = num_pktio * num_queue;
1278
1279 /* Always init globals for destroy calls */
1280 test_global->timer.timer_pool = ODP_TIMER_POOL_INVALID;
1281 test_global->timer.timeout_pool = ODP_POOL_INVALID;
1282
1283 for (i = 0; i < num_pktio; i++)
1284 for (j = 0; j < num_queue; j++)
1285 test_global->timer.timer[i][j] = ODP_TIMER_INVALID;
1286
1287 /* Timers not used */
1288 if (test_global->opt.timeout_us == 0)
1289 return 0;
1290
1291 if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
1292 printf("Timer capa failed\n");
1293 return -1;
1294 }
1295
1296 res_ns = timeout_ns / 10;
1297
1298 if (timer_capa.highest_res_ns > res_ns) {
1299 printf("Timeout too short. Min timeout %" PRIu64 " usec\n",
1300 timer_capa.highest_res_ns / 100);
1301 return -1;
1302 }
1303
1304 odp_timer_pool_param_init(&timer_param);
1305 timer_param.res_ns = res_ns;
1306 timer_param.min_tmo = timeout_ns;
1307 timer_param.max_tmo = timeout_ns;
1308 timer_param.num_timers = num_timer;
1309 timer_param.clk_src = ODP_CLOCK_DEFAULT;
1310
1311 timer_pool = odp_timer_pool_create("sched_pktio_timer", &timer_param);
1312
1313 if (timer_pool == ODP_TIMER_POOL_INVALID) {
1314 printf("Timer pool create failed\n");
1315 return -1;
1316 }
1317
1318 if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) {
1319 ODPH_ERR("Timer pool start failed\n");
1320 return -1;
1321 }
1322
1323 test_global->timer.timer_pool = timer_pool;
1324 tick = odp_timer_ns_to_tick(timer_pool, timeout_ns);
1325 test_global->timer.timeout_tick = tick;
1326
1327 for (i = 0; i < num_pktio; i++) {
1328 for (j = 0; j < num_queue; j++) {
1329 queue = test_global->pktio[i].input_queue[j];
1330 timer = odp_timer_alloc(timer_pool, queue, NULL);
1331
1332 if (timer == ODP_TIMER_INVALID) {
1333 printf("Timer alloc failed.\n");
1334 return -1;
1335 }
1336
1337 test_global->timer.timer[i][j] = timer;
1338 }
1339 }
1340
1341 odp_pool_param_init(&pool_param);
1342 pool_param.type = ODP_POOL_TIMEOUT;
1343 pool_param.tmo.num = num_timer;
1344
1345 pool = odp_pool_create("timeout pool", &pool_param);
1346
1347 if (pool == ODP_POOL_INVALID) {
1348 printf("Timeout pool create failed.\n");
1349 return -1;
1350 }
1351
1352 test_global->timer.timeout_pool = pool;
1353
1354 return 0;
1355}
1356
1357static int start_timers(test_global_t *test_global)
1358{
1359 int i, j;
1360 odp_timeout_t timeout;
1361 odp_timer_t timer;
1363 odp_timer_start_t start_param;
1364 uint64_t timeout_tick = test_global->timer.timeout_tick;
1365 int num_pktio = test_global->opt.num_pktio;
1366 int num_queue = test_global->opt.num_pktio_queue;
1367 odp_pool_t pool = test_global->timer.timeout_pool;
1368
1369 /* Timers not used */
1370 if (test_global->opt.timeout_us == 0)
1371 return 0;
1372
1373 start_param.tick_type = ODP_TIMER_TICK_REL;
1374 start_param.tick = timeout_tick;
1375
1376 for (i = 0; i < num_pktio; i++) {
1377 for (j = 0; j < num_queue; j++) {
1378 timer = test_global->timer.timer[i][j];
1379
1380 timeout = odp_timeout_alloc(pool);
1381 if (timeout == ODP_TIMEOUT_INVALID) {
1382 printf("Timeout alloc failed\n");
1383 return -1;
1384 }
1385
1386 start_param.tmo_ev = odp_timeout_to_event(timeout);
1387
1388 ret = odp_timer_start(timer, &start_param);
1389 if (ret != ODP_TIMER_SUCCESS) {
1390 printf("Timer set failed\n");
1391 return -1;
1392 }
1393 }
1394 }
1395
1396 return 0;
1397}
1398
1399static void destroy_timers(test_global_t *test_global)
1400{
1401 int i, j;
1402 odp_timer_t timer;
1403 int num_pktio = test_global->opt.num_pktio;
1404 int num_queue = test_global->opt.num_pktio_queue;
1405 odp_timer_pool_t timer_pool = test_global->timer.timer_pool;
1406 odp_pool_t pool = test_global->timer.timeout_pool;
1407
1408 if (timer_pool == ODP_TIMER_POOL_INVALID)
1409 return;
1410
1411 /* Wait any remaining timers to expire */
1412 empty_queues(2000 * test_global->opt.timeout_us);
1413
1414 for (i = 0; i < num_pktio; i++) {
1415 for (j = 0; j < num_queue; j++) {
1416 timer = test_global->timer.timer[i][j];
1417
1418 if (timer == ODP_TIMER_INVALID)
1419 break;
1420
1421 if (odp_timer_free(timer))
1422 printf("Timer free failed: %i, %i\n", i, j);
1423 }
1424 }
1425
1426 if (pool != ODP_POOL_INVALID) {
1427 if (odp_pool_destroy(pool))
1428 printf("Timeout pool destroy failed\n");
1429 }
1430
1431 odp_timer_pool_destroy(timer_pool);
1432}
1433
1434static void start_workers(odph_thread_t thread[],
1435 test_global_t *test_global)
1436{
1437 int i;
1438 odp_cpumask_t cpumask;
1439 odph_thread_common_param_t thr_common;
1440 odph_thread_param_t thr_param[MAX_WORKERS];
1441 int num = test_global->opt.num_worker;
1442
1443 odp_cpumask_zero(&cpumask);
1444
1445 odph_thread_common_param_init(&thr_common);
1446 thr_common.instance = test_global->instance;
1447 thr_common.cpumask = &cpumask;
1448
1449 for (i = 0; i < num; i++) {
1450 odp_cpumask_set(&cpumask, test_global->worker_cpu[i]);
1451 test_global->worker_arg[i].worker_id = i;
1452 test_global->worker_arg[i].test_global_ptr = test_global;
1453
1454 odph_thread_param_init(&thr_param[i]);
1455
1456 if (!i) {
1457 if (test_global->opt.timeout_us)
1458 thr_param[0].start = worker_thread_timers;
1459 else if (test_global->opt.pipe_stages)
1460 thr_param[0].start = worker_thread_pipeline;
1461 else
1462 thr_param[0].start = worker_thread_direct;
1463 } else {
1464 thr_param[i].start = thr_param[0].start;
1465 }
1466
1467 thr_param[i].arg = &test_global->worker_arg[i];
1468 thr_param[i].thr_type = ODP_THREAD_WORKER;
1469 }
1470
1471 memset(thread, 0, num * sizeof(odph_thread_t));
1472 odph_thread_create(thread, &thr_common, thr_param, num);
1473}
1474
1475static void wait_workers(odph_thread_t thread[], test_global_t *test_global)
1476{
1477 odph_thread_join(thread, test_global->opt.num_worker);
1478}
1479
1480int main(int argc, char *argv[])
1481{
1482 odp_instance_t instance;
1483 odp_init_t init;
1484 odp_shm_t shm;
1486 odph_helper_options_t helper_options;
1487 odph_thread_t thread[MAX_WORKERS];
1488 test_options_t test_options;
1489 sigset_t sigint_set;
1490 int ret = 0;
1491
1492 /* SIGINT may be ignored, so install the handler before anything else. */
1493 if (setup_sig_handler()) {
1494 printf("Error: sigaction failed.\n");
1495 return -1;
1496 }
1497
1498 /*
1499 * We want SIGINT to be delivered to this initial thread/process.
1500 * Block it now so that child threads/processes inherit the blocking.
1501 */
1502 if (sigemptyset(&sigint_set) || sigaddset(&sigint_set, SIGINT) ||
1503 pthread_sigmask(SIG_BLOCK, &sigint_set, NULL)) {
1504 printf("Error: blocking SIGINT failed.\n");
1505 return -1;
1506 }
1507
1508 /* Let helper collect its own arguments (e.g. --odph_proc) */
1509 argc = odph_parse_options(argc, argv);
1510 if (odph_options(&helper_options)) {
1511 printf("Error: reading ODP helper options failed.\n");
1512 exit(EXIT_FAILURE);
1513 }
1514
1515 if (parse_options(argc, argv, &test_options))
1516 return -1;
1517
1518 /* List features not to be used (may optimize performance) */
1519 odp_init_param_init(&init);
1520 init.not_used.feat.cls = 1;
1521 init.not_used.feat.compress = 1;
1522 init.not_used.feat.crypto = 1;
1523 init.not_used.feat.dma = 1;
1524 init.not_used.feat.ipsec = 1;
1525 init.not_used.feat.ml = 1;
1526 init.not_used.feat.stash = 1;
1527 init.not_used.feat.tm = 1;
1528 init.not_used.feat.timer = 1;
1529
1530 if (test_options.timeout_us)
1531 init.not_used.feat.timer = 0;
1532
1533 init.mem_model = helper_options.mem_model;
1534
1535 /* Init ODP before calling anything else */
1536 if (odp_init_global(&instance, &init, NULL)) {
1537 printf("Error: Global init failed.\n");
1538 return -1;
1539 }
1540
1541 /* Init this thread */
1542 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
1543 printf("Error: Local init failed.\n");
1544 return -1;
1545 }
1546
1547 /* Reserve memory for args from shared mem */
1548 shm = odp_shm_reserve("test_global", sizeof(test_global_t),
1549 ODP_CACHE_LINE_SIZE, 0);
1550
1551 if (shm == ODP_SHM_INVALID) {
1552 printf("Error: shm reserve failed.\n");
1553 return -1;
1554 }
1555
1556 test_global = odp_shm_addr(shm);
1557 memset(test_global, 0, sizeof(test_global_t));
1558
1559 test_global->instance = instance;
1560 test_global->pool = ODP_POOL_INVALID;
1561 odp_atomic_init_u32(&test_global->stop_workers, 0);
1562
1563 memcpy(&test_global->opt, &test_options, sizeof(test_options_t));
1564
1566
1567 if (config_setup(test_global))
1568 goto quit;
1569
1570 print_config(test_global);
1571
1572 if (open_pktios(test_global))
1573 goto quit;
1574
1575 link_pktios(test_global);
1576
1577 if (create_pipeline_queues(test_global))
1578 goto quit;
1579
1580 if (create_timers(test_global))
1581 goto quit;
1582
1583 if (start_pktios(test_global))
1584 goto quit;
1585
1586 odp_barrier_init(&test_global->worker_start,
1587 test_global->opt.num_worker + 1);
1588
1589 start_workers(thread, test_global);
1590
1591 if (start_timers(test_global))
1592 odp_atomic_store_u32(&test_global->stop_workers, 1);
1593
1594 /* Synchronize pktio configuration with workers. Worker are now ready
1595 * to process packets. */
1596 odp_barrier_wait(&test_global->worker_start);
1597
1598 /*
1599 * Unblock SIGINT for this thread. If the signal is already pending,
1600 * it gets delivered now.
1601 */
1602 if (pthread_sigmask(SIG_UNBLOCK, &sigint_set, NULL)) {
1603 printf("Error: unblocking SIGINT failed.\n");
1604 odp_atomic_store_u32(&test_global->stop_workers, 1);
1605 }
1606
1607 t1 = odp_time_local();
1608
1609 while (!odp_atomic_load_u32(&test_global->stop_workers)) {
1610 struct timespec ts = { .tv_sec = 0, .tv_nsec = 100 * 1000 * 1000 };
1611
1612 if (sigint_received) {
1613 odp_atomic_store_u32(&test_global->stop_workers, 1);
1614 break;
1615 }
1616 /*
1617 * This gets interrupted by the signal. But the signal may have
1618 * already come.
1619 */
1620 nanosleep(&ts, NULL);
1621 }
1622
1623 wait_workers(thread, test_global);
1624
1625 t2 = odp_time_local();
1626
1627quit:
1628 stop_pktios(test_global);
1629 empty_queues(ODP_TIME_SEC_IN_NS / 2);
1630 close_pktios(test_global);
1631 destroy_pipeline_queues(test_global);
1632 destroy_timers(test_global);
1633
1634 if (test_global->opt.collect_stat) {
1635 print_stat(test_global, odp_time_diff_ns(t2, t1));
1636
1637 /* Encode return value for validation test usage. */
1638 if (test_global->rx_pkt_sum > TEST_PASSED_LIMIT)
1639 ret += 1;
1640
1641 if (test_global->tx_pkt_sum > TEST_PASSED_LIMIT)
1642 ret += 2;
1643 }
1644 test_global = NULL;
1645
1646 if (odp_shm_free(shm)) {
1647 printf("Error: shm free failed.\n");
1648 ret = -1;
1649 }
1650
1651 if (odp_term_local()) {
1652 printf("Error: term local failed.\n");
1653 ret = -1;
1654 }
1655
1656 if (odp_term_global(instance)) {
1657 printf("Error: term global failed.\n");
1658 ret = -1;
1659 }
1660
1661 return ret;
1662}
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_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
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.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
void odp_event_free_multi(const odp_event_t event[], int num)
Free multiple events.
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_pktin_queue_param_init(odp_pktin_queue_param_t *param)
Initialize packet input queue parameters.
void odp_pktio_param_init(odp_pktio_param_t *param)
Initialize pktio params.
int odp_pktio_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.
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.
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_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.
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_PKTOUT_MODE_DIRECT
Direct packet output on the interface.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIO_OP_MT
Multithread safe operation.
@ 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.
uint32_t odp_packet_flow_hash(odp_packet_t pkt)
Packet flow hash value.
int odp_packet_input_index(odp_packet_t pkt)
Packet input interface index.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
int odp_packet_has_flow_hash(odp_packet_t pkt)
Check for packet flow hash.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
@ ODP_PROTO_LAYER_NONE
No 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_TIMEOUT
Timeout pool.
@ ODP_POOL_PACKET
Packet pool.
void * odp_queue_context(odp_queue_t queue)
Get queue context.
int odp_queue_context_set(odp_queue_t queue, void *context, uint32_t len)
Set queue context.
int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num)
Enqueue multiple events to a queue.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
Enqueue an event to a queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
int odp_schedule_sync_t
Scheduler synchronization method.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_event_t events[], int num)
Schedule multiple events.
void odp_schedule_config_init(odp_schedule_config_t *config)
Initialize schedule configuration options.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_default_prio(void)
Default scheduling priority level.
void odp_schedule_pause(void)
Pause scheduling.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
void odp_sys_info_print(void)
Print system info.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_NULL
Zero time stamp.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num)
Start timer pools.
odp_timeout_t odp_timeout_alloc(odp_pool_t pool)
Timeout alloc.
int odp_timer_free(odp_timer_t timer)
Free a timer.
int odp_timer_restart(odp_timer_t timer, const odp_timer_start_t *start_param)
Restart a single shot timer.
#define ODP_TIMER_POOL_INVALID
Invalid timer pool handle.
odp_timer_pool_t odp_timer_pool_create(const char *name, const odp_timer_pool_param_t *params)
Create a timer pool.
int odp_timer_capability(odp_timer_clk_src_t clk_src, odp_timer_capability_t *capa)
Query timer capabilities per clock source.
uint64_t odp_timer_ns_to_tick(odp_timer_pool_t timer_pool, uint64_t ns)
Convert nanoseconds to timer ticks.
int odp_timer_start(odp_timer_t timer, const odp_timer_start_t *start_param)
Start a single shot timer.
odp_timer_retval_t
Return values for timer start, restart and cancel calls.
odp_event_t odp_timeout_to_event(odp_timeout_t tmo)
Convert timeout handle to event handle.
#define ODP_TIMEOUT_INVALID
Invalid timeout handle.
odp_timer_t odp_timer_alloc(odp_timer_pool_t timer_pool, odp_queue_t queue, const void *user_ptr)
Allocate a single shot timer.
#define ODP_CLOCK_DEFAULT
The default clock source.
#define ODP_TIMER_INVALID
Invalid timer handle.
void odp_timer_pool_param_init(odp_timer_pool_param_t *param)
Initialize timer pool parameters.
void odp_timer_pool_destroy(odp_timer_pool_t timer_pool)
Destroy a timer pool.
@ ODP_TIMER_SUCCESS
Timer operation succeeded.
@ ODP_TIMER_FAIL
Timer operation failed.
@ ODP_TIMER_TICK_REL
Relative ticks.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
Packet input queue parameters.
uint32_t num_queues
Number of input queues to be created.
odp_queue_param_t queue_param
Queue parameters.
odp_pktin_hash_proto_t hash_proto
Protocol field selection for hashing.
odp_bool_t hash_enable
Enable flow hashing.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_output_queues
Maximum number of output queues.
Packet IO configuration options.
odp_pktio_parser_config_t parser
Packet input parser configuration.
Packet IO parameters.
odp_pktin_mode_t in_mode
Packet input mode.
odp_pktout_mode_t out_mode
Packet output mode.
odp_proto_layer_t layer
Protocol parsing level in packet input.
Packet output queue parameters.
odp_pktio_op_mode_t op_mode
Operation mode.
uint32_t num_queues
Number of output queues to be created.
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
uint32_t max_len
Maximum packet data length in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
struct odp_pool_param_t::@140 tmo
Parameters for timeout pools.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
uint32_t size
Queue size.
odp_queue_type_t type
Queue type.
Schedule configuration.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint64_t highest_res_ns
Highest timer resolution in nanoseconds.
Timer pool parameters.
uint64_t res_ns
Timeout resolution in nanoseconds.
uint64_t min_tmo
Minimum relative timeout in nanoseconds.
uint32_t num_timers
Number of timers in the pool.
odp_timer_clk_src_t clk_src
Clock source for timers.
uint64_t max_tmo
Maximum relative timeout in nanoseconds.
Timer start parameters.
uint64_t tick
Expiration time in ticks.
odp_event_t tmo_ev
Timeout event.
odp_timer_tick_type_t tick_type
Tick type.
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()
struct odp_pktin_hash_proto_t::@107 proto
Protocol header fields for hashing.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.