API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_sched_perf.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018 Linaro Limited
3 * Copyright (c) 2020-2026 Nokia
4 */
5
14#ifndef _GNU_SOURCE
15#define _GNU_SOURCE /* Needed for sigaction */
16#endif
17
18#include <signal.h>
19#include <stdio.h>
20#include <string.h>
21#include <stdint.h>
22#include <inttypes.h>
23#include <stdlib.h>
24#include <getopt.h>
25
26#include <odp_api.h>
27#include <odp/helper/odph_api.h>
28
29#include <export_results.h>
30
31#define MAX_QUEUES (256 * 1024)
32#define MAX_GROUPS 256
33#define MAX_VECTOR_SIZE 16U
34
35/* Limit data values to 16 bits. Large data values are costly on square root calculation. */
36#define DATA_MASK 0xffff
37
38/* Max time to wait for new events in nanoseconds */
39#define MAX_SCHED_WAIT_NS (10 * ODP_TIME_SEC_IN_NS)
40
41/* Scheduling round interval to check for MAX_SCHED_WAIT_NS */
42#define TIME_CHECK_INTERVAL (1024 * 1024)
43
44typedef struct test_options_t {
45 uint32_t num_cpu;
46 uint32_t num_queue; /* Active queues (excludes dummy queues) */
47 uint32_t num_def;
48 uint32_t num_low;
49 uint32_t num_high;
50 uint32_t num_dummy;
51 uint32_t num_event;
52 uint32_t num_sched;
53 int num_group;
54 uint32_t num_join;
55 uint32_t num_prefetch;
56 uint32_t max_burst;
57 odp_cache_stash_config_t cache_stash_config;
58 odp_pool_type_t pool_type;
59 int queue_type;
60 int thr_type;
61 int forward;
62 int fairness;
63 uint32_t event_size;
64 uint32_t queue_size;
65 uint32_t forward_group_size;
66 uint32_t tot_queue; /* All queues (includes dummy queues) */
67 uint32_t tot_event;
68 int touch_data;
69 uint32_t stress;
70 uint32_t rd_words;
71 uint32_t rw_words;
72 uint32_t ctx_size;
73 uint32_t ctx_rd_words;
74 uint32_t ctx_rw_words;
75 uint32_t tot_rd_size;
76 uint32_t tot_rw_size;
77 uint32_t uarea_rd;
78 uint32_t uarea_rw;
79 uint32_t uarea_size;
80 uint64_t wait_ns;
81 int verbose;
82
83} test_options_t;
84
85typedef struct test_stat_t {
86 uint64_t rounds;
87 uint64_t enqueues;
88 uint64_t events;
89 uint64_t nsec;
90 uint64_t cycles;
91 uint64_t waits;
92 uint64_t dummy_sum;
93 uint8_t failed;
94
95} test_stat_t;
96
97typedef struct thread_arg_t {
98 void *global;
99 int first_group;
100
101} thread_arg_t;
102
103typedef struct test_global_t {
104 test_options_t test_options;
105 odp_schedule_config_t schedule_config;
106 odp_barrier_t barrier;
107 odp_pool_t pool;
108 odp_pool_t evv_pool;
109 odp_cpumask_t cpumask;
110 odp_shm_t ctx_shm;
111 struct {
112 odp_queue_t dummy[MAX_QUEUES];
113 odp_queue_t def_prio[MAX_QUEUES];
114 odp_queue_t low_prio[MAX_QUEUES];
115 odp_queue_t high_prio[MAX_QUEUES];
116 odp_queue_t all[MAX_QUEUES];
117 } queue;
118 odp_schedule_group_t group[MAX_GROUPS];
119 odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
120 test_stat_t stat[ODP_THREAD_COUNT_MAX];
121 thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
122 odp_atomic_u32_t num_worker;
123 odp_atomic_u32_t exit_threads;
124 test_common_options_t common_options;
125
126} test_global_t;
127
128typedef struct {
129 odp_queue_t next;
130 odp_atomic_u64_t count;
131} queue_context_t;
132
133static test_global_t *test_globals;
134
135static void sig_handler(int signum ODP_UNUSED)
136{
137 odp_atomic_store_u32(&test_globals->exit_threads, 1);
138}
139
140static int setup_sig_handler(void)
141{
142 struct sigaction action = { .sa_handler = sig_handler };
143
144 if (sigemptyset(&action.sa_mask) || sigaction(SIGINT, &action, NULL))
145 return -1;
146
147 return 0;
148}
149
150static void set_cache_stash(odp_cache_stash_region_t *region, uint32_t level, uint32_t len,
151 uint32_t offset)
152{
153 if (level == 0) {
154 region->l2.len = len;
155 region->l2.offset = offset;
156 } else {
157 region->l3.len = len;
158 region->l3.offset = offset;
159 }
160}
161
162static int parse_cache_stash_config(char *optarg, test_options_t *test_options)
163{
164 uint32_t region, level, offset, len;
165 odp_cache_stash_config_t *stash_config = &test_options->cache_stash_config;
166
167 if (sscanf(optarg, "%u,%u,%u,%u", &region, &level, &offset, &len) != 4) {
168 ODPH_ERR("Invalid number of arguments for cache stashing\n");
169 return -1;
170 }
171
172 if (region > 3) {
173 ODPH_ERR("Invalid region for cache stashing: %u\n", region);
174 return -1;
175 }
176
177 if (level > 1) {
178 ODPH_ERR("Invalid cache level for cache stashing: %u\n", level);
179 return -1;
180 }
181
182 if (len == 0) {
183 ODPH_ERR("Invalid len for cache stashing: %u\n", len);
184 return -1;
185 }
186
187 stash_config->regions.all |= (1U << (region * 2 + level));
188
189 switch (region) {
190 case 0:
191 set_cache_stash(&stash_config->event_metadata, level, len, offset);
192 break;
193 case 1:
194 set_cache_stash(&stash_config->event_data, level, len, offset);
195 break;
196 case 2:
197 set_cache_stash(&stash_config->event_user_area, level, len, offset);
198 break;
199 case 3:
200 set_cache_stash(&stash_config->queue_context, level, len, offset);
201 break;
202 default:
203 break;
204 }
205
206 return 0;
207}
208
209static void print_usage(void)
210{
211 printf("\n"
212 "Scheduler performance test\n"
213 "\n"
214 "Usage: odp_sched_perf [options]\n"
215 "\n"
216 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1.\n"
217 " -q, --num_def Number of default priority queues. Default: 1.\n"
218 " -L, --num_low Number of lowest priority queues. Default: 0.\n"
219 " -H, --num_high Number of highest priority queues. Default: 0.\n"
220 " -d, --num_dummy Number of empty queues. Default: 0.\n"
221 " -e, --num_event Number of events per queue. Default: 100.\n"
222 " -s, --num_sched Number of events to schedule per thread. If zero, the application runs\n"
223 " until SIGINT is received. Default: 100 000.\n"
224 " -g, --num_group Number of schedule groups. Round robins threads and queues into groups.\n"
225 " -1: SCHED_GROUP_WORKER\n"
226 " 0: SCHED_GROUP_ALL (default)\n"
227 " -j, --num_join Number of groups a thread joins. Threads are divide evenly into groups,\n"
228 " if num_cpu is multiple of num_group and num_group is multiple of num_join.\n"
229 " 0: join all groups (default)\n"
230 " -b, --burst Maximum number of events per operation. Default: 100.\n"
231 " -C, --cache_stash Enable common group level cache stashing. Format: region,level,offset,len\n"
232 " region: 0: Event metadata, 1: Event data, 2: Event user area, 3: Queue context\n"
233 " level: 0: L2, 1: L3\n"
234 " offset/len: in bytes\n"
235 " E.g.: 1,0,16,32 enables 32-byte L2 stash on event data with a 16-byte offset\n"
236 " For stashing multiple regions, use -C multiple times\n"
237 " -t, --type Queue type. 0: parallel, 1: atomic, 2: ordered. Default: 0.\n"
238 " -T, --thr_type Thread type. 0: worker thread, 1: control thread. Default: 0\n"
239 " -f, --forward 0: Keep event in the original queue (default)\n"
240 " 1: Forward event between all queues\n"
241 " N: Forward events between queues of N identical queue sets. In this mode 'num_def',\n"
242 " 'num_low', and 'num_high' options are per set, so the total number of active\n"
243 " queues is N * (num_def + num_low + num_high).\n"
244 " -F, --fairness 0: Don't count events per queue, 1: Count and report events relative to average. Default: 0.\n"
245 " -w, --wait_ns Number of nsec to wait before enqueueing events. Default: 0.\n"
246 " -S, --stress CPU stress function(s) to be called for each event data word (requires -n or -m).\n"
247 " Data is processed as uint32_t words. Multiple flags may be selected.\n"
248 " 0: No extra data processing (default)\n"
249 " 0x1: Calculate square of each uint32_t\n"
250 " 0x2: Calculate log2 of each uint32_t\n"
251 " 0x4: Calculate square root of each uint32_t\n"
252 " 0x8: Calculate square root of each uint32_t in floating point\n"
253 " -k, --ctx_rd_words Number of queue context words (uint64_t) to read on every event. Default: 0.\n"
254 " -l, --ctx_rw_words Number of queue context words (uint64_t) to modify on every event. Default: 0.\n"
255 " -n, --rd_words Number of event data words (uint64_t) to read before enqueueing it. Default: 0.\n"
256 " -m, --rw_words Number of event data words (uint64_t) to modify before enqueueing it. Default: 0.\n"
257 " -u, --uarea_rd Number of user area words (uint64_t) to read on every event. Default: 0.\n"
258 " -U, --uarea_rw Number of user area words (uint64_t) to modify on every event. Default: 0.\n"
259 " -p, --pool_type Pool type. 0: buffer, 1: packet, 2: event vector. Default: 0.\n"
260 " -P, --prefetch Number of events to be prefetched. Default: 0.\n"
261 " -v, --verbose Verbose output.\n"
262 " -h, --help This help\n"
263 "\n");
264}
265
266static int parse_options(int argc, char *argv[], test_options_t *test_options)
267{
268 int opt, num_group, num_join;
269 int ret = 0;
270 uint32_t ctx_size = 0;
271 int pool_type = 0;
272
273 static const struct option longopts[] = {
274 {"num_cpu", required_argument, NULL, 'c'},
275 {"num_def", required_argument, NULL, 'q'},
276 {"num_low", required_argument, NULL, 'L'},
277 {"num_high", required_argument, NULL, 'H'},
278 {"num_dummy", required_argument, NULL, 'd'},
279 {"num_event", required_argument, NULL, 'e'},
280 {"num_sched", required_argument, NULL, 's'},
281 {"num_group", required_argument, NULL, 'g'},
282 {"num_join", required_argument, NULL, 'j'},
283 {"burst", required_argument, NULL, 'b'},
284 {"cache_stash", required_argument, NULL, 'C'},
285 {"type", required_argument, NULL, 't'},
286 {"thr_type", required_argument, NULL, 'T'},
287 {"forward", required_argument, NULL, 'f'},
288 {"fairness", required_argument, NULL, 'F'},
289 {"wait_ns", required_argument, NULL, 'w'},
290 {"stress", required_argument, NULL, 'S'},
291 {"ctx_rd_words", required_argument, NULL, 'k'},
292 {"ctx_rw_words", required_argument, NULL, 'l'},
293 {"rd_words", required_argument, NULL, 'n'},
294 {"rw_words", required_argument, NULL, 'm'},
295 {"uarea_rd", required_argument, NULL, 'u'},
296 {"uarea_rw", required_argument, NULL, 'U'},
297 {"pool_type", required_argument, NULL, 'p'},
298 {"prefetch", required_argument, NULL, 'P'},
299 {"verbose", no_argument, NULL, 'v'},
300 {"help", no_argument, NULL, 'h'},
301 {NULL, 0, NULL, 0}
302 };
303
304 static const char *shortopts = "+c:q:L:H:d:e:s:g:j:b:C:t:T:f:F:w:S:k:l:n:m:p:P:u:U:vh";
305
306 test_options->num_cpu = 1;
307 test_options->num_def = 1;
308 test_options->num_event = 100;
309 test_options->num_sched = 100000;
310 test_options->max_burst = 100;
311
312 while (1) {
313 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
314
315 if (opt == -1)
316 break;
317
318 switch (opt) {
319 case 'c':
320 test_options->num_cpu = atoi(optarg);
321 break;
322 case 'q':
323 test_options->num_def = atoi(optarg);
324 break;
325 case 'L':
326 test_options->num_low = atoi(optarg);
327 break;
328 case 'H':
329 test_options->num_high = atoi(optarg);
330 break;
331 case 'd':
332 test_options->num_dummy = atoi(optarg);
333 break;
334 case 'e':
335 test_options->num_event = atoi(optarg);
336 break;
337 case 's':
338 test_options->num_sched = atoi(optarg);
339 break;
340 case 'g':
341 test_options->num_group = atoi(optarg);
342 break;
343 case 'j':
344 test_options->num_join = atoi(optarg);
345 break;
346 case 'C':
347 if (parse_cache_stash_config(optarg, test_options))
348 ret = -1;
349 break;
350 case 'b':
351 test_options->max_burst = atoi(optarg);
352 break;
353 case 't':
354 test_options->queue_type = atoi(optarg);
355 break;
356 case 'T':
357 test_options->thr_type = atoi(optarg);
358 break;
359 case 'f':
360 test_options->forward = atoi(optarg);
361 break;
362 case 'F':
363 test_options->fairness = atoi(optarg);
364 break;
365 case 'S':
366 test_options->stress = strtoul(optarg, NULL, 0);
367 break;
368 case 'k':
369 test_options->ctx_rd_words = atoi(optarg);
370 break;
371 case 'l':
372 test_options->ctx_rw_words = atoi(optarg);
373 break;
374 case 'n':
375 test_options->rd_words = atoi(optarg);
376 break;
377 case 'm':
378 test_options->rw_words = atoi(optarg);
379 break;
380 case 'u':
381 test_options->uarea_rd = atoi(optarg);
382 break;
383 case 'U':
384 test_options->uarea_rw = atoi(optarg);
385 break;
386 case 'p':
387 pool_type = atoi(optarg);
388 break;
389 case 'P':
390 test_options->num_prefetch = atoi(optarg);
391 break;
392 case 'w':
393 test_options->wait_ns = atoll(optarg);
394 break;
395 case 'v':
396 test_options->verbose = 1;
397 break;
398 case 'h':
399 /* fall through */
400 default:
401 print_usage();
402 ret = -1;
403 break;
404 }
405 }
406 switch (pool_type) {
407 case 0:
408 test_options->pool_type = ODP_POOL_BUFFER;
409 break;
410 case 1:
411 test_options->pool_type = ODP_POOL_PACKET;
412 break;
413 case 2:
414 test_options->pool_type = ODP_POOL_EVENT_VECTOR;
415 break;
416 default:
417 ODPH_ERR("Invalid pool type: %d.\n", pool_type);
418 ret = -1;
419 break;
420 }
421
422 test_options->touch_data = test_options->rd_words ||
423 test_options->rw_words;
424
425 if (test_options->stress && test_options->touch_data == 0) {
426 ODPH_ERR("Use -n or/and -m to select event data size with a stress function\n");
427 ret = -1;
428 }
429
430 test_options->forward_group_size = test_options->num_def + test_options->num_low +
431 test_options->num_high;
432 /* In queue group forward mode the queue count options are per group */
433 if (test_options->forward > 1) {
434 test_options->num_def *= test_options->forward;
435 test_options->num_low *= test_options->forward;
436 test_options->num_high *= test_options->forward;
437 } else if (test_options->forward < 0) {
438 ODPH_ERR("Invalid forward mode %i.\n", test_options->forward);
439 ret = -1;
440 }
441
442 test_options->num_queue = test_options->num_def + test_options->num_low +
443 test_options->num_high;
444 if ((test_options->num_queue + test_options->num_dummy) > MAX_QUEUES) {
445 ODPH_ERR("Too many queues. Max supported %i.\n", MAX_QUEUES);
446 ret = -1;
447 }
448
449 num_group = test_options->num_group;
450 num_join = test_options->num_join;
451 if (num_group > MAX_GROUPS) {
452 ODPH_ERR("Too many groups. Max supported %i.\n", MAX_GROUPS);
453 ret = -1;
454 }
455
456 if (num_group > 0 && num_join > num_group) {
457 ODPH_ERR("num_join (%i) larger than num_group (%i).\n", num_join, num_group);
458 ret = -1;
459 }
460
461 if (num_join && num_group > (int)(test_options->num_cpu * num_join)) {
462 printf("WARNING: Too many groups (%i). Some groups (%i) are not served.\n\n",
463 num_group, num_group - (test_options->num_cpu * num_join));
464
465 if (test_options->forward) {
466 ODPH_ERR("Cannot forward when some queues are not served.\n");
467 ret = -1;
468 }
469 }
470
471 test_options->tot_queue = test_options->num_queue +
472 test_options->num_dummy;
473 test_options->tot_event = test_options->num_queue *
474 test_options->num_event;
475
476 test_options->queue_size = test_options->num_event;
477
478 if (test_options->forward) {
479 /* When forwarding, events may accumulate into a single queue. */
480 test_options->queue_size *= test_options->forward_group_size;
481 }
482
483 if (test_options->forward || test_options->fairness)
484 ctx_size = sizeof(queue_context_t);
485
486 if (test_options->ctx_rd_words || test_options->ctx_rw_words) {
487 /* Round up queue handle size to a multiple of 8 for correct
488 * context data alignment */
489 ctx_size = ODPH_ROUNDUP_MULTIPLE(ctx_size, 8);
490 ctx_size += 8 * test_options->ctx_rd_words;
491 ctx_size += 8 * test_options->ctx_rw_words;
492 }
493
494 /* When context data is modified, round up to cache line size to avoid
495 * false sharing */
496 if (test_options->fairness || test_options->ctx_rw_words)
497 ctx_size = ODP_CACHE_LINE_ROUNDUP(ctx_size);
498
499 test_options->ctx_size = ctx_size;
500 test_options->uarea_size = 8 * (test_options->uarea_rd + test_options->uarea_rw);
501 test_options->tot_rd_size = 8 * (test_options->ctx_rd_words + test_options->uarea_rd +
502 test_options->rd_words);
503 test_options->tot_rw_size = 8 * (test_options->ctx_rw_words + test_options->uarea_rw +
504 test_options->rw_words);
505
506 return ret;
507}
508
509static int set_num_cpu(test_global_t *global)
510{
511 int ret;
512 test_options_t *test_options = &global->test_options;
513 int num_cpu = test_options->num_cpu;
514
515 /* One thread used for the main thread */
516 if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
517 ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
518 return -1;
519 }
520
521 ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
522
523 if (num_cpu && ret != num_cpu) {
524 ODPH_ERR("Too many workers. Max supported %i\n.", ret);
525 return -1;
526 }
527
528 /* Zero: all available workers */
529 if (num_cpu == 0) {
530 num_cpu = ret;
531 test_options->num_cpu = num_cpu;
532 }
533
534 odp_barrier_init(&global->barrier, num_cpu);
535
536 return 0;
537}
538
539static uint64_t init_data(uint64_t init, uint64_t *data, uint32_t words)
540{
541 uint32_t i;
542 uint64_t val = init;
543
544 for (i = 0; i < words; i++) {
545 data[i] = val;
546 val = (val + 1) & DATA_MASK;
547 }
548
549 return val;
550}
551
552static void print_options(test_options_t *options)
553{
554 printf("\nScheduler performance test\n");
555 printf(" num sched %u\n", options->num_sched);
556 printf(" num cpu %u\n", options->num_cpu);
557 printf(" num default prio queues %u\n", options->num_def);
558 printf(" num lowest prio queues %u\n", options->num_low);
559 printf(" num highest prio queues %u\n", options->num_high);
560 printf(" num empty queues %u\n", options->num_dummy);
561 printf(" total queues %u\n", options->tot_queue);
562 printf(" num groups %i", options->num_group);
563
564 if (options->num_group == -1)
565 printf(" (ODP_SCHED_GROUP_WORKER)\n");
566 else if (options->num_group == 0)
567 printf(" (ODP_SCHED_GROUP_ALL)\n");
568 else
569 printf("\n");
570
571 printf(" num join %u\n", options->num_join);
572 printf(" forward events %i\n", options->forward);
573 printf(" wait %" PRIu64 " nsec\n", options->wait_ns);
574 printf(" events per queue %u\n", options->num_event);
575 printf(" queue size %u\n", options->queue_size);
576 printf(" max burst size %u\n", options->max_burst);
577 printf(" total events %u\n", options->tot_event);
578 printf(" stress 0x%x\n", options->stress);
579
580 printf(" event size %u bytes", options->event_size);
581 if (options->touch_data)
582 printf(" (rd: %u, rw: %u)", 8 * options->rd_words, 8 * options->rw_words);
583 printf("\n");
584
585 printf(" queue context size %u bytes", options->ctx_size);
586 if (options->ctx_rd_words || options->ctx_rw_words) {
587 printf(" (rd: %u, rw: %u)",
588 8 * options->ctx_rd_words,
589 8 * options->ctx_rw_words);
590 }
591 printf("\n");
592
593 printf(" user area size %u bytes", options->uarea_size);
594 if (options->uarea_size)
595 printf(" (rd: %u, rw: %u)", 8 * options->uarea_rd, 8 * options->uarea_rw);
596 printf("\n");
597
598 printf(" pool type %s\n", options->pool_type == ODP_POOL_BUFFER ? "buffer"
599 : options->pool_type == ODP_POOL_PACKET ? "packet"
600 : "event vector");
601
602 printf(" queue type %s\n", options->queue_type == 0 ? "parallel" :
603 options->queue_type == 1 ? "atomic" :
604 "ordered");
605 printf(" thread type %s\n\n", options->thr_type == 0 ? "worker" :
606 "control");
607
608 printf("Extra rd/rw ops per event (queue context + user area + event data)\n");
609 printf(" read %u bytes\n", options->tot_rd_size);
610 printf(" write %u bytes\n\n", options->tot_rw_size);
611}
612
613static int create_pool_helper(const char *name, odp_pool_t *pool_ptr, odp_pool_type_t pool_type,
614 uint32_t tot_event, uint32_t uarea_size,
615 uint32_t event_size)
616{
617 odp_pool_capability_t pool_capa;
618 odp_pool_param_t pool_param;
619 uint32_t max_num, max_uarea;
620 uint32_t max_vector_size = 0;
621 uint32_t max_size = 0;
622 uint32_t max_vector_pools = 0;
623
624 if (odp_pool_capability(&pool_capa)) {
625 ODPH_ERR("Pool capa failed\n");
626 return -1;
627 }
628
629 if (pool_type == ODP_POOL_BUFFER) {
630 max_num = pool_capa.buf.max_num;
631 max_size = pool_capa.buf.max_size;
632 max_uarea = pool_capa.buf.max_uarea_size;
633 } else if (pool_type == ODP_POOL_PACKET) {
634 max_num = pool_capa.pkt.max_num;
635 max_size = pool_capa.pkt.max_seg_len;
636 max_uarea = pool_capa.pkt.max_uarea_size;
637 } else {
638 max_num = pool_capa.event_vector.max_num;
639 max_vector_size = ODPH_MIN(MAX_VECTOR_SIZE, pool_capa.event_vector.max_size);
640 max_uarea = pool_capa.event_vector.max_uarea_size;
641 max_vector_pools = pool_capa.event_vector.max_pools;
642 }
643
644 if (max_num && tot_event > max_num) {
645 ODPH_ERR("Max events supported %u\n", max_num);
646 return -1;
647 }
648
649 if (max_size && event_size > max_size) {
650 ODPH_ERR("Max supported event size %u\n", max_size);
651 return -1;
652 }
653
654 if (uarea_size > max_uarea) {
655 ODPH_ERR("Max supported user area size %u\n", max_uarea);
656 return -1;
657 }
658
659 if (pool_type == ODP_POOL_EVENT_VECTOR && !max_vector_pools) {
660 ODPH_ERR("Event vector pools not supported\n");
661 return 77; /* Return 77 to indicate "test skipped" to Automake */
662 }
663
664 odp_pool_param_init(&pool_param);
665 pool_param.type = pool_type;
666
667 if (pool_param.type == ODP_POOL_BUFFER) {
668 pool_param.buf.num = tot_event;
669 pool_param.buf.size = event_size;
670 pool_param.buf.align = 8;
671 pool_param.buf.uarea_size = uarea_size;
672 } else if (pool_param.type == ODP_POOL_PACKET) {
673 pool_param.pkt.num = tot_event;
674 pool_param.pkt.len = event_size;
675 pool_param.pkt.seg_len = event_size;
676 pool_param.pkt.align = 8;
677 pool_param.pkt.uarea_size = uarea_size;
678 } else {
679 pool_param.event_vector.num = tot_event;
680 pool_param.event_vector.uarea_size = uarea_size;
681 pool_param.event_vector.max_size = max_vector_size;
682 }
683
684 *pool_ptr = odp_pool_create(name, &pool_param);
685 if (*pool_ptr == ODP_POOL_INVALID) {
686 ODPH_ERR("Pool creation failed for type %d (%s)\n", pool_param.type, name);
687 return -1;
688 }
689
690 return 0;
691}
692
693static int create_pool(test_global_t *global)
694{
695 test_options_t *test_options = &global->test_options;
696 uint32_t tot_event = test_options->tot_event;
697 uint32_t event_size = 16;
698 uint32_t uarea_size = test_options->uarea_size;
699 int ret;
700
701 if (test_options->touch_data) {
702 event_size = test_options->rd_words + test_options->rw_words;
703 event_size = 8 * event_size;
704 }
705 test_options->event_size = event_size;
706
707 ret = create_pool_helper("sched perf", &global->pool, test_options->pool_type, tot_event,
708 uarea_size, event_size);
709 if (ret)
710 return ret;
711
712 if (test_options->pool_type == ODP_POOL_EVENT_VECTOR) {
713 ret = create_pool_helper("sched perf event vector content", &global->evv_pool,
714 ODP_POOL_BUFFER, tot_event, 0, event_size);
715 if (ret)
716 return ret;
717 }
718
719 return 0;
720}
721
722static int create_groups(test_global_t *global)
723{
724 odp_schedule_capability_t sched_capa;
725 odp_thrmask_t thrmask;
726 uint32_t i;
727 test_options_t *test_options = &global->test_options;
728 uint32_t num_group = test_options->num_group;
729 odp_schedule_group_param_t group_param;
730
731 if (test_options->num_group <= 0)
732 return 0;
733
734 if (odp_schedule_capability(&sched_capa)) {
735 ODPH_ERR("Schedule capability failed\n");
736 return -1;
737 }
738
739 if (num_group > sched_capa.max_groups) {
740 ODPH_ERR("Too many sched groups (max_groups capa %u)\n", sched_capa.max_groups);
741 return -1;
742 }
743
744 odp_thrmask_zero(&thrmask);
745 odp_schedule_group_param_init(&group_param);
746 group_param.cache_stash_hints.common = test_options->cache_stash_config;
747
748 for (i = 0; i < num_group; i++) {
750
751 group = odp_schedule_group_create_2("test_group", &thrmask, &group_param);
752
753 if (group == ODP_SCHED_GROUP_INVALID) {
754 ODPH_ERR("Group create failed %u\n", i);
755 return -1;
756 }
757
758 global->group[i] = group;
759 }
760
761 return 0;
762}
763
764static void setup_forwarding(test_global_t *global)
765{
766 const test_options_t *test_options = &global->test_options;
767 const uint32_t forward_group_size = test_options->forward_group_size;
768 const uint32_t num_groups = test_options->forward;
769 uint32_t num_low = test_options->num_low;
770 uint32_t num_high = test_options->num_high;
771 uint32_t num_def = test_options->num_def;
772 const uint32_t num_high_per_group = num_high / num_groups;
773 const uint32_t num_low_per_group = num_low / num_groups;
774 const uint32_t num_def_per_group = num_def / num_groups;
775 odp_queue_t cur_queue, first_queue;
776
777 for (uint32_t i = 0; i < num_groups; i++) {
778 uint32_t num_group_low = num_low_per_group;
779 uint32_t num_group_high = num_high_per_group;
780 uint32_t num_group_def = num_def_per_group;
781
782 if (num_group_low) {
783 first_queue = global->queue.low_prio[--num_low];
784 num_group_low--;
785 } else if (num_group_high) {
786 first_queue = global->queue.high_prio[--num_high];
787 num_group_high--;
788 } else {
789 first_queue = global->queue.def_prio[--num_def];
790 num_group_def--;
791 }
792 cur_queue = first_queue;
793
794 for (uint32_t j = 0; j < forward_group_size; j++) {
795 queue_context_t *qc = (queue_context_t *)odp_queue_context(cur_queue);
796 odp_queue_t *next_queue = &qc->next;
797 const uint32_t next_id = j + 1;
798
799 if (next_id == forward_group_size) {
800 /* Last queue points to the first one */
801 *next_queue = first_queue;
802 break;
803 }
804
805 /* Mix low, high and default priority queues */
806 switch (next_id % 3) {
807 case 0:
808 if (num_group_low) {
809 *next_queue = global->queue.low_prio[--num_low];
810 num_group_low--;
811 } else if (num_group_high) {
812 *next_queue = global->queue.high_prio[--num_high];
813 num_group_high--;
814 } else {
815 *next_queue = global->queue.def_prio[--num_def];
816 num_group_def--;
817 }
818 break;
819 case 1:
820 if (num_group_high) {
821 *next_queue = global->queue.high_prio[--num_high];
822 num_group_high--;
823 } else if (num_group_low) {
824 *next_queue = global->queue.low_prio[--num_low];
825 num_group_low--;
826 } else {
827 *next_queue = global->queue.def_prio[--num_def];
828 num_group_def--;
829 }
830 break;
831 default:
832 if (num_group_def) {
833 *next_queue = global->queue.def_prio[--num_def];
834 num_group_def--;
835 } else if (num_group_high) {
836 *next_queue = global->queue.high_prio[--num_high];
837 num_group_high--;
838 } else {
839 *next_queue = global->queue.low_prio[--num_low];
840 num_group_low--;
841 }
842 break;
843 }
844 cur_queue = *next_queue;
845 }
846 }
847}
848
849static int set_queue_contexts(test_global_t *global, uint8_t *ctx)
850{
851 test_options_t *test_options = &global->test_options;
852 uint32_t tot_queue = test_options->tot_queue;
853 uint32_t ctx_size = test_options->ctx_size;
854 uint32_t first = test_options->num_dummy;
855
856 if (ctx_size == 0)
857 return 0;
858
859 for (uint32_t i = first; i < tot_queue; i++) {
860 if (test_options->fairness) {
861 /* Cast increases alignment, but it's ok, since ctx and ctx_size are both
862 * cache line aligned. */
863 queue_context_t *qc = (queue_context_t *)(uintptr_t)ctx;
864
865 odp_atomic_init_u64(&qc->count, 0);
866 }
867
868 if (odp_queue_context_set(global->queue.all[i], ctx, ctx_size)) {
869 ODPH_ERR("Context set failed %u\n", i);
870 return -1;
871 }
872
873 ctx += ctx_size;
874 }
875
876 if (test_options->forward)
877 setup_forwarding(global);
878
879 return 0;
880}
881
882static int create_queues(test_global_t *global, odp_queue_param_t *queue_param, int num_groups,
883 odp_queue_t queue[], uint32_t num)
884{
885 static uint32_t total_queues;
886
887 for (uint32_t i = 0; i < num; i++) {
888 if (num_groups > 0) /* Divide all queues evenly into groups */
889 queue_param->sched.group = global->group[(total_queues + i) % num_groups];
890
891 queue[i] = odp_queue_create(NULL, queue_param);
892
893 if (queue[i] == ODP_QUEUE_INVALID) {
894 ODPH_ERR("Queue create failed %u\n", i);
895 return -1;
896 }
897 }
898
899 /* Copy all queue handles to a single array for simpler initialization and clean-up */
900 for (uint32_t i = 0; i < num; i++)
901 global->queue.all[total_queues + i] = queue[i];
902 total_queues += num;
903
904 return 0;
905}
906
907static int create_all_queues(test_global_t *global)
908{
909 odp_queue_param_t queue_param;
910 odp_queue_t queue;
912 uint32_t i, j, first;
913 test_options_t *test_options = &global->test_options;
914 uint32_t event_size = test_options->event_size;
915 uint32_t num_event = test_options->num_event;
916 uint32_t queue_size = test_options->queue_size;
917 uint32_t tot_queue = test_options->tot_queue;
918 uint32_t num_low = test_options->num_low;
919 uint32_t num_high = test_options->num_high;
920 uint32_t num_default = test_options->num_def;
921 int num_group = test_options->num_group;
922 int type = test_options->queue_type;
923 odp_pool_t pool = global->pool;
924 uint8_t *ctx = NULL;
925 uint32_t ctx_size = test_options->ctx_size;
926 uint64_t init_val = 0;
927
928 if (type == 0)
930 else if (type == 1)
932 else
934
935 if (tot_queue > global->schedule_config.num_queues) {
936 ODPH_ERR("Max queues supported %u\n", global->schedule_config.num_queues);
937 return -1;
938 }
939
940 if (global->schedule_config.queue_size &&
941 queue_size > global->schedule_config.queue_size) {
942 ODPH_ERR("Max queue size %u\n", global->schedule_config.queue_size);
943 return -1;
944 }
945
946 if (ctx_size) {
947 ctx = odp_shm_addr(global->ctx_shm);
948 if (ctx == NULL) {
949 ODPH_ERR("Bad queue context\n");
950 return -1;
951 }
952 }
953
954 odp_queue_param_init(&queue_param);
955 queue_param.type = ODP_QUEUE_TYPE_SCHED;
956 queue_param.sched.sync = sync;
957 queue_param.sched.prio = odp_schedule_default_prio();
958 queue_param.size = queue_size;
959 if (num_group == -1)
960 queue_param.sched.group = ODP_SCHED_GROUP_WORKER;
961 else
962 queue_param.sched.group = ODP_SCHED_GROUP_ALL;
963
964 first = test_options->num_dummy;
965
966 /* Dummy queues */
967 if (create_queues(global, &queue_param, num_group, global->queue.dummy,
968 test_options->num_dummy)) {
969 ODPH_ERR("Dummy queue create failed\n");
970 return -1;
971 }
972
973 /* Lowest priority queues */
974 queue_param.sched.prio = odp_schedule_min_prio();
975 if (create_queues(global, &queue_param, num_group, global->queue.low_prio, num_low)) {
976 ODPH_ERR("Lowest priority queue create failed\n");
977 return -1;
978 }
979
980 /* Highest priority queues */
981 queue_param.sched.prio = odp_schedule_max_prio();
982 if (create_queues(global, &queue_param, num_group, global->queue.high_prio, num_high)) {
983 ODPH_ERR("Highest priority queue create failed\n");
984 return -1;
985 }
986
987 /* Default priority queues */
988 queue_param.sched.prio = odp_schedule_default_prio();
989 if (create_queues(global, &queue_param, num_group, global->queue.def_prio, num_default)) {
990 ODPH_ERR("Default priority queue create failed\n");
991 return -1;
992 }
993
994 if (set_queue_contexts(global, ctx)) {
995 ODPH_ERR("Set queue context failed\n");
996 return -1;
997 }
998
999 /* Store events into queues. Dummy queues are allocated from
1000 * the beginning of the array, so that usage of those affect allocation
1001 * of active queues. Dummy queues are left empty. */
1002 for (i = first; i < tot_queue; i++) {
1003 queue = global->queue.all[i];
1004
1005 for (j = 0; j < num_event; j++) {
1006 odp_event_t ev;
1007 uint64_t *data;
1008 uint32_t words;
1009
1010 if (test_options->pool_type == ODP_POOL_BUFFER) {
1011 odp_buffer_t buf = odp_buffer_alloc(pool);
1012
1013 if (buf == ODP_BUFFER_INVALID) {
1014 ODPH_ERR("Alloc failed %u/%u\n", i, j);
1015 return -1;
1016 }
1017 ev = odp_buffer_to_event(buf);
1018
1019 data = odp_buffer_addr(buf);
1020 words = odp_buffer_size(buf) / 8;
1021 } else if (test_options->pool_type == ODP_POOL_PACKET) {
1022 odp_packet_t pkt = odp_packet_alloc(pool, event_size);
1023
1024 if (pkt == ODP_PACKET_INVALID) {
1025 ODPH_ERR("Alloc failed %u/%u\n", i, j);
1026 return -1;
1027 }
1028 ev = odp_packet_to_event(pkt);
1029
1030 data = odp_packet_data(pkt);
1031 words = odp_packet_seg_len(pkt) / 8;
1032 } else {
1034 odp_event_t *tbl;
1035 odp_buffer_t buf;
1036
1037 if (evv == ODP_EVENT_VECTOR_INVALID) {
1038 ODPH_ERR("Alloc failed %u/%u\n", i, j);
1039 return -1;
1040 }
1041 odp_event_vector_tbl(evv, &tbl);
1042 buf = odp_buffer_alloc(global->evv_pool);
1043
1044 if (buf == ODP_BUFFER_INVALID) {
1045 ODPH_ERR("Alloc failed %u/%u\n", i, j);
1047 return -1;
1048 }
1049 tbl[0] = odp_buffer_to_event(buf);
1050 data = odp_buffer_addr(buf);
1051 words = odp_buffer_size(buf) / 8;
1052
1054 ev = odp_event_vector_to_event(evv);
1055 }
1056
1057 init_val = init_data(init_val, data, words);
1058
1059 if (odp_queue_enq(queue, ev)) {
1060 ODPH_ERR("Enqueue failed %u/%u\n", i, j);
1061 return -1;
1062 }
1063 }
1064 }
1065
1066 return 0;
1067}
1068
1069static int schedule_config(test_global_t *global)
1070{
1071 test_options_t *test_options = &global->test_options;
1072 odp_schedule_group_param_t group_param;
1073
1074 odp_schedule_config_init(&global->schedule_config);
1075 odp_schedule_group_param_init(&group_param);
1076 group_param.cache_stash_hints.common = test_options->cache_stash_config;
1077
1078 if (test_options->num_group == -1)
1079 global->schedule_config.sched_group.worker_param = group_param;
1080 else if (test_options->num_group == 0)
1081 global->schedule_config.sched_group.all_param = group_param;
1082
1083 return odp_schedule_config(&global->schedule_config);
1084}
1085
1086static int join_group(test_global_t *global, int grp_index, int thr)
1087{
1088 odp_thrmask_t thrmask;
1090
1091 odp_thrmask_zero(&thrmask);
1092 odp_thrmask_set(&thrmask, thr);
1093 group = global->group[grp_index];
1094
1095 if (odp_schedule_group_join(group, &thrmask)) {
1096 ODPH_ERR("Group %i join failed (thr %i)\n", grp_index, thr);
1097 return -1;
1098 }
1099
1100 return 0;
1101}
1102
1103static int join_all_groups(test_global_t *global, int thr)
1104{
1105 int i;
1106 test_options_t *test_options = &global->test_options;
1107 int num_group = test_options->num_group;
1108
1109 if (num_group <= 0)
1110 return 0;
1111
1112 for (i = 0; i < num_group; i++) {
1113 if (join_group(global, i, thr)) {
1114 ODPH_ERR("Group %u join failed (thr %i)\n", i, thr);
1115 return -1;
1116 }
1117 }
1118
1119 return 0;
1120}
1121
1122static void print_queue_fairness(test_global_t *global)
1123{
1124 uint32_t i;
1125 queue_context_t *ctx;
1126 test_options_t *test_options = &global->test_options;
1127 uint32_t first = test_options->num_dummy;
1128 uint32_t num_queue = test_options->num_queue;
1129 uint32_t tot_queue = test_options->tot_queue;
1130 uint64_t total = 0;
1131 double average;
1132
1133 if (!test_options->fairness)
1134 return;
1135
1136 for (i = first; i < tot_queue; i++) {
1137 ctx = odp_queue_context(global->queue.all[i]);
1138 total += odp_atomic_load_u64(&ctx->count);
1139 }
1140
1141 average = (double)total / (double)num_queue;
1142
1143 printf("\n");
1144 printf("RESULTS - events per queue (percent of average):\n");
1145 printf("------------------------------------------------\n");
1146 printf(" 1 2 3 4 5 6 7 8 9 10");
1147
1148 for (i = first; i < tot_queue; i++) {
1149 ctx = odp_queue_context(global->queue.all[i]);
1150
1151 if ((i % 10) == 0)
1152 printf("\n ");
1153
1154 printf("%6.1f ", (double)odp_atomic_load_u64(&ctx->count) /
1155 average * 100.0);
1156 }
1157
1158 printf("\n");
1159}
1160
1161static int destroy_queues(test_global_t *global)
1162{
1163 uint32_t i;
1164 odp_event_t ev;
1165 uint64_t wait;
1166 test_options_t *test_options = &global->test_options;
1167 uint32_t tot_queue = test_options->tot_queue;
1168 int thr = odp_thread_id();
1169
1170 if (join_all_groups(global, thr))
1171 return -1;
1172
1174
1175 while ((ev = odp_schedule(NULL, wait)) != ODP_EVENT_INVALID)
1176 odp_event_free(ev);
1177
1178 for (i = 0; i < tot_queue; i++) {
1179 if (global->queue.all[i] != ODP_QUEUE_INVALID) {
1180 if (odp_queue_destroy(global->queue.all[i])) {
1181 ODPH_ERR("Queue destroy failed %u\n", i);
1182 return -1;
1183 }
1184 }
1185 }
1186
1187 return 0;
1188}
1189
1190static int destroy_groups(test_global_t *global)
1191{
1192 int i;
1193 test_options_t *test_options = &global->test_options;
1194 int num_group = test_options->num_group;
1195
1196 if (num_group <= 0)
1197 return 0;
1198
1199 for (i = 0; i < num_group; i++) {
1200 odp_schedule_group_t group = global->group[i];
1201
1202 if (odp_schedule_group_destroy(group)) {
1203 ODPH_ERR("Group destroy failed %u\n", i);
1204 return -1;
1205 }
1206 }
1207
1208 return 0;
1209}
1210
1211static uint64_t rw_uarea(odp_event_t ev[], int num, uint32_t rd_words, uint32_t rw_words)
1212{
1213 uint64_t *data;
1214 int i;
1215 uint32_t j;
1216 uint64_t sum = 0;
1217
1218 for (i = 0; i < num; i++) {
1219 data = odp_event_user_area(ev[i]);
1220
1221 for (j = 0; j < rd_words; j++)
1222 sum += data[j];
1223
1224 for (; j < rd_words + rw_words; j++) {
1225 sum += data[j];
1226 data[j] += 1;
1227 }
1228 }
1229
1230 return sum;
1231}
1232
1233static inline uint64_t rw_ctx_data(void *ctx, uint32_t offset,
1234 uint32_t rd_words, uint32_t rw_words)
1235{
1236 uint64_t *data;
1237 uint32_t i;
1238 uint64_t sum = 0;
1239
1240 data = (uint64_t *)(uintptr_t)((uint8_t *)ctx + offset);
1241
1242 for (i = 0; i < rd_words; i++)
1243 sum += data[i];
1244
1245 for (; i < rd_words + rw_words; i++) {
1246 sum += data[i];
1247 data[i] += 1;
1248 }
1249
1250 return sum;
1251}
1252
1253static uint64_t rw_data(odp_event_t ev[], int num, uint32_t rd_words, uint32_t rw_words,
1254 odp_pool_type_t pool_type)
1255{
1256 uint64_t *data;
1257 uint32_t j;
1258 uint64_t sum = 0;
1260 odp_event_t *tbl;
1261
1262 for (int i = 0; i < num; i++) {
1263 if (pool_type == ODP_POOL_BUFFER) {
1265 } else if (pool_type == ODP_POOL_PACKET) {
1267 } else {
1268 evv = odp_event_vector_from_event(ev[i]);
1269 odp_event_vector_tbl(evv, &tbl);
1270 data = odp_buffer_addr(odp_buffer_from_event(tbl[0]));
1271 }
1272
1273 for (j = 0; j < rd_words; j++)
1274 sum += data[j];
1275
1276 for (; j < rd_words + rw_words; j++) {
1277 sum += data[j];
1278 data[j] += 1;
1279 }
1280 }
1281
1282 return sum;
1283}
1284
1285static uint64_t rw_data_stress(odp_event_t ev[], int num, uint32_t rd_words, uint32_t rw_words,
1286 uint32_t stress, odp_pool_type_t pool_type)
1287{
1288 uint64_t *data;
1289 uint64_t word;
1290 uint32_t j;
1291 uint64_t sum = 0;
1293 odp_event_t *tbl;
1294
1295 for (int i = 0; i < num; i++) {
1296 if (pool_type == ODP_POOL_BUFFER) {
1298 } else if (pool_type == ODP_POOL_PACKET) {
1300 } else {
1301 evv = odp_event_vector_from_event(ev[i]);
1302 odp_event_vector_tbl(evv, &tbl);
1303 data = odp_buffer_addr(odp_buffer_from_event(tbl[0]));
1304 }
1305
1306 for (j = 0; j < rd_words + rw_words; j++) {
1307 word = data[j];
1308
1309 if (stress & 0x1)
1310 sum += odph_stress_pow2_u32(word);
1311 if (stress & 0x2)
1312 sum += odph_stress_log2_u32(word);
1313 if (stress & 0x4)
1314 sum += odph_stress_sqrt_u32(word);
1315 if (stress & 0x8)
1316 sum += odph_stress_sqrt_f32(word);
1317
1318 if (j >= rd_words)
1319 data[j] = (word + 1) & DATA_MASK;
1320 }
1321 }
1322
1323 return sum;
1324}
1325
1326static int test_sched(void *arg)
1327{
1328 int num, num_enq, ret, thr;
1329 uint32_t i, rounds;
1330 uint64_t c1, c2, cycles, nsec;
1331 uint64_t events, enqueues, waits, events_prev;
1332 odp_time_t t1, t2, last_retry_ts;
1333 odp_queue_t queue;
1334 thread_arg_t *thread_arg = arg;
1335 test_global_t *global = thread_arg->global;
1336 test_options_t *test_options = &global->test_options;
1337 uint32_t num_sched = test_options->num_sched;
1338 uint32_t max_burst = test_options->max_burst;
1339 int num_group = test_options->num_group;
1340 int forward = test_options->forward;
1341 int fairness = test_options->fairness;
1342 const int touch_data = test_options->touch_data;
1343 const uint32_t stress = test_options->stress;
1344 const uint32_t rd_words = test_options->rd_words;
1345 const uint32_t rw_words = test_options->rw_words;
1346 uint32_t ctx_size = test_options->ctx_size;
1347 uint32_t ctx_rd_words = test_options->ctx_rd_words;
1348 uint32_t ctx_rw_words = test_options->ctx_rw_words;
1349 const uint32_t uarea_size = test_options->uarea_size;
1350 const uint32_t uarea_rd = test_options->uarea_rd;
1351 const uint32_t uarea_rw = test_options->uarea_rw;
1352 const odp_pool_type_t pool_type = test_options->pool_type;
1353 const uint32_t num_prefetch = test_options->num_prefetch;
1354 int touch_ctx = ctx_rd_words || ctx_rw_words;
1355 odp_atomic_u32_t *exit_threads = &global->exit_threads;
1356 uint32_t ctx_offset = 0;
1357 uint32_t sched_retries = 0;
1358 uint64_t data_sum = 0;
1359 uint64_t ctx_sum = 0;
1360 uint64_t uarea_sum = 0;
1361 uint64_t wait_ns = test_options->wait_ns;
1362 odp_event_t ev[max_burst];
1363
1364 thr = odp_thread_id();
1365
1366 if (forward || fairness)
1367 ctx_offset = ODPH_ROUNDUP_MULTIPLE(sizeof(queue_context_t), 8);
1368
1369 if (num_group > 0) {
1370 uint32_t num_join = test_options->num_join;
1371
1372 if (num_join) {
1373 int pos = 0;
1374 int n = 512;
1375 char str[n];
1376 int group_index = thread_arg->first_group;
1377
1378 pos += snprintf(&str[pos], n - pos,
1379 "Thread %i joined groups:", thr);
1380
1381 for (i = 0; i < num_join; i++) {
1382 if (join_group(global, group_index, thr))
1383 return -1;
1384
1385 pos += snprintf(&str[pos], n - pos, " %i",
1386 group_index);
1387
1388 group_index = (group_index + 1) % num_group;
1389 }
1390
1391 printf("%s\n", str);
1392
1393 } else {
1394 if (join_all_groups(global, thr))
1395 return -1;
1396 }
1397 }
1398
1399 for (i = 0; i < max_burst; i++)
1400 ev[i] = ODP_EVENT_INVALID;
1401
1402 enqueues = 0;
1403 events = 0;
1404 events_prev = 0;
1405 waits = 0;
1406 ret = 0;
1407
1408 /* Start all workers at the same time */
1409 odp_barrier_wait(&global->barrier);
1410
1411 t1 = odp_time_local();
1412 c1 = odp_cpu_cycles();
1413 last_retry_ts = t1;
1414
1415 for (rounds = 0; odp_likely(!odp_atomic_load_u32(exit_threads)); rounds++) {
1416 if (odp_unlikely(num_sched && events >= num_sched))
1417 break;
1418
1420 ev, max_burst);
1421
1422 if (odp_likely(num > 0)) {
1423 sched_retries = 0;
1424 events += num;
1425 i = 0;
1426
1427 if (odp_unlikely(uarea_size))
1428 uarea_sum += rw_uarea(ev, num, uarea_rd, uarea_rw);
1429
1430 if (odp_unlikely(ctx_size)) {
1431 queue_context_t *ctx = odp_queue_context(queue);
1432
1433 if (forward)
1434 queue = ctx->next;
1435
1436 if (fairness)
1437 odp_atomic_add_u64(&ctx->count, num);
1438
1439 if (odp_unlikely(touch_ctx))
1440 ctx_sum += rw_ctx_data(ctx, ctx_offset,
1441 ctx_rd_words,
1442 ctx_rw_words);
1443 }
1444
1445 if (odp_unlikely(touch_data)) {
1446 if (stress) {
1447 data_sum += rw_data_stress(ev, num, rd_words, rw_words,
1448 stress, pool_type);
1449 } else {
1450 data_sum += rw_data(ev, num, rd_words, rw_words, pool_type);
1451 }
1452 }
1453
1454 if (odp_unlikely(wait_ns)) {
1455 waits++;
1456 odp_time_wait_ns(wait_ns);
1457 }
1458
1459 if (num_prefetch)
1460 odp_schedule_prefetch(num_prefetch);
1461
1462 while (num) {
1463 num_enq = odp_queue_enq_multi(queue, &ev[i],
1464 num);
1465
1466 if (num_enq < 0) {
1467 ODPH_ERR("Enqueue failed. Round %u\n", rounds);
1468 odp_event_free_multi(&ev[i], num);
1469 ret = -1;
1470 break;
1471 }
1472
1473 num -= num_enq;
1474 i += num_enq;
1475 enqueues++;
1476 }
1477
1478 if (odp_unlikely(ret))
1479 break;
1480
1481 continue;
1482 } else if (num == 0) {
1483 sched_retries++;
1484 if (odp_unlikely(sched_retries > TIME_CHECK_INTERVAL)) {
1485 odp_time_t cur_time = odp_time_local();
1486
1487 /* Measure time from the last received event and
1488 * break if MAX_SCHED_WAIT_NS is exceeded */
1489 sched_retries = 0;
1490 if (events_prev != events) {
1491 events_prev = events;
1492 last_retry_ts = cur_time;
1493 } else if (odp_time_diff_ns(cur_time,
1494 last_retry_ts) >
1495 MAX_SCHED_WAIT_NS) {
1496 ODPH_ERR("Scheduling timed out\n");
1497 ret = -1;
1498 break;
1499 }
1500 }
1501 }
1502
1503 /* <0 not specified as an error but checking anyway */
1504 if (num < 0) {
1505 ODPH_ERR("Sched failed. Round %u\n", rounds);
1506 ret = -1;
1507 break;
1508 }
1509 }
1510
1511 c2 = odp_cpu_cycles();
1512 t2 = odp_time_local();
1513
1514 nsec = odp_time_diff_ns(t2, t1);
1515 cycles = odp_cpu_cycles_diff(c2, c1);
1516
1517 /* Update stats*/
1518 global->stat[thr].rounds = rounds;
1519 global->stat[thr].enqueues = enqueues;
1520 global->stat[thr].events = events;
1521 global->stat[thr].nsec = nsec;
1522 global->stat[thr].cycles = cycles;
1523 global->stat[thr].waits = waits;
1524 global->stat[thr].dummy_sum = data_sum + ctx_sum + uarea_sum;
1525 global->stat[thr].failed = ret;
1526
1527 if (odp_atomic_fetch_dec_u32(&global->num_worker) == 1) {
1528 /* The last worker frees all events. This is needed when the main
1529 * thread cannot do the clean up (ODP_SCHED_GROUP_WORKER). */
1530 odp_event_t event;
1531 uint64_t sched_wait = odp_schedule_wait_time(200 * ODP_TIME_MSEC_IN_NS);
1532
1533 /* Print queue and scheduler status at the end of the test, before any queues
1534 * are emptied or destroyed. */
1535 if (test_options->verbose) {
1538 }
1539
1540 while ((event = odp_schedule(NULL, sched_wait)) != ODP_EVENT_INVALID)
1541 odp_event_free(event);
1542 }
1543
1544 /* Pause scheduling before thread exit */
1546
1547 while (1) {
1548 ev[0] = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
1549
1550 if (ev[0] == ODP_EVENT_INVALID)
1551 break;
1552
1553 if (odp_unlikely(forward))
1554 queue = ((queue_context_t *)odp_queue_context(queue))->next;
1555
1556 if (odp_queue_enq(queue, ev[0])) {
1557 ODPH_ERR("Queue enqueue failed\n");
1558 odp_event_free(ev[0]);
1559 ret = -1;
1560 }
1561 }
1562
1563 return ret;
1564}
1565
1566static int start_workers(test_global_t *global, odp_instance_t instance)
1567{
1568 odph_thread_common_param_t thr_common;
1569 int i, ret;
1570 test_options_t *test_options = &global->test_options;
1571 int num_group = test_options->num_group;
1572 uint32_t num_join = test_options->num_join;
1573 int num_cpu = test_options->num_cpu;
1574 odph_thread_param_t thr_param[num_cpu];
1575 odp_thread_type_t thr_type = test_options->thr_type ?
1577
1578 odp_atomic_init_u32(&global->num_worker, num_cpu);
1579
1580 memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
1581 odph_thread_common_param_init(&thr_common);
1582
1583 thr_common.instance = instance;
1584 thr_common.cpumask = &global->cpumask;
1585
1586 for (i = 0; i < num_cpu; i++) {
1587 odph_thread_param_init(&thr_param[i]);
1588 thr_param[i].start = test_sched;
1589 thr_param[i].arg = &global->thread_arg[i];
1590 thr_param[i].thr_type = thr_type;
1591
1592 global->thread_arg[i].global = global;
1593 global->thread_arg[i].first_group = 0;
1594
1595 if (num_group > 0 && num_join) {
1596 /* Each thread joins only num_join groups, starting
1597 * from this group index and wrapping around the group
1598 * table. */
1599 int first_group = (i * num_join) % num_group;
1600
1601 global->thread_arg[i].first_group = first_group;
1602 }
1603 }
1604
1605 ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
1606 num_cpu);
1607
1608 if (ret != num_cpu) {
1609 ODPH_ERR("Thread create failed %i\n", ret);
1610 return -1;
1611 }
1612
1613 return 0;
1614}
1615
1616static double measure_wait_time_cycles(uint64_t wait_ns)
1617{
1618 uint64_t i, c1, c2, diff;
1619 uint64_t rounds;
1620 double wait_cycles;
1621
1622 if (wait_ns == 0)
1623 return 0.0;
1624
1625 /* Run measurement for 100msec or at least two times, so that effect
1626 * from CPU frequency scaling is minimized. */
1627 rounds = (100 * ODP_TIME_MSEC_IN_NS) / wait_ns;
1628 if (rounds == 0)
1629 rounds = 2;
1630
1631 c1 = odp_cpu_cycles();
1632
1633 for (i = 0; i < rounds; i++)
1634 odp_time_wait_ns(wait_ns);
1635
1636 c2 = odp_cpu_cycles();
1637 diff = odp_cpu_cycles_diff(c2, c1);
1638 wait_cycles = (double)diff / rounds;
1639
1640 printf("\nMeasured wait cycles: %.3f\n", wait_cycles);
1641
1642 return wait_cycles;
1643}
1644
1645static int output_results(test_global_t *global)
1646{
1647 int i, num;
1648 double rounds_ave, enqueues_ave, events_ave, events_per_sec, nsec_ave, cycles_ave;
1649 double waits_ave, wait_cycles, wait_cycles_ave;
1650 test_options_t *test_options = &global->test_options;
1651 int num_cpu = test_options->num_cpu;
1652 uint64_t wait_ns = test_options->wait_ns;
1653 uint64_t rounds_sum = 0;
1654 uint64_t enqueues_sum = 0;
1655 uint64_t events_sum = 0;
1656 uint64_t nsec_sum = 0;
1657 uint64_t cycles_sum = 0;
1658 uint64_t waits_sum = 0;
1659 uint32_t tot_rd = test_options->tot_rd_size;
1660 uint32_t tot_rw = test_options->tot_rw_size;
1661
1662 wait_cycles = measure_wait_time_cycles(wait_ns);
1663
1664 /* Averages */
1665 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1666 if (global->stat[i].failed) {
1667 num_cpu--;
1668 continue;
1669 }
1670 rounds_sum += global->stat[i].rounds;
1671 enqueues_sum += global->stat[i].enqueues;
1672 events_sum += global->stat[i].events;
1673 nsec_sum += global->stat[i].nsec;
1674 cycles_sum += global->stat[i].cycles;
1675 waits_sum += global->stat[i].waits;
1676 }
1677
1678 if (rounds_sum == 0 || num_cpu <= 0) {
1679 printf("No results.\n");
1680 return 0;
1681 }
1682
1683 rounds_ave = rounds_sum / num_cpu;
1684 enqueues_ave = enqueues_sum / num_cpu;
1685 events_ave = events_sum / num_cpu;
1686 nsec_ave = nsec_sum / num_cpu;
1687 cycles_ave = cycles_sum / num_cpu;
1688 waits_ave = waits_sum / num_cpu;
1689 wait_cycles_ave = waits_ave * wait_cycles;
1690 num = 0;
1691
1692 printf("\n");
1693 printf("RESULTS - per thread (Million events per sec):\n");
1694 printf("----------------------------------------------\n");
1695 printf(" 1 2 3 4 5 6 7 8 9 10");
1696
1697 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1698 if (global->stat[i].rounds) {
1699 if ((num % 10) == 0)
1700 printf("\n ");
1701
1702 if (global->stat[i].failed)
1703 printf(" n/a ");
1704 else
1705 printf("%6.1f ",
1706 (1000.0 * global->stat[i].events) /
1707 global->stat[i].nsec);
1708
1709 num++;
1710 }
1711 }
1712 printf("\n\n");
1713
1714 printf("RESULTS - average over %i threads:\n", num_cpu);
1715 printf("----------------------------------\n");
1716 printf(" schedule calls: %.3f\n", rounds_ave);
1717 printf(" enqueue calls: %.3f\n", enqueues_ave);
1718 printf(" duration: %.3f msec\n", nsec_ave / 1000000);
1719 printf(" num cycles: %.3f M\n", cycles_ave / 1000000);
1720 printf(" cycles per round: %.3f\n",
1721 cycles_ave / rounds_ave);
1722 printf(" cycles per event: %.3f\n",
1723 cycles_ave / events_ave);
1724 if (wait_ns) {
1725 printf(" without wait_ns cycles: %.3f\n",
1726 (cycles_ave - wait_cycles_ave) / events_ave);
1727 }
1728 printf(" ave events received: %.3f\n",
1729 events_ave / rounds_ave);
1730 printf(" rounds per sec: %.3f M\n",
1731 (1000.0 * rounds_ave) / nsec_ave);
1732
1733 events_per_sec = (1000.0 * events_ave) / nsec_ave;
1734 printf(" events per sec: %.3f M\n", events_per_sec);
1735
1736 printf(" extra reads per sec: %.3f MB\n", tot_rd * events_per_sec);
1737 printf(" extra writes per sec: %.3f MB\n", tot_rw * events_per_sec);
1738
1739 printf("TOTAL events per sec: %.3f M\n\n",
1740 (1000.0 * events_sum) / nsec_ave);
1741
1742 if (global->common_options.is_export) {
1743 if (test_common_write("schedule calls,enqueue calls,duration (msec),"
1744 "num cycles (M),cycles per round,cycles per event,"
1745 "ave events received,rounds per sec (M),"
1746 "events per sec (M),total events per sec (M)\n")) {
1747 ODPH_ERR("Export failed\n");
1748 test_common_write_term();
1749 return -1;
1750 }
1751
1752 if (test_common_write("%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n",
1753 rounds_ave, enqueues_ave, nsec_ave / 1000000,
1754 cycles_ave / 1000000, cycles_ave / rounds_ave,
1755 cycles_ave / events_ave, events_ave / rounds_ave,
1756 (1000.0 * rounds_ave) / nsec_ave,
1757 (1000.0 * events_ave) / nsec_ave,
1758 (1000.0 * events_sum) / nsec_ave)) {
1759 ODPH_ERR("Export failed\n");
1760 test_common_write_term();
1761 return -1;
1762 }
1763
1764 test_common_write_term();
1765 }
1766
1767 return 0;
1768}
1769
1770int main(int argc, char **argv)
1771{
1772 odph_helper_options_t helper_options;
1773 odp_instance_t instance;
1774 odp_init_t init;
1775 odp_shm_t shm;
1776 test_global_t *global;
1777 test_common_options_t common_options;
1778 int ret;
1779
1780 /* Let helper collect its own arguments (e.g. --odph_proc) */
1781 argc = odph_parse_options(argc, argv);
1782 if (odph_options(&helper_options)) {
1783 ODPH_ERR("Reading ODP helper options failed\n");
1784 exit(EXIT_FAILURE);
1785 }
1786
1787 argc = test_common_parse_options(argc, argv);
1788 if (test_common_options(&common_options)) {
1789 ODPH_ERR("Reading test options failed\n");
1790 exit(EXIT_FAILURE);
1791 }
1792
1793 /* List features not to be used */
1794 odp_init_param_init(&init);
1795 init.not_used.feat.cls = 1;
1796 init.not_used.feat.compress = 1;
1797 init.not_used.feat.crypto = 1;
1798 init.not_used.feat.dma = 1;
1799 init.not_used.feat.ipsec = 1;
1800 init.not_used.feat.ml = 1;
1801 init.not_used.feat.stash = 1;
1802 init.not_used.feat.timer = 1;
1803 init.not_used.feat.tm = 1;
1804
1805 init.mem_model = helper_options.mem_model;
1806
1807 /* Init ODP before calling anything else */
1808 if (odp_init_global(&instance, &init, NULL)) {
1809 ODPH_ERR("Global init failed\n");
1810 return -1;
1811 }
1812
1813 /* Init this thread */
1814 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
1815 ODPH_ERR("Local init failed\n");
1816 return -1;
1817 }
1818
1819 shm = odp_shm_reserve("sched_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
1820 if (shm == ODP_SHM_INVALID) {
1821 ODPH_ERR("SHM reserve failed\n");
1822 exit(EXIT_FAILURE);
1823 }
1824
1825 global = odp_shm_addr(shm);
1826 if (global == NULL) {
1827 ODPH_ERR("SHM alloc failed\n");
1828 exit(EXIT_FAILURE);
1829 }
1830 test_globals = global;
1831
1832 memset(global, 0, sizeof(test_global_t));
1833 global->pool = ODP_POOL_INVALID;
1834 global->ctx_shm = ODP_SHM_INVALID;
1835 odp_atomic_init_u32(&global->exit_threads, 0);
1836
1837 global->common_options = common_options;
1838
1839 if (setup_sig_handler()) {
1840 ODPH_ERR("Signal handler setup failed\n");
1841 exit(EXIT_FAILURE);
1842 }
1843
1844 if (parse_options(argc, argv, &global->test_options))
1845 return -1;
1846
1848
1849 if (global->test_options.ctx_size) {
1850 uint64_t size = (uint64_t)global->test_options.ctx_size *
1851 global->test_options.tot_queue;
1852
1853 global->ctx_shm = odp_shm_reserve("queue contexts", size,
1854 ODP_CACHE_LINE_SIZE, 0);
1855 if (global->ctx_shm == ODP_SHM_INVALID) {
1856 ODPH_ERR("SHM reserve %" PRIu64 " bytes failed\n", size);
1857 return -1;
1858 }
1859 }
1860
1861 if (schedule_config(global))
1862 return -1;
1863
1864 if (set_num_cpu(global))
1865 return -1;
1866
1867 ret = create_pool(global);
1868 if (ret)
1869 return ret;
1870
1871 if (create_groups(global))
1872 return -1;
1873
1874 if (create_all_queues(global))
1875 return -1;
1876
1877 if (global->test_options.verbose)
1879
1880 print_options(&global->test_options);
1881
1882 /* Start workers */
1883 start_workers(global, instance);
1884
1885 /* Wait workers to exit */
1886 odph_thread_join(global->thread_tbl, global->test_options.num_cpu);
1887
1888 print_queue_fairness(global);
1889
1890 if (destroy_queues(global))
1891 return -1;
1892
1893 if (destroy_groups(global))
1894 return -1;
1895
1896 if (output_results(global))
1897 return -1;
1898
1899 if (odp_pool_destroy(global->pool)) {
1900 ODPH_ERR("Pool destroy failed.\n");
1901 return -1;
1902 }
1903
1904 if (global->test_options.pool_type == ODP_POOL_EVENT_VECTOR &&
1905 odp_pool_destroy(global->evv_pool)) {
1906 ODPH_ERR("Event vector content pool destroy failed.\n");
1907 return -1;
1908 }
1909
1910 if (global->ctx_shm != ODP_SHM_INVALID)
1911 odp_shm_free(global->ctx_shm);
1912
1913 if (odp_shm_free(shm)) {
1914 ODPH_ERR("SHM free failed\n");
1915 exit(EXIT_FAILURE);
1916 }
1917
1918 if (odp_term_local()) {
1919 ODPH_ERR("Term local failed\n");
1920 return -1;
1921 }
1922
1923 if (odp_term_global(instance)) {
1924 ODPH_ERR("Term global failed\n");
1925 return -1;
1926 }
1927
1928 return 0;
1929}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_init_u64(odp_atomic_u64_t *atom, uint64_t val)
Initialize atomic uint64 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
uint32_t odp_atomic_fetch_dec_u32(odp_atomic_u32_t *atom)
Fetch and decrement atomic uint32 variable.
void odp_atomic_add_u64(odp_atomic_u64_t *atom, uint64_t val)
Add to atomic uint64 variable.
uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
Load value of atomic uint64 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.
uint32_t odp_buffer_size(odp_buffer_t buf)
Buffer maximum data size.
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.
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.
#define ODP_BUFFER_INVALID
Invalid buffer.
#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
#define odp_likely(x)
Branch likely taken.
Definition spec/hints.h:59
#define ODP_CACHE_LINE_ROUNDUP(x)
Round up to cache line size.
uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
CPU cycle count difference.
uint64_t odp_cpu_cycles(void)
Current CPU cycle count.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
odp_event_t odp_event_vector_to_event(odp_event_vector_t evv)
Convert event vector handle to event.
void odp_event_vector_size_set(odp_event_vector_t evv, uint32_t size)
Set the number of events stored in a vector.
void odp_event_vector_free(odp_event_vector_t evv)
Free event vector.
odp_event_vector_t odp_event_vector_from_event(odp_event_t ev)
Get event vector handle from event.
uint32_t odp_event_vector_tbl(odp_event_vector_t evv, odp_event_t **event_tbl)
Get event vector table.
odp_event_vector_t odp_event_vector_alloc(odp_pool_t pool)
Allocate event vector from event vector pool.
#define ODP_EVENT_VECTOR_INVALID
Invalid event vector.
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.
void * odp_event_user_area(odp_event_t event)
Event user area.
#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_event_t odp_packet_to_event(odp_packet_t pkt)
Convert packet handle to event.
uint32_t odp_packet_seg_len(odp_packet_t pkt)
Packet data length following the data pointer.
void * odp_packet_data(odp_packet_t pkt)
Packet data pointer.
odp_packet_t odp_packet_alloc(odp_pool_t pool, uint32_t len)
Allocate a packet from a packet pool.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
#define ODP_PACKET_INVALID
Invalid packet.
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()
odp_pool_type_t
Pool types.
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_BUFFER
Buffer pool.
@ ODP_POOL_PACKET
Packet pool.
@ ODP_POOL_EVENT_VECTOR
Event vector 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.
void odp_queue_print_all(void)
Print debug info about all queues.
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.
int odp_schedule_group_t
Scheduler thread group.
void odp_schedule_config_init(odp_schedule_config_t *config)
Initialize schedule configuration options.
int odp_schedule_group_join(odp_schedule_group_t group, const odp_thrmask_t *mask)
Join a schedule group.
void odp_schedule_group_param_init(odp_schedule_group_param_t *param)
Initialize schedule group parameters.
#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.
int odp_schedule_default_prio(void)
Default scheduling priority level.
void odp_schedule_pause(void)
Pause scheduling.
int odp_schedule_max_prio(void)
Maximum scheduling priority level.
void odp_schedule_prefetch(int num)
Prefetch events for next schedule call.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
int odp_schedule_capability(odp_schedule_capability_t *capa)
Query scheduler capabilities.
odp_schedule_group_t odp_schedule_group_create_2(const char *name, const odp_thrmask_t *mask, const odp_schedule_group_param_t *param)
Schedule group create with parameters.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
void odp_schedule_print(void)
Print debug info about scheduler.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
void odp_shm_print_all(void)
Print all shared memory blocks.
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.
enum odp_thread_type_e odp_thread_type_t
Thread type.
#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.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Cache stashing configuration.
union odp_cache_stash_config_t::@161 regions
Region specific configuration toggle.
odp_cache_stash_region_t event_user_area
Cache stashing for event user area.
odp_cache_stash_region_t event_data
Cache stashing for event data.
uint32_t all
All bits of the bit field structure.
odp_cache_stash_region_t event_metadata
Cache stashing for event metadata.
odp_cache_stash_region_t queue_context
Cache stashing for queue context region.
Region specific cache stashing configuration.
uint32_t len
Length in bytes to cache.
struct odp_cache_stash_region_t::@160 l3
L3 cache stashing.
struct odp_cache_stash_region_t::@159 l2
L2 cache stashing.
uint32_t offset
Byte offset into a region to start caching from.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@133 buf
Buffer pool capabilities
uint32_t max_uarea_size
Maximum user area size in bytes.
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
struct odp_pool_capability_t::@137 event_vector
Event vector pool capabilities.
uint32_t max_size
Maximum buffer data size in bytes.
uint32_t max_pools
Maximum number of pools of any type (odp_pool_type_t)
uint32_t max_seg_len
Maximum packet segment data length in bytes.
uint32_t uarea_size
Minimum user area size in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
struct odp_pool_param_t::@142 event_vector
Parameters for event vector pools.
uint32_t align
Minimum buffer alignment in bytes.
uint32_t size
Minimum buffer size in bytes.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t max_size
Maximum number of handles (such as odp_packet_t) in a vector.
struct odp_pool_param_t::@138 buf
Parameters for buffer 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.
uint32_t max_groups
Maximum number of scheduling groups.
Schedule configuration.
Schedule group parameters.
odp_cache_stash_config_t common
Common group specific cache stashing hints.
struct odp_schedule_group_param_t::@165 cache_stash_hints
Group specific cache stashing hints.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t stash
Stash APIs, e.g., odp_stash_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t dma
DMA APIs, e.g., odp_dma_xxx()
uint32_t timer
Timer APIs, e.g., odp_timer_xxx(), odp_timeout_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
uint32_t ml
Machine Learning APIs, e.g., odp_ml_xxx()
struct odp_feature_t::@174 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()