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