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 Nokia
4 */
5
14#include <stdio.h>
15#include <string.h>
16#include <signal.h>
17#include <stdlib.h>
18#include <stdint.h>
19#include <inttypes.h>
20
21#include <odp_api.h>
22#include <odp/helper/odph_api.h>
23#include <pktio_common.h>
24
25#define DEBUG_PRINT 0
26#define MAX_WORKERS 64
27#define MAX_PKTIOS (ODP_PKTIO_MAX_INDEX + 1)
28#define MAX_PKTIO_NAME 31
29#define MAX_PKTIO_QUEUES MAX_WORKERS
30#define MAX_PIPE_STAGES 64
31#define MAX_PIPE_QUEUES 1024
32#define MAX_PKT_LEN 1514
33#define MAX_PKT_NUM (16 * 1024)
34#define MIN_PKT_SEG_LEN 64
35#define CHECK_PERIOD 10000
36#define TEST_PASSED_LIMIT 5000
37#define SCHED_MODE_PARAL 1
38#define SCHED_MODE_ATOMIC 2
39#define SCHED_MODE_ORDER 3
40
41typedef struct test_options_t {
42 long int timeout_us;
43 int sched_mode;
44 int num_worker;
45 int num_pktio;
46 int num_pktio_queue;
47 int burst_size;
48 int pipe_stages;
49 int pipe_queues;
50 uint32_t pipe_queue_size;
51 uint8_t collect_stat;
52 uint32_t wait_sec;
53 char pktio_name[MAX_PKTIOS][MAX_PKTIO_NAME + 1];
54
55} test_options_t;
56
57typedef struct {
58 int worker_id;
59 void *test_global_ptr;
60} worker_arg_t;
61
62typedef struct ODP_ALIGNED_CACHE {
63 uint64_t rx_pkt;
64 uint64_t tx_pkt;
65 uint64_t pipe_pkt;
66 uint64_t tx_drop;
67 uint64_t pipe_drop;
68 uint64_t tmo;
69} worker_stat_t;
70
71typedef struct pktin_queue_context_t {
72 /* Queue context must start with stage and idx */
73 uint16_t stage;
74 uint16_t queue_idx;
75
76 uint8_t dst_pktio;
77 uint8_t dst_queue;
78 uint8_t src_pktio;
79 uint8_t src_queue;
80 odp_pktout_queue_t dst_pktout;
81} pktin_queue_context_t;
82
83typedef struct pipe_queue_context_t {
84 /* Queue context must start with stage and idx. */
85 uint16_t stage;
86 uint16_t queue_idx;
87} pipe_queue_context_t;
88
89typedef struct {
90 volatile int stop_workers;
91 odp_barrier_t worker_start;
92
93 test_options_t opt;
94
95 int max_workers;
96 odp_cpumask_t cpumask;
97 odp_instance_t instance;
98
99 int worker_cpu[MAX_WORKERS];
100
101 odp_pool_t pool;
102 uint32_t pkt_len;
103 uint32_t pkt_num;
104
105 struct {
106 odp_pktio_t pktio;
107 int pktio_index;
108 int started;
109 odph_ethaddr_t my_addr;
110 odp_queue_t input_queue[MAX_PKTIO_QUEUES];
111 odp_pktout_queue_t pktout[MAX_PKTIO_QUEUES];
112 pktin_queue_context_t queue_context[MAX_PKTIO_QUEUES];
113
114 } pktio[MAX_PKTIOS];
115
116 struct {
117 odp_timer_pool_t timer_pool;
118 odp_pool_t timeout_pool;
119 uint64_t timeout_tick;
120 odp_timer_t timer[MAX_PKTIOS][MAX_PKTIO_QUEUES];
121
122 } timer;
123
124 struct {
125 odp_queue_t queue[MAX_PIPE_QUEUES];
126 } pipe_queue[MAX_PKTIOS][MAX_PIPE_STAGES];
127
128 struct {
129 pipe_queue_context_t ctx;
130 } pipe_queue_ctx[MAX_PIPE_STAGES][MAX_PIPE_QUEUES];
131
132 worker_arg_t worker_arg[MAX_WORKERS];
133
134 worker_stat_t worker_stat[MAX_WORKERS];
135 uint64_t rx_pkt_sum;
136 uint64_t tx_pkt_sum;
137
138 odp_schedule_config_t schedule_config;
139
140} test_global_t;
141
143enum longopt_only {
144 OPT_WAIT_LINK = 256
145};
146
147static test_global_t *test_global;
148
149static inline void set_dst_eth_addr(odph_ethaddr_t *eth_addr, int index)
150{
151 eth_addr->addr[0] = 0x02;
152 eth_addr->addr[1] = 0;
153 eth_addr->addr[2] = 0;
154 eth_addr->addr[3] = 0;
155 eth_addr->addr[4] = 0;
156 eth_addr->addr[5] = index;
157}
158
159static inline void fill_eth_addr(odp_packet_t pkt[], int num,
160 test_global_t *test_global, int out)
161{
162 odph_ethhdr_t *eth;
163 int i;
164
165 for (i = 0; i < num; ++i) {
166 eth = odp_packet_data(pkt[i]);
167
168 eth->src = test_global->pktio[out].my_addr;
169 set_dst_eth_addr(&eth->dst, out);
170 }
171}
172
173static inline void send_packets(test_global_t *test_global,
174 odp_packet_t pkt[], int num_pkt,
175 int output, odp_pktout_queue_t pktout,
176 int worker_id)
177{
178 int sent, drop;
179
180 fill_eth_addr(pkt, num_pkt, test_global, output);
181
182 sent = odp_pktout_send(pktout, pkt, num_pkt);
183
184 if (odp_unlikely(sent < 0))
185 sent = 0;
186
187 drop = num_pkt - sent;
188
189 if (odp_unlikely(drop > 0))
190 odp_packet_free_multi(&pkt[sent], drop);
191
192 if (odp_unlikely(test_global->opt.collect_stat)) {
193 test_global->worker_stat[worker_id].tx_pkt += sent;
194 test_global->worker_stat[worker_id].tx_drop += drop;
195 }
196}
197
198static int worker_thread_direct(void *arg)
199{
200 int num_pkt, out;
201 odp_pktout_queue_t pktout;
202 odp_queue_t queue;
203 pktin_queue_context_t *queue_context;
204 worker_arg_t *worker_arg = arg;
205 test_global_t *test_global = worker_arg->test_global_ptr;
206 int worker_id = worker_arg->worker_id;
207 uint32_t polls = 0;
208 int burst_size = test_global->opt.burst_size;
209
210 printf("Worker %i started\n", worker_id);
211
212 /* Wait for other workers to start */
213 odp_barrier_wait(&test_global->worker_start);
214
215 while (1) {
216 odp_event_t ev[burst_size];
217 odp_packet_t pkt[burst_size];
218
219 polls++;
220
221 if (polls == CHECK_PERIOD) {
222 polls = 0;
223 if (test_global->stop_workers)
224 break;
225 }
226
227 num_pkt = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
228 ev, burst_size);
229
230 if (num_pkt <= 0)
231 continue;
232
233 queue_context = odp_queue_context(queue);
234
235 if (DEBUG_PRINT)
236 printf("worker %i: [%i/%i] -> [%i/%i], %i packets\n",
237 worker_id,
238 queue_context->src_pktio,
239 queue_context->src_queue,
240 queue_context->dst_pktio,
241 queue_context->dst_queue, num_pkt);
242
243 odp_packet_from_event_multi(pkt, ev, num_pkt);
244
245 pktout = queue_context->dst_pktout;
246 out = queue_context->dst_pktio;
247
248 send_packets(test_global, pkt, num_pkt, out, pktout, worker_id);
249
250 if (odp_unlikely(test_global->opt.collect_stat))
251 test_global->worker_stat[worker_id].rx_pkt += num_pkt;
252 }
253
254 /*
255 * Free prefetched packets before exiting worker thread as
256 * such packets can block main thread event cleanup or
257 * cause buffer leak.
258 */
260 while (1) {
261 odp_event_t ev;
262
264 if (ev == ODP_EVENT_INVALID)
265 break;
266 odp_event_free(ev);
267 }
268
269 /* Non-prefetched events in scheduler are cleaned up by main thread */
270 printf("Worker %i stopped\n", worker_id);
271
272 return 0;
273}
274
275static inline void enqueue_events(odp_queue_t dst_queue, odp_event_t ev[],
276 int num, int worker_id)
277{
278 int sent, drop;
279
280 sent = odp_queue_enq_multi(dst_queue, ev, num);
281
282 if (odp_unlikely(sent < 0))
283 sent = 0;
284
285 drop = num - sent;
286
287 if (odp_unlikely(drop))
288 odp_event_free_multi(&ev[sent], drop);
289
290 if (odp_unlikely(test_global->opt.collect_stat))
291 test_global->worker_stat[worker_id].pipe_drop += drop;
292}
293
294static inline odp_queue_t next_queue(test_global_t *test_global, int input,
295 uint16_t stage, uint16_t queue_idx)
296{
297 return test_global->pipe_queue[input][stage].queue[queue_idx];
298}
299
300static int worker_thread_pipeline(void *arg)
301{
302 int i, num_pkt, input, output, output_queue;
303 odp_queue_t queue, dst_queue;
304 odp_pktout_queue_t pktout;
305 pipe_queue_context_t *pipe_context;
306 uint16_t stage, queue_idx;
307 worker_arg_t *worker_arg = arg;
308 test_global_t *test_global = worker_arg->test_global_ptr;
309 int worker_id = worker_arg->worker_id;
310 int pipe_stages = test_global->opt.pipe_stages;
311 int pipe_queues = test_global->opt.pipe_queues;
312 int num_pktio = test_global->opt.num_pktio;
313 int num_pktio_queue = test_global->opt.num_pktio_queue;
314 uint32_t polls = 0;
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 num_pkt = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT,
327 ev, burst_size);
328
329 polls++;
330
331 if (polls == CHECK_PERIOD) {
332 polls = 0;
333 if (test_global->stop_workers)
334 break;
335 }
336
337 if (num_pkt <= 0)
338 continue;
339
340 pipe_context = odp_queue_context(queue);
341 stage = pipe_context->stage;
342 queue_idx = pipe_context->queue_idx;
343
344 /* A queue is connected to a single input interface. All
345 * packets from a queue are from the same interface. */
347
348 if (DEBUG_PRINT)
349 printf("worker %i: stage %u, idx %u, %i packets\n",
350 worker_id, stage, queue_idx, num_pkt);
351
352 if (stage == 0) {
353 if (odp_unlikely(test_global->opt.collect_stat))
354 test_global->worker_stat[worker_id].rx_pkt +=
355 num_pkt;
356
357 /* The first stage (packet input). Forward packet flows
358 * into first pipeline queues. */
359 if (pipe_queues > num_pktio_queue) {
360 /* More pipeline queues than input queues.
361 * Use flow hash to spread flows into pipeline
362 * queues. */
363 odp_packet_t p;
364 worker_stat_t *stat;
365 uint32_t hash;
366 uint16_t idx;
367 int drop = 0;
368
369 stat = &test_global->worker_stat[worker_id];
370
371 for (i = 0; i < num_pkt; i++) {
372 p = odp_packet_from_event(ev[i]);
373 hash = odp_packet_flow_hash(p);
374 idx = queue_idx;
375
377 idx = hash % pipe_queues;
378
379 dst_queue = next_queue(test_global,
380 input, stage,
381 idx);
382
383 if (odp_queue_enq(dst_queue, ev[i])) {
384 odp_event_free(ev[i]);
385 drop++;
386 }
387 }
388
389 if (odp_unlikely(test_global->opt.collect_stat))
390 stat->pipe_drop += drop;
391 } else {
392 queue_idx = queue_idx % pipe_queues;
393 dst_queue = next_queue(test_global, input,
394 stage, queue_idx);
395
396 enqueue_events(dst_queue, ev, num_pkt,
397 worker_id);
398 }
399 continue;
400 }
401
402 if (stage < pipe_stages) {
403 /* Middle stages */
404 dst_queue = next_queue(test_global, input, stage,
405 queue_idx);
406 enqueue_events(dst_queue, ev, num_pkt, worker_id);
407
408 if (odp_unlikely(test_global->opt.collect_stat))
409 test_global->worker_stat[worker_id].pipe_pkt +=
410 num_pkt;
411
412 continue;
413 }
414
415 /* The last stage, send packets out */
416 odp_packet_from_event_multi(pkt, ev, num_pkt);
417
418 /* If single interface loopback, otherwise forward to the next
419 * interface. */
420 output = (input + 1) % num_pktio;
421 output_queue = queue_idx % num_pktio_queue;
422 pktout = test_global->pktio[output].pktout[output_queue];
423
424 send_packets(test_global, pkt, num_pkt, output, pktout,
425 worker_id);
426 }
427
428 printf("Worker %i stopped\n", worker_id);
429
430 return 0;
431}
432
433static int worker_thread_timers(void *arg)
434{
435 int num, num_pkt, out, tmos, i, src_pktio, src_queue;
436 odp_pktout_queue_t pktout;
437 odp_queue_t queue;
438 pktin_queue_context_t *queue_context;
439 odp_timer_t timer;
441 odp_timer_start_t start_param;
442 worker_arg_t *worker_arg = arg;
443 test_global_t *test_global = worker_arg->test_global_ptr;
444 int worker_id = worker_arg->worker_id;
445 uint32_t polls = 0;
446 int burst_size = test_global->opt.burst_size;
447 uint64_t tick = test_global->timer.timeout_tick;
448
449 printf("Worker (timers) %i started\n", worker_id);
450
451 /* Wait for other workers to start */
452 odp_barrier_wait(&test_global->worker_start);
453
454 while (1) {
455 odp_event_t ev[burst_size];
456 odp_packet_t pkt[burst_size];
457
459 ev, burst_size);
460
461 polls++;
462
463 if (polls == CHECK_PERIOD) {
464 polls = 0;
465 if (test_global->stop_workers)
466 break;
467 }
468
469 if (num <= 0)
470 continue;
471
472 tmos = 0;
473 queue_context = odp_queue_context(queue);
474 src_pktio = queue_context->src_pktio;
475 src_queue = queue_context->src_queue;
476 timer = test_global->timer.timer[src_pktio][src_queue];
477 start_param.tick_type = ODP_TIMER_TICK_REL;
478 start_param.tick = tick;
479 start_param.tmo_ev = ODP_EVENT_INVALID;
480
481 for (i = 0; i < num; i++) {
482 if (odp_unlikely(odp_event_type(ev[i]) ==
483 ODP_EVENT_TIMEOUT)) {
484 tmos++;
485
486 start_param.tmo_ev = ev[i];
487 ret = odp_timer_start(timer, &start_param);
488
489 if (odp_unlikely(ret != ODP_TIMER_SUCCESS)) {
490 /* Should never happen. Timeout event
491 * has been received, timer should be
492 * ready to be set again. */
493 printf("Expired timer reset failed "
494 "%i\n", ret);
495 odp_event_free(ev[i]);
496 }
497
498 if (odp_unlikely(tmos > 1)) {
499 /* Should never happen */
500 printf("Too many timeouts\n");
501 }
502 } else {
503 pkt[i - tmos] = odp_packet_from_event(ev[i]);
504 }
505 }
506
507 if (tmos == 0) {
508 /* Reset timer with existing timeout event */
509 ret = odp_timer_restart(timer, &start_param);
510
511 if (odp_unlikely(ret != ODP_TIMER_SUCCESS &&
512 ret != ODP_TIMER_FAIL)) {
513 /* Tick period is too short or long. Normally,
514 * reset either succeeds or fails due to timer
515 * expiration, in which case timeout event will
516 * be received soon and reset will be done
517 * then. */
518 printf("Timer reset failed %i\n", ret);
519 }
520 }
521
522 num_pkt = num - tmos;
523
524 if (DEBUG_PRINT)
525 printf("worker %i: [%i/%i] -> [%i/%i], %i packets "
526 "%i timeouts\n",
527 worker_id,
528 queue_context->src_pktio,
529 queue_context->src_queue,
530 queue_context->dst_pktio,
531 queue_context->dst_queue, num_pkt, tmos);
532
533 if (odp_unlikely(test_global->opt.collect_stat && tmos))
534 test_global->worker_stat[worker_id].tmo += tmos;
535
536 if (odp_unlikely(num_pkt == 0))
537 continue;
538
539 pktout = queue_context->dst_pktout;
540 out = queue_context->dst_pktio;
541
542 send_packets(test_global, pkt, num_pkt, out, pktout, worker_id);
543
544 if (odp_unlikely(test_global->opt.collect_stat))
545 test_global->worker_stat[worker_id].rx_pkt += num_pkt;
546 }
547
548 printf("Worker %i stopped\n", worker_id);
549
550 return 0;
551}
552
553static void sig_handler(int signo)
554{
555 (void)signo;
556
557 if (test_global) {
558 test_global->stop_workers = 1;
559 odp_mb_full();
560 }
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 int ret = 0;
1490
1491 /* Let helper collect its own arguments (e.g. --odph_proc) */
1492 argc = odph_parse_options(argc, argv);
1493 if (odph_options(&helper_options)) {
1494 printf("Error: reading ODP helper options failed.\n");
1495 exit(EXIT_FAILURE);
1496 }
1497
1498 signal(SIGINT, sig_handler);
1499
1500 if (parse_options(argc, argv, &test_options))
1501 return -1;
1502
1503 /* List features not to be used (may optimize performance) */
1504 odp_init_param_init(&init);
1505 init.not_used.feat.cls = 1;
1506 init.not_used.feat.compress = 1;
1507 init.not_used.feat.crypto = 1;
1508 init.not_used.feat.ipsec = 1;
1509 init.not_used.feat.tm = 1;
1510 init.not_used.feat.timer = 1;
1511
1512 if (test_options.timeout_us)
1513 init.not_used.feat.timer = 0;
1514
1515 init.mem_model = helper_options.mem_model;
1516
1517 /* Init ODP before calling anything else */
1518 if (odp_init_global(&instance, &init, NULL)) {
1519 printf("Error: Global init failed.\n");
1520 return -1;
1521 }
1522
1523 /* Init this thread */
1524 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
1525 printf("Error: Local init failed.\n");
1526 return -1;
1527 }
1528
1529 /* Reserve memory for args from shared mem */
1530 shm = odp_shm_reserve("test_global", sizeof(test_global_t),
1531 ODP_CACHE_LINE_SIZE, 0);
1532
1533 if (shm == ODP_SHM_INVALID) {
1534 printf("Error: shm reserve failed.\n");
1535 return -1;
1536 }
1537
1538 test_global = odp_shm_addr(shm);
1539 memset(test_global, 0, sizeof(test_global_t));
1540
1541 test_global->instance = instance;
1542 test_global->pool = ODP_POOL_INVALID;
1543
1544 memcpy(&test_global->opt, &test_options, sizeof(test_options_t));
1545
1547
1548 if (config_setup(test_global))
1549 goto quit;
1550
1551 print_config(test_global);
1552
1553 if (open_pktios(test_global))
1554 goto quit;
1555
1556 link_pktios(test_global);
1557
1558 if (create_pipeline_queues(test_global))
1559 goto quit;
1560
1561 if (create_timers(test_global))
1562 goto quit;
1563
1564 if (start_pktios(test_global))
1565 goto quit;
1566
1567 odp_barrier_init(&test_global->worker_start,
1568 test_global->opt.num_worker + 1);
1569
1570 start_workers(thread, test_global);
1571
1572 if (start_timers(test_global)) {
1573 test_global->stop_workers = 1;
1574 odp_mb_full();
1575 }
1576
1577 /* Synchronize pktio configuration with workers. Worker are now ready
1578 * to process packets. */
1579 odp_barrier_wait(&test_global->worker_start);
1580
1581 t1 = odp_time_local();
1582
1583 wait_workers(thread, test_global);
1584
1585 t2 = odp_time_local();
1586
1587quit:
1588 stop_pktios(test_global);
1589 empty_queues(ODP_TIME_SEC_IN_NS / 2);
1590 close_pktios(test_global);
1591 destroy_pipeline_queues(test_global);
1592 destroy_timers(test_global);
1593
1594 if (test_global->opt.collect_stat) {
1595 print_stat(test_global, odp_time_diff_ns(t2, t1));
1596
1597 /* Encode return value for validation test usage. */
1598 if (test_global->rx_pkt_sum > TEST_PASSED_LIMIT)
1599 ret += 1;
1600
1601 if (test_global->tx_pkt_sum > TEST_PASSED_LIMIT)
1602 ret += 2;
1603 }
1604 test_global = NULL;
1605 odp_mb_full();
1606
1607 if (odp_shm_free(shm)) {
1608 printf("Error: shm free failed.\n");
1609 ret = -1;
1610 }
1611
1612 if (odp_term_local()) {
1613 printf("Error: term local failed.\n");
1614 ret = -1;
1615 }
1616
1617 if (odp_term_global(instance)) {
1618 printf("Error: term global failed.\n");
1619 ret = -1;
1620 }
1621
1622 return ret;
1623}
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_mb_full(void)
Full memory barrier.
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 crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
struct odp_feature_t::@174 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()
struct odp_pktin_hash_proto_t::@107 proto
Protocol header fields for hashing.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.