API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_sched_latency.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2016-2018 Linaro Limited
3 * Copyright (c) 2020-2025 Nokia
4 */
5
14#include <string.h>
15#include <stdlib.h>
16#include <inttypes.h>
17
18/* ODP main header */
19#include <odp_api.h>
20
21/* ODP helper for Linux apps */
22#include <odp/helper/odph_api.h>
23
24/* Result export helpers */
25#include <export_results.h>
26
27/* GNU lib C */
28#include <getopt.h>
29
30#define MAX_QUEUES 4096
31#define MAX_GROUPS 64
32#define EVENT_POOL_SIZE (1024 * 1024)
33#define MAIN_THREAD 1
35/* Test priorities */
36#define NUM_PRIOS 2
37#define HI_PRIO 0
38#define LO_PRIO 1
39
40/* Test event forwarding mode */
41#define EVENT_FORWARD_RAND 0
42#define EVENT_FORWARD_INC 1
43#define EVENT_FORWARD_NONE 2
44
45#define MAX_BURST_SIZE 64
46
48typedef enum {
49 WARM_UP,
50 COOL_DOWN,
51 TRAFFIC,
52 SAMPLE
53} event_type_t;
54
56typedef struct {
57 odp_time_t time_stamp;
58 event_type_t type;
59 int src_idx[NUM_PRIOS];
60 int prio;
61 int warm_up_rounds;
62} test_event_t;
63
65typedef struct {
66 odp_time_t enq_time;
67 odp_event_t sample_collector[MAX_BURST_SIZE];
68 odp_atomic_u32_t collector_idx;
69 odp_atomic_u32_t ready;
70} test_track_t;
71
73typedef struct {
74 unsigned int cpu_count;
75 odp_schedule_sync_t sync_type;
76 int forward_mode;
77 int num_group;
78 int isolate;
79 int test_rounds;
80 int warm_up_rounds;
81 int burst_size;
82 struct {
83 int queues;
84 int events;
85 int samples;
86 odp_bool_t events_per_queue;
88 } prio[NUM_PRIOS];
89 odp_bool_t sample_per_prio;
90 uint64_t wait_ns;
91} test_args_t;
92
94typedef struct {
95 uint64_t events;
96 uint64_t samples;
97 odp_time_t tot;
98 odp_time_t min;
99 odp_time_t max;
100 uint64_t max_idx;
101} test_stat_t;
102
104typedef struct ODP_ALIGNED_CACHE {
105 test_stat_t prio[NUM_PRIOS];
106} core_stat_t;
107
109typedef struct {
111 core_stat_t core_stat[ODP_THREAD_COUNT_MAX];
112 odp_barrier_t barrier;
113 odp_pool_t pool;
114 test_args_t args;
115 odp_queue_t queue[NUM_PRIOS][MAX_QUEUES];
116 test_common_options_t common_options;
117 test_track_t track[NUM_PRIOS];
119 odp_schedule_group_t group[NUM_PRIOS][MAX_GROUPS];
120
121} test_globals_t;
122
128static void clear_sched_queues(test_globals_t *globals)
129{
130 odp_event_t ev;
131 odp_buffer_t buf;
132 test_event_t *event;
133 int i, j;
134 odp_queue_t fromq;
135
136 /* Allocate the cool_down event. */
137 buf = odp_buffer_alloc(globals->pool);
138 if (buf == ODP_BUFFER_INVALID)
139 ODPH_ABORT("Buffer alloc failed.\n");
140
141 event = odp_buffer_addr(buf);
142 event->type = COOL_DOWN;
143 ev = odp_buffer_to_event(buf);
144
145 for (i = 0; i < NUM_PRIOS; i++) {
146 for (j = 0; j < globals->args.prio[i].queues; j++) {
147 /* Enqueue cool_down event on each queue. */
148 if (odp_queue_enq(globals->queue[i][j], ev))
149 ODPH_ABORT("Queue enqueue failed.\n");
150
151 /* Invoke scheduler until cool_down event has been
152 * received. */
153 while (1) {
154 ev = odp_schedule(NULL, ODP_SCHED_WAIT);
155 buf = odp_buffer_from_event(ev);
156 event = odp_buffer_addr(buf);
157 if (event->type == COOL_DOWN)
158 break;
159 odp_event_free(ev);
160 }
161 }
162 }
163
164 /* Free the cool_down event. */
165 odp_event_free(ev);
166
167 /* Call odp_schedule() to trigger a release of any scheduler context. */
168 ev = odp_schedule(&fromq, ODP_SCHED_NO_WAIT);
169 if (ev != ODP_EVENT_INVALID)
170 ODPH_ABORT("Queue %" PRIu64 " not empty.\n",
171 odp_queue_to_u64(fromq));
172}
173
188static int enqueue_events(int prio, int num_queues, int num_events,
189 int num_samples, odp_bool_t div_events,
190 test_globals_t *globals)
191{
192 odp_buffer_t buf[num_events + num_samples];
193 odp_event_t ev[num_events + num_samples];
194 odp_queue_t queue;
195 test_event_t *event;
196 int i, j, ret;
197 int enq_events;
198 int events_per_queue;
199 int tot_events;
200 int rdy_events = 0;
201
202 tot_events = num_events + num_samples;
203
204 if (!num_queues || !tot_events)
205 return 0;
206
207 events_per_queue = tot_events;
208 if (div_events)
209 events_per_queue = (tot_events + num_queues - 1) / num_queues;
210
211 for (i = 0; i < num_queues; i++) {
212 queue = globals->queue[prio][i];
213
214 ret = odp_buffer_alloc_multi(globals->pool, buf,
215 events_per_queue);
216 if (ret != events_per_queue) {
217 ODPH_ERR("Buffer alloc failed. Try increasing EVENT_POOL_SIZE.\n");
218 ret = ret < 0 ? 0 : ret;
219 odp_buffer_free_multi(buf, ret);
220 return -1;
221 }
222 for (j = 0; j < events_per_queue; j++) {
223 if (!odp_buffer_is_valid(buf[j])) {
224 ODPH_ERR("Buffer alloc failed\n");
225 odp_buffer_free_multi(buf, events_per_queue);
226 return -1;
227 }
228
229 event = odp_buffer_addr(buf[j]);
230 memset(event, 0, sizeof(test_event_t));
231
232 /* Latency isn't measured from the first processing
233 * rounds. */
234 if (num_samples > 0) {
235 event->type = WARM_UP;
236 event->warm_up_rounds = 0;
237 num_samples--;
238 } else {
239 event->type = TRAFFIC;
240 }
241 event->src_idx[prio] = i;
242 event->prio = prio;
243 ev[j] = odp_buffer_to_event(buf[j]);
244 }
245
246 enq_events = 0;
247 do {
248 ret = odp_queue_enq_multi(queue, &ev[enq_events],
249 events_per_queue -
250 enq_events);
251 if (ret < 0) {
252 ODPH_ERR("Queue enqueue failed.\n");
253 return -1;
254 }
255 enq_events += ret;
256 } while (enq_events < events_per_queue);
257
258 rdy_events += events_per_queue;
259 if (div_events && rdy_events >= tot_events)
260 return 0;
261 }
262 return 0;
263}
264
270static int output_results(test_globals_t *globals)
271{
272 test_stat_t *lat;
274 test_stat_t total;
275 test_args_t *args;
276 uint64_t avg;
277 unsigned int i, j;
278
279 args = &globals->args;
280 stype = globals->args.sync_type;
281
282 printf("\n%s queue scheduling latency\n",
283 (stype == ODP_SCHED_SYNC_ATOMIC) ? "ATOMIC" :
284 ((stype == ODP_SCHED_SYNC_ORDERED) ? "ORDERED" : "PARALLEL"));
285
286 printf(" Forwarding mode: %s\n",
287 (args->forward_mode == EVENT_FORWARD_RAND) ? "random" :
288 ((args->forward_mode == EVENT_FORWARD_INC) ? "incremental" :
289 "none"));
290
291 printf(" Scheduling mode: %s\n", args->burst_size ? "burst" : "single");
292 if (args->burst_size)
293 printf(" Burst size: %d\n", args->burst_size);
294
295 printf(" LO_PRIO queues: %i\n", args->prio[LO_PRIO].queues);
296 if (args->prio[LO_PRIO].events_per_queue)
297 printf(" LO_PRIO event per queue: %i\n",
298 args->prio[LO_PRIO].events);
299 else
300 printf(" LO_PRIO events: %i\n", args->prio[LO_PRIO].events);
301
302 printf(" LO_PRIO sample events: %i\n", args->prio[LO_PRIO].samples);
303
304 printf(" HI_PRIO queues: %i\n", args->prio[HI_PRIO].queues);
305 if (args->prio[HI_PRIO].events_per_queue)
306 printf(" HI_PRIO event per queue: %i\n\n",
307 args->prio[HI_PRIO].events);
308 else
309 printf(" HI_PRIO events: %i\n", args->prio[HI_PRIO].events);
310
311 printf(" HI_PRIO sample events: %i\n\n", args->prio[HI_PRIO].samples);
312
313 if (globals->common_options.is_export) {
314 if (test_common_write("high priority avg (ns),high priority min (ns),"
315 "high priority max (ns),low priority avg (ns),"
316 "low priority min (ns),low priority max (ns)\n")) {
317 ODPH_ERR("Export failed\n");
318 test_common_write_term();
319 return -1;
320 }
321 }
322
323 for (i = 0; i < NUM_PRIOS; i++) {
324 memset(&total, 0, sizeof(test_stat_t));
326 total.max = ODP_TIME_NULL;
327 total.tot = ODP_TIME_NULL;
328
329 printf("%s priority\n"
330 "Thread Avg[ns] Min[ns] Max[ns] Samples Total Max idx\n"
331 "-----------------------------------------------------------------------\n",
332 i == HI_PRIO ? "HIGH" : "LOW");
333 for (j = 1; j <= args->cpu_count; j++) {
334 lat = &globals->core_stat[j].prio[i];
335
336 if (lat->samples == 0) {
337 printf("%-8d N/A\n", j);
338 continue;
339 }
340
341 if (odp_time_cmp(lat->max, total.max) > 0)
342 total.max = lat->max;
343 if (odp_time_cmp(lat->min, total.min) < 0)
344 total.min = lat->min;
345 total.tot = odp_time_sum(total.tot, lat->tot);
346 total.samples += lat->samples;
347 total.events += lat->events;
348
349 avg = lat->samples ? odp_time_to_ns(lat->tot) / lat->samples : 0;
350 printf("%-8d %-10" PRIu64 " %-10" PRIu64 " "
351 "%-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 "\n",
352 j, avg, odp_time_to_ns(lat->min), odp_time_to_ns(lat->max),
353 lat->samples, lat->events, lat->max_idx);
354 }
355 printf("-----------------------------------------------------------------------\n");
356 if (total.samples == 0) {
357 printf("Total N/A\n\n");
358 continue;
359 }
360
361 avg = total.samples ? odp_time_to_ns(total.tot) / total.samples : 0;
362 printf("Total %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " "
363 "%-10" PRIu64 " %-10" PRIu64 "\n\n", avg, odp_time_to_ns(total.min),
364 odp_time_to_ns(total.max), total.samples, total.events);
365
366 if (globals->common_options.is_export) {
367 if (test_common_write("%" PRIu64 ",%" PRIu64 ",%" PRIu64 "%s",
368 avg, odp_time_to_ns(total.min),
369 odp_time_to_ns(total.max), i == 0 ? "," : "")) {
370 ODPH_ERR("Export failed\n");
371 test_common_write_term();
372 return -1;
373 }
374 }
375 }
376
377 if (globals->common_options.is_export)
378 test_common_write_term();
379
380 return 0;
381}
382
383static int join_groups(test_globals_t *globals, int thr)
384{
385 odp_thrmask_t thrmask;
387 int i, num;
388 int num_group = globals->args.num_group;
389
390 if (num_group <= 0)
391 return 0;
392
393 num = num_group;
394 if (globals->args.isolate)
395 num = 2 * num_group;
396
397 odp_thrmask_zero(&thrmask);
398 odp_thrmask_set(&thrmask, thr);
399
400 for (i = 0; i < num; i++) {
401 if (globals->args.isolate)
402 group = globals->group[i % 2][i / 2];
403 else
404 group = globals->group[0][i];
405
406 if (odp_schedule_group_join(group, &thrmask)) {
407 ODPH_ERR("Group join failed %i (thr %i)\n", i, thr);
408 return -1;
409 }
410 }
411
412 return 0;
413}
414
415static inline void record_stats(test_stat_t *stats, odp_time_t sched, odp_time_t enq)
416{
417 /* Skip measurement if enq has not been set yet */
419 return;
420
421 odp_time_t latency = odp_time_diff(sched, enq);
422
423 if (odp_time_cmp(latency, stats->max) > 0) {
424 stats->max = latency;
425 stats->max_idx = stats->samples;
426 }
427
428 if (odp_time_cmp(latency, stats->min) < 0)
429 stats->min = latency;
430
431 stats->samples++;
432 stats->tot = odp_time_sum(stats->tot, latency);
433}
434
435static inline int get_dst_idx(const test_args_t *args, const test_event_t *event)
436{
437 int dst_idx;
438
439 if (args->forward_mode != EVENT_FORWARD_NONE)
440 dst_idx = event->src_idx[event->prio] + 1;
441 else
442 dst_idx = event->src_idx[event->prio];
443
444 if (dst_idx >= args->prio[event->prio].queues)
445 dst_idx = 0;
446
447 return dst_idx;
448}
449
450static int test_schedule_single(int thr, test_globals_t *globals)
451{
452 const int warm_up_rounds = globals->args.warm_up_rounds;
453 const uint64_t test_rounds = globals->args.test_rounds * (uint64_t)1000000;
454 const uint64_t wait_ns = globals->args.wait_ns;
455 odp_event_t ev;
456 odp_buffer_t buf;
457 odp_queue_t dst_queue;
458 uint64_t i;
459 test_event_t *event;
460 test_stat_t *stats;
461 odp_time_t time;
462 int dst_idx;
463
464 for (i = 0; i < test_rounds; i++) {
465 ev = odp_schedule(NULL, ODP_SCHED_WAIT);
466 time = odp_time_global_strict();
467 buf = odp_buffer_from_event(ev);
468 event = odp_buffer_addr(buf);
469
470 if (odp_unlikely(wait_ns))
471 odp_time_wait_ns(wait_ns);
472
473 stats = &globals->core_stat[thr].prio[event->prio];
474
475 if (event->type == SAMPLE) {
476 record_stats(stats, time, event->time_stamp);
477
478 /* Move sample event to a different priority */
479 if (!globals->args.sample_per_prio &&
480 globals->args.prio[!event->prio].queues)
481 event->prio = !event->prio;
482 }
483
484 if (odp_unlikely(event->type == WARM_UP)) {
485 event->warm_up_rounds++;
486 if (event->warm_up_rounds >= warm_up_rounds)
487 event->type = SAMPLE;
488 } else {
489 stats->events++;
490 }
491
492 /* Move event to next queue if forwarding is enabled */
493 dst_idx = get_dst_idx(&globals->args, event);
494 event->src_idx[event->prio] = dst_idx;
495 dst_queue = globals->queue[event->prio][dst_idx];
496
497 if (event->type == SAMPLE)
498 event->time_stamp = odp_time_global_strict();
499
500 if (odp_queue_enq(dst_queue, ev)) {
501 ODPH_ERR("[%i] Queue enqueue failed.\n", thr);
502 odp_event_free(ev);
503 return -1;
504 }
505 }
506
507 return 0;
508}
509
525static inline int enq_burst(int thr, test_globals_t *globals, odp_event_t burst_array[],
526 uint32_t burst_size)
527{
528 const test_args_t *args = &globals->args;
529 test_track_t *track = globals->track;
530 test_event_t *event;
531 odp_queue_t dst_queue;
532 uint32_t i;
533 int num_enq, new_prio;
534 int enq = 0;
535 int dst_idx = 0;
536
537 event = odp_buffer_addr(odp_buffer_from_event(burst_array[0]));
538 if (event->type == SAMPLE) {
539 /* Move sample event to different priority if possible */
540 if (!args->sample_per_prio && args->prio[!event->prio].queues) {
541 new_prio = !event->prio;
542 event->prio = new_prio;
543 } else {
544 new_prio = event->prio;
545 }
546
547 dst_idx = get_dst_idx(&globals->args, event);
548 dst_queue = globals->queue[event->prio][dst_idx];
549
550 for (i = 0; i < burst_size; i++) {
551 event = odp_buffer_addr(odp_buffer_from_event(burst_array[i]));
552 event->prio = new_prio;
553 event->src_idx[event->prio] = dst_idx;
554 }
555
556 track[event->prio].enq_time = odp_time_global_strict();
557 } else {
558 dst_idx = get_dst_idx(&globals->args, event);
559 dst_queue = globals->queue[event->prio][dst_idx];
560
561 for (i = 0; i < burst_size; i++) {
562 event = odp_buffer_addr(odp_buffer_from_event(burst_array[i]));
563 event->src_idx[event->prio] = dst_idx;
564 }
565 }
566
567 do {
568 num_enq = odp_queue_enq_multi(dst_queue, &burst_array[enq], burst_size - enq);
569 if (odp_unlikely(num_enq < 0)) {
570 ODPH_ERR("[%i] Queue enqueue failed.\n", thr);
571 odp_event_free_multi(&burst_array[enq], burst_size - enq);
572 return -1;
573 }
574 enq += num_enq;
575 } while (enq < (int)burst_size);
576
577 return 0;
578}
579
580static inline int collect_enq_sample(int thr, test_globals_t *globals, odp_event_t ev,
581 odp_time_t time, int prio)
582{
583 const uint32_t burst_size = globals->args.burst_size;
584 test_stat_t *stats = &globals->core_stat[thr].prio[prio];
585 test_track_t *track = &globals->track[prio];
586 odp_atomic_u32_t *collector_idx = &track->collector_idx;
587 odp_atomic_u32_t *ready = &track->ready;
588 odp_event_t *sample_collector = track->sample_collector;
589 odp_event_t local_collector[MAX_BURST_SIZE];
590 uint32_t idx, i;
591
592 idx = odp_atomic_fetch_inc_u32(collector_idx);
593 sample_collector[idx] = ev;
594 odp_atomic_add_rel_u32(ready, 1);
595
596 if (idx == burst_size - 1) {
597 while (odp_atomic_load_acq_u32(ready) != burst_size)
599
600 record_stats(stats, time, track->enq_time);
601
602 /* Snapshot the burst to avoid data race with scheduling threads */
603 for (i = 0; i < burst_size; i++)
604 local_collector[i] = sample_collector[i];
605
606 odp_atomic_store_u32(collector_idx, 0);
607 odp_atomic_store_u32(ready, 0);
608
609 if (enq_burst(thr, globals, local_collector, burst_size))
610 return -1;
611 }
612
613 return 0;
614}
615
616static int test_schedule_burst(int thr, test_globals_t *globals)
617{
618 const int warm_up_rounds = globals->args.warm_up_rounds;
619 const uint64_t test_rounds = globals->args.test_rounds * (uint64_t)1000000;
620 const uint32_t burst_size = globals->args.burst_size;
621 const uint64_t wait_ns = globals->args.wait_ns;
622 uint32_t num_sched_ev;
623 odp_event_t ev_burst[MAX_BURST_SIZE];
624 odp_event_t traffic_collector[MAX_BURST_SIZE];
625 odp_event_t ev;
626 test_event_t *event;
627 test_stat_t *stats;
628 odp_time_t time;
629 uint32_t traffic_idx = 0;
630 uint64_t i;
631 uint32_t j;
632
633 for (i = 0; i < test_rounds; i++) {
634 num_sched_ev = odp_schedule_multi(NULL, ODP_SCHED_WAIT,
635 ev_burst, burst_size);
636 time = odp_time_global_strict();
637
638 if (odp_unlikely(wait_ns))
639 odp_time_wait_ns(wait_ns);
640
641 for (j = 0; j < num_sched_ev; j++) {
642 ev = ev_burst[j];
644 stats = &globals->core_stat[thr].prio[event->prio];
645
646 if (odp_unlikely(event->type == WARM_UP)) {
647 event->warm_up_rounds++;
648 if (event->warm_up_rounds >= warm_up_rounds)
649 event->type = SAMPLE;
650 } else {
651 stats->events++;
652 }
653
654 /* Collect events for burst enqueueing */
655 if (event->type == SAMPLE) {
656 if (odp_unlikely(collect_enq_sample(thr, globals, ev, time,
657 event->prio)))
658 return -1;
659 } else {
660 traffic_collector[traffic_idx++] = ev;
661 }
662 }
663
664 if (traffic_idx) {
665 if (odp_unlikely(enq_burst(thr, globals, traffic_collector,
666 traffic_idx) == -1))
667 return -1;
668 traffic_idx = 0;
669 }
670 }
671 return 0;
672}
673
691static int test_schedule(int thr, test_globals_t *globals)
692{
693 odp_event_t ev;
694 test_track_t *track;
695 uint32_t cnt;
696
697 globals->core_stat[thr].prio[HI_PRIO].min = odp_time_global_from_ns(ODP_TIME_HOUR_IN_NS);
698 globals->core_stat[thr].prio[HI_PRIO].max = ODP_TIME_NULL;
699 globals->core_stat[thr].prio[HI_PRIO].tot = ODP_TIME_NULL;
700 globals->core_stat[thr].prio[LO_PRIO].min = odp_time_global_from_ns(ODP_TIME_HOUR_IN_NS);
701 globals->core_stat[thr].prio[LO_PRIO].max = ODP_TIME_NULL;
702 globals->core_stat[thr].prio[LO_PRIO].tot = ODP_TIME_NULL;
703
704 odp_barrier_wait(&globals->barrier);
705
706 if (globals->args.burst_size) {
707 if (test_schedule_burst(thr, globals))
708 return -1;
709 } else {
710 if (test_schedule_single(thr, globals))
711 return -1;
712 }
713
714 /* Clear possible locally stored buffers */
716
717 while (1) {
718 odp_queue_t src_queue;
719
720 ev = odp_schedule(&src_queue, ODP_SCHED_NO_WAIT);
721
722 if (ev == ODP_EVENT_INVALID)
723 break;
724
725 if (odp_queue_enq(src_queue, ev)) {
726 ODPH_ERR("[%i] Queue enqueue failed.\n", thr);
727 odp_event_free(ev);
728 return -1;
729 }
730 }
731
732 odp_barrier_wait(&globals->barrier);
733
734 if (thr == MAIN_THREAD) {
735 if (globals->args.burst_size) {
736 for (int i = 0; i < NUM_PRIOS; i++) {
737 track = &globals->track[i];
738 cnt = odp_atomic_load_u32(&track->collector_idx);
739
740 if (cnt) {
741 odp_event_free_multi(track->sample_collector, cnt);
742 odp_atomic_store_u32(&track->collector_idx, 0);
743 odp_atomic_store_u32(&track->ready, 0);
744 }
745 }
746 }
748 clear_sched_queues(globals);
749 if (output_results(globals))
750 return -1;
751 }
752
753 return 0;
754}
755
764static int run_thread(void *arg ODP_UNUSED)
765{
766 odp_shm_t shm;
767 test_globals_t *globals;
768 test_args_t *args;
769 int thr;
770
771 thr = odp_thread_id();
772
773 shm = odp_shm_lookup("test_globals");
774 globals = odp_shm_addr(shm);
775
776 if (globals == NULL) {
777 ODPH_ERR("Shared mem lookup failed\n");
778 return -1;
779 }
780
781 if (join_groups(globals, thr))
782 return -1;
783
784 if (thr == MAIN_THREAD) {
785 args = &globals->args;
786
787 if (enqueue_events(HI_PRIO, args->prio[HI_PRIO].queues,
788 args->prio[HI_PRIO].events, args->prio[HI_PRIO].samples,
789 !args->prio[HI_PRIO].events_per_queue,
790 globals))
791 return -1;
792
793 if (enqueue_events(LO_PRIO, args->prio[LO_PRIO].queues,
794 args->prio[LO_PRIO].events, args->prio[LO_PRIO].samples,
795 !args->prio[LO_PRIO].events_per_queue,
796 globals))
797 return -1;
798 }
799
800 if (test_schedule(thr, globals))
801 return -1;
802
803 return 0;
804}
805
809static void usage(void)
810{
811 printf("\n"
812 "OpenDataPlane scheduler latency benchmark application.\n"
813 "\n"
814 "Usage: ./odp_sched_latency [options]\n"
815 "Optional OPTIONS:\n"
816 " -c, --count <number> CPU count, 0=all available, default=1\n"
817 " -b, --burst <num> Test burst size. Latency is measured over whole bursts.\n"
818 " -d, --duration <number> Test duration in scheduling rounds (millions), default=10, min=1\n"
819 " -f, --forward-mode <mode> Selection of target queue\n"
820 " 0: Random (default)\n"
821 " 1: Incremental\n"
822 " 2: Use source queue\n"
823 " -g, --num_group <num> Number of schedule groups. Round robins queues into groups.\n"
824 " -1: SCHED_GROUP_WORKER\n"
825 " 0: SCHED_GROUP_ALL (default)\n"
826 " -i, --isolate <mode> Select if shared or isolated groups are used. Ignored when num_group <= 0.\n"
827 " 0: All queues share groups (default)\n"
828 " 1: Separate groups for high and low priority queues. Creates 2xnum_group groups.\n"
829 " -l, --lo-prio-queues <number> Number of low priority scheduled queues (default=64)\n"
830 " -t, --hi-prio-queues <number> Number of high priority scheduled queues (default=16)\n"
831 " -m, --lo-prio-events-per-queue <number> Number of events per low priority queue (default=32).\n"
832 " Does not include sample event.\n"
833 " -n, --hi-prio-events-per-queue <number> Number of events per high priority queues (default=0)\n"
834 " Does not include sample event.\n"
835 " -o, --lo-prio-events <number> Total number of low priority events. Overrides the\n"
836 " number of events per queue, does not include sample event.\n"
837 " -p, --hi-prio-events <number> Total number of high priority events. Overrides the\n"
838 " number of events per queue, does not include sample event.\n"
839 " -r --sample-per-prio Allocate a separate sample event for each priority. By default\n"
840 " a single sample event is used and its priority is changed after\n"
841 " each processing round.\n"
842 " -s, --sync Scheduled queues' sync type\n"
843 " 0: ODP_SCHED_SYNC_PARALLEL (default)\n"
844 " 1: ODP_SCHED_SYNC_ATOMIC\n"
845 " 2: ODP_SCHED_SYNC_ORDERED\n"
846 " -w, --warm-up <number> Number of warm-up rounds, default=100, min=1\n"
847 " -W, --wait-ns <number> Number of nsec to wait per schedule round to simulate work (default=0)\n"
848 " -h, --help Display help and exit.\n\n");
849}
850
858static void parse_args(int argc, char *argv[], test_args_t *args)
859{
860 int opt;
861 int i;
862
863 static const struct option longopts[] = {
864 {"count", required_argument, NULL, 'c'},
865 {"burst", required_argument, NULL, 'b'},
866 {"duration", required_argument, NULL, 'd'},
867 {"forward-mode", required_argument, NULL, 'f'},
868 {"num_group", required_argument, NULL, 'g'},
869 {"isolate", required_argument, NULL, 'i'},
870 {"lo-prio-queues", required_argument, NULL, 'l'},
871 {"hi-prio-queues", required_argument, NULL, 't'},
872 {"lo-prio-events-per-queue", required_argument, NULL, 'm'},
873 {"hi-prio-events-per-queue", required_argument, NULL, 'n'},
874 {"lo-prio-events", required_argument, NULL, 'o'},
875 {"hi-prio-events", required_argument, NULL, 'p'},
876 {"sync", required_argument, NULL, 's'},
877 {"warm-up", required_argument, NULL, 'w'},
878 {"wait-ns", required_argument, NULL, 'W'},
879 {"sample-per-prio", no_argument, NULL, 'r'},
880 {"help", no_argument, NULL, 'h'},
881 {NULL, 0, NULL, 0}
882 };
883
884 static const char *shortopts = "+c:b:d:f:g:i:l:t:m:n:o:p:s:w:W:rh";
885
886 args->cpu_count = 1;
887 args->forward_mode = EVENT_FORWARD_RAND;
888 args->num_group = 0;
889 args->isolate = 0;
890 args->test_rounds = 10;
891 args->warm_up_rounds = 100;
892 args->sync_type = ODP_SCHED_SYNC_PARALLEL;
893 args->sample_per_prio = 0;
894 args->prio[LO_PRIO].queues = 64;
895 args->prio[HI_PRIO].queues = 16;
896 args->prio[LO_PRIO].events = 32;
897 args->prio[HI_PRIO].events = 0;
898 args->prio[LO_PRIO].events_per_queue = 1;
899 args->prio[HI_PRIO].events_per_queue = 0;
900 args->prio[LO_PRIO].samples = 0;
901 args->prio[HI_PRIO].samples = 1;
902
903 while (1) {
904 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
905
906 if (opt == -1)
907 break; /* No more options */
908
909 switch (opt) {
910 case 'c':
911 args->cpu_count = atoi(optarg);
912 break;
913 case 'd':
914 args->test_rounds = atoi(optarg);
915 break;
916 case 'f':
917 args->forward_mode = atoi(optarg);
918 break;
919 case 'g':
920 args->num_group = atoi(optarg);
921 break;
922 case 'i':
923 args->isolate = atoi(optarg);
924 break;
925 case 'l':
926 args->prio[LO_PRIO].queues = atoi(optarg);
927 break;
928 case 't':
929 args->prio[HI_PRIO].queues = atoi(optarg);
930 break;
931 case 'm':
932 args->prio[LO_PRIO].events = atoi(optarg);
933 args->prio[LO_PRIO].events_per_queue = 1;
934 break;
935 case 'n':
936 args->prio[HI_PRIO].events = atoi(optarg);
937 args->prio[HI_PRIO].events_per_queue = 1;
938 break;
939 case 'o':
940 args->prio[LO_PRIO].events = atoi(optarg);
941 args->prio[LO_PRIO].events_per_queue = 0;
942 break;
943 case 'p':
944 args->prio[HI_PRIO].events = atoi(optarg);
945 args->prio[HI_PRIO].events_per_queue = 0;
946 break;
947 case 's':
948 i = atoi(optarg);
949 if (i == 1)
950 args->sync_type = ODP_SCHED_SYNC_ATOMIC;
951 else if (i == 2)
952 args->sync_type = ODP_SCHED_SYNC_ORDERED;
953 else
954 args->sync_type = ODP_SCHED_SYNC_PARALLEL;
955 break;
956 case 'r':
957 args->sample_per_prio = 1;
958 break;
959 case 'w':
960 args->warm_up_rounds = atoi(optarg);
961 break;
962 case 'W':
963 args->wait_ns = atoll(optarg);
964 break;
965 case 'b':
966 args->burst_size = atoi(optarg);
967 break;
968 case 'h':
969 usage();
970 exit(EXIT_SUCCESS);
971 break;
972
973 default:
974 break;
975 }
976 }
977
978 /* Make sure arguments are valid */
979 /* -1 for main thread */
980 if (args->cpu_count > ODP_THREAD_COUNT_MAX - 1)
981 args->cpu_count = ODP_THREAD_COUNT_MAX - 1;
982 if (args->prio[LO_PRIO].queues > MAX_QUEUES)
983 args->prio[LO_PRIO].queues = MAX_QUEUES;
984 if (args->prio[HI_PRIO].queues > MAX_QUEUES)
985 args->prio[HI_PRIO].queues = MAX_QUEUES;
986 if (args->test_rounds < 1)
987 args->test_rounds = 1;
988 if (!args->prio[HI_PRIO].queues && !args->prio[LO_PRIO].queues) {
989 printf("No queues configured\n");
990 usage();
991 exit(EXIT_FAILURE);
992 }
993 if (args->forward_mode > EVENT_FORWARD_NONE ||
994 args->forward_mode < EVENT_FORWARD_RAND) {
995 printf("Invalid forwarding mode\n");
996 usage();
997 exit(EXIT_FAILURE);
998 }
999
1000 if (args->num_group > MAX_GROUPS) {
1001 ODPH_ERR("Too many groups. Max supported %i.\n", MAX_GROUPS);
1002 exit(EXIT_FAILURE);
1003 }
1004
1005 if (args->prio[HI_PRIO].queues == 0 || args->sample_per_prio)
1006 args->prio[LO_PRIO].samples = 1;
1007
1008 if (args->burst_size < 0 || args->burst_size > MAX_BURST_SIZE) {
1009 ODPH_ERR("Invalid burst size %i, min 0, max %i\n",
1010 args->burst_size, MAX_BURST_SIZE);
1011 exit(EXIT_FAILURE);
1012 }
1013
1014 if (args->burst_size) {
1015 args->prio[HI_PRIO].samples = args->burst_size;
1016 if (args->sample_per_prio)
1017 args->prio[LO_PRIO].samples = args->burst_size;
1018 }
1019}
1020
1021static void randomize_queues(odp_queue_t queues[], uint32_t num, uint64_t *seed)
1022{
1023 uint32_t i;
1024
1025 for (i = 0; i < num; i++) {
1026 uint32_t new_index;
1027 odp_queue_t swap_queue;
1028 odp_queue_t cur_queue = queues[i];
1029
1030 odp_random_test_data((uint8_t *)&new_index, sizeof(new_index),
1031 seed);
1032 new_index = new_index % num;
1033 swap_queue = queues[new_index];
1034
1035 queues[new_index] = cur_queue;
1036 queues[i] = swap_queue;
1037 }
1038}
1039
1040static int create_groups(test_globals_t *globals, odp_schedule_group_t group[], int num)
1041{
1042 odp_schedule_capability_t sched_capa;
1043 odp_thrmask_t zeromask;
1044 int i, j, max;
1045
1046 if (num <= 0)
1047 return 0;
1048
1049 if (odp_schedule_capability(&sched_capa)) {
1050 ODPH_ERR("Schedule capability failed\n");
1051 return 0;
1052 }
1053
1054 max = sched_capa.max_groups - 3;
1055 if (num > max) {
1056 printf("Too many schedule groups %i (max %u)\n", num, max);
1057 return 0;
1058 }
1059
1060 for (i = 0; i < NUM_PRIOS; i++)
1061 for (j = 0; j < MAX_GROUPS; j++)
1062 globals->group[i][j] = ODP_SCHED_GROUP_INVALID;
1063
1064 odp_thrmask_zero(&zeromask);
1065
1066 for (i = 0; i < num; i++) {
1067 group[i] = odp_schedule_group_create("test_group", &zeromask);
1068
1069 if (group[i] == ODP_SCHED_GROUP_INVALID) {
1070 ODPH_ERR("Group create failed %i\n", i);
1071 break;
1072 }
1073
1074 if (globals->args.isolate) {
1075 globals->group[i % 2][i / 2] = group[i];
1076 } else {
1077 globals->group[0][i] = group[i];
1078 globals->group[1][i] = group[i];
1079 }
1080 }
1081
1082 return i;
1083}
1084
1085static int destroy_groups(odp_schedule_group_t group[], int num)
1086{
1087 int i;
1088
1089 if (num <= 0)
1090 return 0;
1091
1092 for (i = 0; i < num; i++) {
1093 if (odp_schedule_group_destroy(group[i])) {
1094 ODPH_ERR("Group destroy failed %i\n", i);
1095 return -1;
1096 }
1097 }
1098
1099 return 0;
1100}
1101
1102static int calc_queue_sizes(test_globals_t *globals, uint32_t queue_size[])
1103{
1105 test_args_t *args = &globals->args;
1106 const uint32_t min_queue_size = 256;
1107 uint32_t tot_queues = 0;
1108
1109 if (odp_schedule_capability(&capa)) {
1110 ODPH_ERR("Schedule capability failed\n");
1111 return -1;
1112 }
1113
1114 for (int i = 0; i < NUM_PRIOS; i++) {
1115 uint32_t events = args->prio[i].events;
1116 int queues = args->prio[i].queues;
1117
1118 if (!args->prio[i].events_per_queue && queues)
1119 events = (events + queues - 1) / queues;
1120
1121 /* Events may stack up if forwarding is enabled */
1122 if (args->forward_mode != EVENT_FORWARD_NONE)
1123 events *= queues;
1124
1125 /* Reserve room for sample event */
1126 events++;
1127
1128 queue_size[i] = ODPH_MAX(events, min_queue_size);
1129
1130 if (capa.max_queue_size && queue_size[i] > capa.max_queue_size) {
1131 ODPH_ERR("Warn: queues may not be able to store all events (required size "
1132 "%" PRIu32 ", max supported %" PRIu32 ")\n", queue_size[i],
1133 capa.max_queue_size);
1134 queue_size[i] = capa.max_queue_size;
1135 }
1136 tot_queues += queues;
1137 }
1138
1139 if (tot_queues > capa.max_queues) {
1140 ODPH_ERR("Requested %" PRIu32 " queues, max %" PRIu32 " supported\n",
1141 tot_queues, capa.max_queues);
1142 return -1;
1143 }
1144
1145 return 0;
1146}
1147
1148static int create_queues(test_globals_t *globals)
1149{
1150 odp_queue_param_t param;
1151 test_args_t *args = &globals->args;
1152 int num_group = args->num_group;
1153 uint32_t queue_size[NUM_PRIOS];
1154
1155 if (calc_queue_sizes(globals, queue_size))
1156 return -1;
1157
1158 odp_queue_param_init(&param);
1159 param.type = ODP_QUEUE_TYPE_SCHED;
1160 param.sched.sync = args->sync_type;
1161
1162 for (int i = 0; i < NUM_PRIOS; i++) {
1163 char name[] = "sched_XX_YY";
1164 odp_queue_t queue;
1165 odp_schedule_group_t grp = num_group < 0 ? ODP_SCHED_GROUP_WORKER :
1167 const int prio = i == HI_PRIO ? odp_schedule_max_prio() :
1169
1170 param.sched.prio = prio;
1171 param.size = queue_size[i];
1172
1173 /* Replace XX and YY in name to differentiate queues */
1174 name[6] = '0' + (prio / 10);
1175 name[7] = '0' + prio - (10 * (prio / 10));
1176
1177 for (int j = 0; j < args->prio[i].queues; j++) {
1178 name[9] = '0' + j / 10;
1179 name[10] = '0' + j - 10 * (j / 10);
1180
1181 /* Round robin queues into groups */
1182 if (num_group > 0)
1183 grp = globals->group[i][j % num_group];
1184
1185 param.sched.group = grp;
1186
1187 queue = odp_queue_create(name, &param);
1188
1189 if (queue == ODP_QUEUE_INVALID) {
1190 ODPH_ERR("Scheduled queue create failed\n");
1191 return -1;
1192 }
1193
1194 globals->queue[i][j] = queue;
1195 }
1196 if (args->forward_mode == EVENT_FORWARD_RAND) {
1197 uint64_t seed = i;
1198
1199 randomize_queues(globals->queue[i], args->prio[i].queues, &seed);
1200 }
1201 }
1202 return 0;
1203}
1204
1205static void init_global_data(test_globals_t *globals, test_args_t *args,
1206 test_common_options_t common_options)
1207{
1208 memset(globals, 0, sizeof(test_globals_t));
1209 memcpy(&globals->args, args, sizeof(test_args_t));
1210
1211 globals->common_options = common_options;
1212
1213 odp_barrier_init(&globals->barrier, globals->args.cpu_count);
1214
1215 globals->pool = ODP_POOL_INVALID;
1216 for (int i = 0; i < NUM_PRIOS; i++) {
1217 for (int j = 0; j < MAX_GROUPS; j++) {
1218 globals->group[i][j] = ODP_SCHED_GROUP_INVALID;
1219 globals->queue[i][j] = ODP_QUEUE_INVALID;
1220 }
1221
1222 globals->track[i].enq_time = ODP_TIME_NULL;
1223 odp_atomic_init_u32(&globals->track[i].ready, 0);
1224 odp_atomic_init_u32(&globals->track[i].collector_idx, 0);
1225 }
1226}
1227
1231int main(int argc, char *argv[])
1232{
1233 odp_instance_t instance;
1234 odp_init_t init_param;
1235 odph_helper_options_t helper_options;
1236 odph_thread_common_param_t thr_common;
1237 odph_thread_param_t thr_param;
1238 odp_cpumask_t cpumask;
1239 odp_pool_capability_t pool_capa;
1240 odp_pool_param_t params;
1241 test_globals_t *globals;
1242 test_args_t args;
1243 char cpumaskstr[ODP_CPUMASK_STR_SIZE];
1244 uint32_t pool_size;
1245 int i, j, ret;
1246 int num_group, tot_group;
1247 odp_schedule_group_t group[2 * MAX_GROUPS];
1248 odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
1249 int err = 0;
1250 int num_workers = 0;
1253 test_common_options_t common_options;
1254
1255 printf("\nODP scheduling latency benchmark starts\n\n");
1256
1257 /* Let helper collect its own arguments (e.g. --odph_proc) */
1258 argc = odph_parse_options(argc, argv);
1259 if (odph_options(&helper_options)) {
1260 ODPH_ERR("Error: reading ODP helper options failed.\n");
1261 exit(EXIT_FAILURE);
1262 }
1263
1264 argc = test_common_parse_options(argc, argv);
1265 if (test_common_options(&common_options)) {
1266 ODPH_ERR("Error: reading test options failed\n");
1267 exit(EXIT_FAILURE);
1268 }
1269
1270 odp_init_param_init(&init_param);
1271 init_param.mem_model = helper_options.mem_model;
1272
1273 memset(&args, 0, sizeof(args));
1274 parse_args(argc, argv, &args);
1275
1276 /* ODP global init */
1277 if (odp_init_global(&instance, &init_param, NULL)) {
1278 ODPH_ERR("ODP global init failed.\n");
1279 exit(EXIT_FAILURE);
1280 }
1281
1282 /*
1283 * Init this thread. It makes also ODP calls when
1284 * setting up resources for worker threads.
1285 */
1286 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
1287 ODPH_ERR("ODP global init failed.\n");
1288 exit(EXIT_FAILURE);
1289 }
1290
1292
1293 num_group = args.num_group;
1294
1295 tot_group = 0;
1296 if (num_group > 0)
1297 tot_group = args.isolate ? 2 * num_group : num_group;
1298
1299 /* Get default worker cpumask */
1300 if (args.cpu_count)
1301 num_workers = args.cpu_count;
1302
1303 num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
1304 args.cpu_count = num_workers;
1305
1306 (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
1307
1308 printf("Test options:\n");
1309 printf(" Worker threads: %i\n", num_workers);
1310 printf(" First CPU: %i\n", odp_cpumask_first(&cpumask));
1311 printf(" CPU mask: %s\n", cpumaskstr);
1312 printf(" Test rounds: %iM\n", args.test_rounds);
1313 printf(" Warm-up rounds: %i\n", args.warm_up_rounds);
1314 printf(" Wait nsec: %" PRIu64 "\n", args.wait_ns);
1315 printf(" Isolated groups: %i\n", args.isolate);
1316 printf(" Number of groups: %i\n", num_group);
1317 printf(" Created groups: %i\n", tot_group);
1318 printf("\n");
1319
1320 shm = odp_shm_reserve("test_globals", sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
1321 if (shm == ODP_SHM_INVALID) {
1322 ODPH_ERR("Shared memory reserve failed.\n");
1323 err = -1;
1324 goto error;
1325 }
1326
1327 globals = odp_shm_addr(shm);
1328 init_global_data(globals, &args, common_options);
1329 odp_schedule_config(NULL);
1330
1331 /*
1332 * Create event pool
1333 */
1334 if (odp_pool_capability(&pool_capa)) {
1335 ODPH_ERR("pool capa failed\n");
1336 err = -1;
1337 goto error;
1338 }
1339
1340 pool_size = EVENT_POOL_SIZE;
1341 if (pool_capa.buf.max_num && pool_capa.buf.max_num < EVENT_POOL_SIZE)
1342 pool_size = pool_capa.buf.max_num;
1343
1344 odp_pool_param_init(&params);
1345 params.buf.size = sizeof(test_event_t);
1346 params.buf.align = 0;
1347 params.buf.num = pool_size;
1348 params.type = ODP_POOL_BUFFER;
1349
1350 pool = odp_pool_create("event_pool", &params);
1351
1352 if (pool == ODP_POOL_INVALID) {
1353 ODPH_ERR("Pool create failed.\n");
1354 err = -1;
1355 goto error;
1356 }
1357 globals->pool = pool;
1358
1359 /* Create groups */
1360 ret = create_groups(globals, group, tot_group);
1361 if (ret != tot_group) {
1362 ODPH_ERR("Group create failed.\n");
1363 tot_group = ret;
1364 err = -1;
1365 goto error;
1366 }
1367
1368 if (create_queues(globals)) {
1369 ODPH_ERR("Creating test queues failed.\n");
1370 err = -1;
1371 goto error;
1372 }
1373
1374 /* Create and launch worker threads */
1375 memset(thread_tbl, 0, sizeof(thread_tbl));
1376
1377 odph_thread_common_param_init(&thr_common);
1378 thr_common.instance = instance;
1379 thr_common.cpumask = &cpumask;
1380 thr_common.share_param = 1;
1381
1382 odph_thread_param_init(&thr_param);
1383 thr_param.start = run_thread;
1384 thr_param.arg = NULL;
1385 thr_param.thr_type = ODP_THREAD_WORKER;
1386
1387 odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
1388
1389 /* Wait for worker threads to terminate */
1390 if (odph_thread_join(thread_tbl, num_workers) != num_workers)
1391 err = -1;
1392
1393 printf("ODP scheduling latency test complete\n\n");
1394
1395 for (i = 0; i < NUM_PRIOS; i++) {
1396 odp_queue_t queue;
1397 int num_queues;
1398
1399 num_queues = args.prio[i].queues;
1400
1401 for (j = 0; j < num_queues; j++) {
1402 queue = globals->queue[i][j];
1403 if (odp_queue_destroy(queue)) {
1404 ODPH_ERR("Queue destroy failed [%i][%i]\n", i, j);
1405 err = -1;
1406 break;
1407 }
1408 }
1409 }
1410
1411error:
1412 if (destroy_groups(group, tot_group)) {
1413 ODPH_ERR("Group destroy failed\n");
1414 err = -1;
1415 }
1416
1417 if (pool != ODP_POOL_INVALID) {
1418 if (odp_pool_destroy(pool)) {
1419 ODPH_ERR("Pool destroy failed\n");
1420 err = -1;
1421 }
1422 }
1423
1424 if (shm != ODP_SHM_INVALID) {
1425 if (odp_shm_free(shm)) {
1426 ODPH_ERR("SHM destroy failed\n");
1427 err = -1;
1428 }
1429 }
1430
1431 err += odp_term_local();
1432 err += odp_term_global(instance);
1433
1434 return err;
1435}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
uint32_t odp_atomic_fetch_inc_u32(odp_atomic_u32_t *atom)
Fetch and increment atomic uint32 variable.
uint32_t odp_atomic_load_acq_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable using ACQUIRE memory ordering.
void odp_atomic_add_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
Add to atomic uint32 variable using RELEASE memory ordering.
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.
odp_event_t odp_buffer_to_event(odp_buffer_t buf)
Convert buffer handle to event.
odp_buffer_t odp_buffer_alloc(odp_pool_t pool)
Buffer alloc.
int odp_buffer_is_valid(odp_buffer_t buf)
Check that buffer is valid.
odp_buffer_t odp_buffer_from_event(odp_event_t ev)
Get buffer handle from event.
void * odp_buffer_addr(odp_buffer_t buf)
Buffer start address.
int odp_buffer_alloc_multi(odp_pool_t pool, odp_buffer_t buf[], int num)
Allocate multiple buffers.
#define ODP_BUFFER_INVALID
Invalid buffer.
void odp_buffer_free_multi(const odp_buffer_t buf[], int num)
Free multiple buffers.
#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
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition spec/hints.h:54
void odp_cpu_pause(void)
Pause CPU execution for a short while.
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.
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.
#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.
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_BUFFER
Buffer pool.
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.
uint64_t odp_queue_to_u64(odp_queue_t hdl)
Get printable value for an odp_queue_t.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
int32_t odp_random_test_data(uint8_t *buf, uint32_t len, uint64_t *seed)
Generate repeatable random data for testing purposes.
int odp_schedule_sync_t
Scheduler synchronization method.
#define ODP_SCHED_WAIT
Wait infinitely.
#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.
int odp_schedule_group_t
Scheduler thread group.
int odp_schedule_group_join(odp_schedule_group_t group, const odp_thrmask_t *mask)
Join a schedule group.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
int odp_schedule_min_prio(void)
Minimum scheduling priority level.
#define ODP_SCHED_GROUP_WORKER
Group of all worker threads.
int odp_schedule_group_destroy(odp_schedule_group_t group)
Schedule group destroy.
#define ODP_SCHED_GROUP_INVALID
Invalid scheduler group.
#define ODP_SCHED_NO_WAIT
Do not wait.
void odp_schedule_pause(void)
Pause scheduling.
int odp_schedule_max_prio(void)
Maximum scheduling priority level.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
int odp_schedule_capability(odp_schedule_capability_t *capa)
Query scheduler capabilities.
odp_schedule_group_t odp_schedule_group_create(const char *name, const odp_thrmask_t *mask)
Schedule group create.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
void odp_schedule_resume(void)
Resume scheduling.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
odp_shm_t odp_shm_lookup(const char *name)
Lookup for a block of shared memory.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
void odp_sys_info_print(void)
Print system info.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
void odp_thrmask_set(odp_thrmask_t *mask, int thr)
Add thread to mask.
int odp_thread_id(void)
Get thread identifier.
void odp_thrmask_zero(odp_thrmask_t *mask)
Clear entire thread mask.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
uint64_t odp_time_to_ns(odp_time_t time)
Convert time to nanoseconds.
odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
Time difference.
odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2)
Time sum.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
#define ODP_TIME_NULL
Zero time stamp.
odp_time_t odp_time_global_strict(void)
Current global time (strict)
int odp_time_cmp(odp_time_t t2, odp_time_t t1)
Compare two times.
odp_time_t odp_time_global_from_ns(uint64_t ns)
Convert nanoseconds to global time.
#define ODP_TIME_HOUR_IN_NS
An hour in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@133 buf
Buffer pool capabilities
uint32_t num
Number of buffers in the pool.
uint32_t align
Minimum buffer alignment in bytes.
uint32_t size
Minimum buffer size in bytes.
odp_pool_type_t type
Pool type.
struct odp_pool_param_t::@138 buf
Parameters for buffer pools.
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
uint32_t size
Queue size.
odp_queue_type_t type
Queue type.
uint32_t max_groups
Maximum number of scheduling groups.
uint32_t max_queues
Maximum number of scheduled (ODP_BLOCKING) queues of the default size.
uint32_t max_queue_size
Maximum number of events a scheduled (ODP_BLOCKING) queue can store simultaneously.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.