API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_lock_perf.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2021-2024 Nokia
3 */
4
13#include <stdio.h>
14#include <string.h>
15#include <stdint.h>
16#include <inttypes.h>
17#include <stdlib.h>
18#include <getopt.h>
19
20#include <odp_api.h>
21#include <odp/helper/odph_api.h>
22
23#include <export_results.h>
24
25/* Max number of workers if num_cpu=0 */
26#define DEFAULT_MAX_WORKERS 10
27
28/* Max number of counters */
29#define MAX_COUNTERS 8
30
31#define TEST_INFO(name, test, validate) { name, test, validate }
32
33/* Maximum number of results to be held */
34#define TEST_MAX_BENCH 50
35
36typedef enum repeat_t {
37 REPEAT_NO,
38 REPEAT_UNTIL_FAIL,
39 REPEAT_FOREVER,
40} repeat_t;
41
42typedef enum place_t {
43 PLACE_PACK,
44 PLACE_SEPARATE,
45 PLACE_ALL_SEPARATE,
46} place_t;
47
48/* Command line options */
49typedef struct test_options_t {
50 uint32_t num_cpu;
51 uint32_t type;
52 uint64_t num_round;
53 repeat_t repeat;
54 uint32_t num_counter;
55 place_t place;
56} test_options_t;
57
58/* command line options default values */
59static test_options_t test_options_def = {
60 .num_cpu = 0,
61 .type = 0,
62 .num_round = 100000,
63 .repeat = REPEAT_NO,
64 .num_counter = 2,
65 .place = 2,
66};
67
68typedef struct test_global_t test_global_t;
69
70/* Test function template */
71typedef void (*test_fn_t)(test_global_t *g, uint64_t **counter,
72 uint32_t num_counter);
73/* Test result validation function template */
74typedef int (*validate_fn_t)(test_global_t *g, uint64_t **counter,
75 uint32_t num_counter);
76
77/* Worker thread context */
78typedef struct test_thread_ctx_t {
79 test_global_t *global;
80 test_fn_t func;
81 uint64_t nsec;
82 uint64_t cycles;
83 uint32_t idx;
84} test_thread_ctx_t;
85
86typedef struct results_t {
87 const char *test_name;
88 double cycles_per_round;
89 double nsec_per_op;
90 double rounds_per_cpu;
91 double total_rounds;
92} results_t;
93
94/* Global data */
95struct test_global_t {
96 test_options_t test_options;
97 uint32_t cur_type;
98 odp_barrier_t barrier;
99 odp_cpumask_t cpumask;
100 odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
101 test_thread_ctx_t thread_ctx[ODP_THREAD_COUNT_MAX];
102 test_common_options_t common_options;
103 results_t results[TEST_MAX_BENCH];
104 struct {
105 struct ODP_ALIGNED_CACHE {
106 odp_spinlock_t lock;
107 uint64_t counter[MAX_COUNTERS];
108 } spinlock;
109 struct ODP_ALIGNED_CACHE {
111 uint64_t counter[MAX_COUNTERS];
112 } spinlock_recursive;
113 struct ODP_ALIGNED_CACHE {
114 odp_rwlock_t lock;
115 uint64_t counter[MAX_COUNTERS];
116 } rwlock;
117 struct ODP_ALIGNED_CACHE {
119 uint64_t counter[MAX_COUNTERS];
120 } rwlock_recursive;
121 struct ODP_ALIGNED_CACHE {
122 odp_ticketlock_t lock;
123 uint64_t counter[MAX_COUNTERS];
124 } ticketlock;
125 struct ODP_ALIGNED_CACHE {
126 uint64_t counter[MAX_COUNTERS];
127 } separate;
128 struct {
129 uint64_t ODP_ALIGNED_CACHE counter;
130 } all_separate[MAX_COUNTERS];
131 } item;
132};
133
134typedef struct {
135 const char *name;
136 test_fn_t test_fn;
137 validate_fn_t validate_fn;
138} test_case_t;
139
140static test_global_t *test_global;
141
142static inline void test_spinlock(test_global_t *g, uint64_t **counter,
143 uint32_t num_counter)
144{
145 odp_spinlock_t *lock = &g->item.spinlock.lock;
146
147 for (uint64_t i = 0; i < g->test_options.num_round; i++) {
148 odp_spinlock_lock(lock);
149 for (uint32_t j = 0; j < num_counter; j++)
150 (*counter[j])++;
152 }
153}
154
155static inline void test_spinlock_recursive(test_global_t *g, uint64_t **counter,
156 uint32_t num_counter)
157{
158 odp_spinlock_recursive_t *lock = &g->item.spinlock_recursive.lock;
159
160 for (uint64_t i = 0; i < g->test_options.num_round; i++) {
163 for (uint32_t j = 0; j < num_counter; j++)
164 (*counter[j])++;
167 }
168}
169
170static inline void test_rwlock(test_global_t *g, uint64_t **counter,
171 uint32_t num_counter)
172{
173 odp_rwlock_t *lock = &g->item.rwlock.lock;
174
175 for (uint64_t i = 0; i < g->test_options.num_round; i++) {
177 for (uint32_t j = 0; j < num_counter; j++)
178 (*counter[j])++;
181 for (uint32_t j = 1; j < num_counter; j++)
182 if (*counter[0] != *counter[j]) {
184 ODPH_ERR("Error: Counter mismatch\n");
185 return;
186 }
188 }
189}
190
191static inline void test_rwlock_recursive(test_global_t *g, uint64_t **counter,
192 uint32_t num_counter)
193{
194 odp_rwlock_recursive_t *lock = &g->item.rwlock_recursive.lock;
195
196 for (uint64_t i = 0; i < g->test_options.num_round; i++) {
199 for (uint32_t j = 0; j < num_counter; j++)
200 (*counter[j])++;
205 for (uint32_t j = 1; j < num_counter; j++)
206 if (*counter[0] != *counter[j]) {
209 ODPH_ERR("Error: Counter mismatch\n");
210 return;
211 }
214 }
215}
216
217static inline void test_ticketlock(test_global_t *g, uint64_t **counter,
218 uint32_t num_counter)
219{
220 odp_ticketlock_t *lock = &g->item.ticketlock.lock;
221
222 for (uint64_t i = 0; i < g->test_options.num_round; i++) {
224 for (uint32_t j = 0; j < num_counter; j++)
225 (*counter[j])++;
227 }
228}
229
230static inline int validate_generic(test_global_t *g, uint64_t **counter,
231 uint32_t num_counter)
232{
233 int status = 0;
234 uint64_t total = (uint64_t)g->test_options.num_cpu * g->test_options.num_round;
235
236 for (uint32_t i = 0; i < num_counter; i++) {
237 if (*counter[i] != total) {
238 status = 1;
239 ODPH_ERR("Error: Counter %d value %" PRIu64 " expected %" PRIu64 "\n",
240 i, *counter[i], total);
241 }
242 }
243
244 return status;
245}
246
247static void print_usage(void)
248{
249 printf("\n"
250 "Lock performance test\n"
251 "\n"
252 "Usage: odp_lock_perf [options]\n"
253 "\n"
254 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs (or max %d) (default)\n"
255 " -t, --type Lock type to test. 0: all (default %u)\n"
256 " 1: odp_spinlock_t\n"
257 " 2: odp_spinlock_recursive_t\n"
258 " 3: odp_rwlock_t\n"
259 " 4: odp_rwlock_recursive_t\n"
260 " 5: odp_ticketlock_t\n"
261 " -r, --num_round Number of rounds (default %" PRIu64 ")\n"
262 " -e, --repeat Repeat the tests (default %u)\n"
263 " 0: no repeat, run the tests once\n"
264 " 1: repeat until failure\n"
265 " 2: repeat forever\n"
266 " -o, --num_counter Number of counters (default %u)\n"
267 " -p, --place Counter placement (default %d)\n"
268 " 0: pack to same cache line with lock\n"
269 " 1: pack to separate cache line\n"
270 " 2: place each counter to separate cache line\n"
271 " -h, --help This help\n"
272 "\n",
273 DEFAULT_MAX_WORKERS, test_options_def.type,
274 test_options_def.num_round, test_options_def.repeat,
275 test_options_def.num_counter, test_options_def.place);
276}
277
278static void print_info(test_options_t *test_options)
279{
280 printf("\nLock performance test configuration:\n");
281 printf(" num cpu %u\n", test_options->num_cpu);
282 printf(" type %u\n", test_options->type);
283 printf(" num rounds %" PRIu64 "\n", test_options->num_round);
284 printf(" repeat %u\n", test_options->repeat);
285 printf(" num counters %u\n", test_options->num_counter);
286 printf(" place %u\n", test_options->place);
287 printf("\n\n");
288}
289
290static int output_summary(test_global_t *global)
291{
292 int results_size = ODPH_ARRAY_SIZE(global->results);
293 results_t res;
294
295 if (global->common_options.is_export) {
296 if (test_common_write("function name,rounds/cpu (M/s),"
297 "total rounds (M/s),cycles/round,nsec/round\n")) {
298 test_common_write_term();
299 return -1;
300 }
301 }
302
303 printf("Average results over %i threads:\n", global->test_options.num_cpu);
304 printf("%-33s %-18s %-20s %-14s %-12s\n", "function name", "rounds/cpu (M/s)",
305 "total rounds (M/s)", "cycles/round", "nsec/round");
306 printf("----------------------------------------------------------------------------"
307 "---------------------\n");
308 for (int i = 0; i < results_size && global->results[i].test_name; i++) {
309 res = global->results[i];
310 printf("[%02d] %-28s %-18.2f %-20.2f %-14.2f %-12.2f\n", i + 1,
311 res.test_name, res.rounds_per_cpu, res.total_rounds,
312 res.cycles_per_round, res.nsec_per_op);
313 if (global->common_options.is_export) {
314 if (test_common_write("%s,%f,%f,%f,%f\n", res.test_name, res.rounds_per_cpu,
315 res.total_rounds, res.cycles_per_round,
316 res.nsec_per_op)){
317 test_common_write_term();
318 return -1;
319 }
320 }
321 }
322
323 if (global->common_options.is_export)
324 test_common_write_term();
325
326 return 0;
327}
328
329static int parse_options(int argc, char *argv[], test_options_t *test_options)
330{
331 int opt;
332 int ret = 0;
333
334 static const struct option longopts[] = {
335 { "num_cpu", required_argument, NULL, 'c' },
336 { "type", required_argument, NULL, 't' },
337 { "num_round", required_argument, NULL, 'r' },
338 { "repeat", required_argument, NULL, 'e' },
339 { "num_counter", required_argument, NULL, 'o' },
340 { "place", required_argument, NULL, 'p' },
341 { "help", no_argument, NULL, 'h' },
342 { NULL, 0, NULL, 0 }
343 };
344
345 static const char *shortopts = "+c:t:r:e:o:p:h";
346
347 *test_options = test_options_def;
348
349 while (1) {
350 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
351
352 if (opt == -1)
353 break;
354
355 switch (opt) {
356 case 'c':
357 test_options->num_cpu = atoi(optarg);
358 break;
359 case 't':
360 test_options->type = atoi(optarg);
361 break;
362 case 'r':
363 test_options->num_round = atoll(optarg);
364 break;
365 case 'e':
366 test_options->repeat = atoi(optarg);
367 break;
368 case 'o':
369 test_options->num_counter = atoi(optarg);
370 break;
371 case 'p':
372 test_options->place = atoi(optarg);
373 break;
374 case 'h':
375 /* fall through */
376 default:
377 print_usage();
378 ret = -1;
379 break;
380 }
381 }
382
383 if (test_options->num_round < 1) {
384 ODPH_ERR("Invalid number of test rounds: %" PRIu64 "\n",
385 test_options->num_round);
386 return -1;
387 }
388
389 if (test_options->num_counter < 1 ||
390 test_options->num_counter > MAX_COUNTERS) {
391 ODPH_ERR("Invalid number of counters: %" PRIu32 "\n",
392 test_options->num_counter);
393 return -1;
394 }
395
396 return ret;
397}
398
399static int set_num_cpu(test_global_t *global)
400{
401 int ret, max_num;
402 test_options_t *test_options = &global->test_options;
403 int num_cpu = test_options->num_cpu;
404
405 /* One thread used for the main thread */
406 if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
407 ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
408 return -1;
409 }
410
411 max_num = num_cpu;
412 if (num_cpu == 0) {
413 max_num = ODP_THREAD_COUNT_MAX - 1;
414 if (max_num > DEFAULT_MAX_WORKERS)
415 max_num = DEFAULT_MAX_WORKERS;
416 }
417
418 ret = odp_cpumask_default_worker(&global->cpumask, max_num);
419
420 if (num_cpu && ret != num_cpu) {
421 ODPH_ERR("Too many workers. Max supported %i.\n", ret);
422 return -1;
423 }
424
425 /* Zero: all available workers */
426 if (num_cpu == 0) {
427 if (ret > max_num) {
428 ODPH_ERR("Too many cpus from odp_cpumask_default_worker(): %i\n", ret);
429 return -1;
430 }
431
432 num_cpu = ret;
433 test_options->num_cpu = num_cpu;
434 }
435
436 odp_barrier_init(&global->barrier, num_cpu);
437
438 return 0;
439}
440
441static int init_test(test_global_t *g, const char *name)
442{
443 printf("TEST: %s\n", name);
444
445 memset(&g->item, 0, sizeof(g->item));
446 odp_spinlock_init(&g->item.spinlock.lock);
447 odp_spinlock_recursive_init(&g->item.spinlock_recursive.lock);
448 odp_rwlock_init(&g->item.rwlock.lock);
449 odp_rwlock_recursive_init(&g->item.rwlock_recursive.lock);
450 odp_ticketlock_init(&g->item.ticketlock.lock);
451
452 return 0;
453}
454
455static void fill_counter_ptrs(test_global_t *g, uint64_t **counter_out)
456{
457 test_options_t *test_options = &g->test_options;
458
459 memset(counter_out, 0, sizeof(uint64_t *) * MAX_COUNTERS);
460
461 switch (test_options->place) {
462 case PLACE_PACK:
463 for (uint32_t i = 0; i < test_options->num_counter; i++) {
464 switch (g->cur_type) {
465 case 0:
466 counter_out[i] = &g->item.spinlock.counter[i];
467 break;
468 case 1:
469 counter_out[i] = &g->item.spinlock_recursive.counter[i];
470 break;
471 case 2:
472 counter_out[i] = &g->item.rwlock.counter[i];
473 break;
474 case 3:
475 counter_out[i] = &g->item.rwlock_recursive.counter[i];
476 break;
477 case 4:
478 counter_out[i] = &g->item.ticketlock.counter[i];
479 break;
480 }
481 }
482 break;
483 case PLACE_SEPARATE:
484 for (uint32_t i = 0; i < test_options->num_counter; i++)
485 counter_out[i] = &g->item.separate.counter[i];
486 break;
487 case PLACE_ALL_SEPARATE:
488 for (uint32_t i = 0; i < test_options->num_counter; i++)
489 counter_out[i] = &g->item.all_separate[i].counter;
490 break;
491 }
492}
493
494static int run_test(void *arg)
495{
496 uint64_t nsec;
497 odp_time_t t1, t2;
498 uint64_t cycles;
499 uint64_t c1, c2;
500 test_thread_ctx_t *thread_ctx = arg;
501 test_global_t *global = thread_ctx->global;
502 test_options_t *test_options = &global->test_options;
503 test_fn_t test_func = thread_ctx->func;
504 uint64_t *counter[MAX_COUNTERS];
505
506 fill_counter_ptrs(global, counter);
507
508 /* Start all workers at the same time */
509 odp_barrier_wait(&global->barrier);
510
512 c1 = odp_cpu_cycles();
513
514 test_func(global, counter, test_options->num_counter);
515
516 c2 = odp_cpu_cycles();
518
519 nsec = odp_time_diff_ns(t2, t1);
520 cycles = odp_cpu_cycles_diff(c2, c1);
521
522 /* Update stats */
523 thread_ctx->nsec = nsec;
524 thread_ctx->cycles = cycles;
525
526 return 0;
527}
528
529static int start_workers(test_global_t *global, odp_instance_t instance,
530 test_fn_t func)
531{
532 odph_thread_common_param_t param;
533 int i, ret;
534 test_options_t *test_options = &global->test_options;
535 int num_cpu = test_options->num_cpu;
536 odph_thread_param_t thr_param[num_cpu];
537
538 odph_thread_common_param_init(&param);
539 param.instance = instance;
540 param.cpumask = &global->cpumask;
541
542 for (i = 0; i < num_cpu; i++) {
543 test_thread_ctx_t *thread_ctx = &global->thread_ctx[i];
544
545 thread_ctx->global = global;
546 thread_ctx->idx = i;
547 thread_ctx->func = func;
548
549 odph_thread_param_init(&thr_param[i]);
550 thr_param[i].thr_type = ODP_THREAD_WORKER;
551 thr_param[i].start = run_test;
552 thr_param[i].arg = thread_ctx;
553 }
554
555 ret = odph_thread_create(global->thread_tbl, &param, thr_param,
556 num_cpu);
557 if (ret != num_cpu) {
558 ODPH_ERR("Failed to create all threads %i\n", ret);
559 return -1;
560 }
561
562 return 0;
563}
564
565static int validate_results(test_global_t *global, validate_fn_t validate)
566{
567 test_options_t *test_options = &global->test_options;
568 uint64_t *counter[MAX_COUNTERS];
569
570 fill_counter_ptrs(global, counter);
571
572 if (validate(global, counter, test_options->num_counter))
573 return -1;
574
575 return 0;
576}
577
581static test_case_t test_suite[] = {
582 TEST_INFO("odp_spinlock", test_spinlock, validate_generic),
583 TEST_INFO("odp_spinlock_recursive", test_spinlock_recursive, validate_generic),
584 TEST_INFO("odp_rwlock", test_rwlock, validate_generic),
585 TEST_INFO("odp_rwlock_recursive", test_rwlock_recursive, validate_generic),
586 TEST_INFO("odp_ticketlock", test_ticketlock, validate_generic),
587};
588
589ODP_STATIC_ASSERT(ODPH_ARRAY_SIZE(test_suite) < TEST_MAX_BENCH,
590 "Result array is too small to hold all the results");
591
592static void output_results(test_global_t *global, int idx)
593{
594 int i, num;
595 double cycles_per_round, nsec_ave, nsec_per_round, rounds_per_cpu, total_rounds;
596 test_options_t *test_options = &global->test_options;
597 int num_cpu = test_options->num_cpu;
598 uint64_t num_round = test_options->num_round;
599 uint64_t nsec_sum = 0;
600 uint64_t cycles_sum = 0;
601
602 global->results[idx].test_name = test_suite[idx].name;
603
604 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
605 nsec_sum += global->thread_ctx[i].nsec;
606 cycles_sum += global->thread_ctx[i].cycles;
607 }
608
609 if (nsec_sum == 0) {
610 printf("No results.\n");
611 return;
612 }
613
614 nsec_ave = (double)nsec_sum / num_cpu;
615 nsec_per_round = (double)nsec_sum / (num_cpu * num_round);
616 cycles_per_round = (double)cycles_sum / (num_cpu * num_round);
617 num = 0;
618
619 rounds_per_cpu = num_round / (nsec_ave / 1000.0);
620 total_rounds = ((uint64_t)num_cpu * num_round) / (nsec_ave / 1000.0);
621
622 global->results[idx].cycles_per_round = cycles_per_round;
623 global->results[idx].rounds_per_cpu = rounds_per_cpu;
624 global->results[idx].nsec_per_op = nsec_per_round;
625 global->results[idx].total_rounds = total_rounds;
626
627 printf("------------------------------------------------\n");
628 printf("Per thread results (Millions of rounds per sec):\n");
629 printf("------------------------------------------------\n");
630 printf(" 1 2 3 4 5 6 7 8 9 10");
631
632 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
633 if (global->thread_ctx[i].nsec) {
634 if ((num % 10) == 0)
635 printf("\n ");
636
637 printf("%8.3f ", num_round / (global->thread_ctx[i].nsec / 1000.0));
638 num++;
639 }
640 }
641 printf("\n\n");
642
643 printf("Average results over %i threads:\n", num_cpu);
644 printf("------------------------------------------\n");
645 printf(" duration: %8.4f sec\n", nsec_ave / ODP_TIME_SEC_IN_NS);
646 printf(" cycles per round %8.2f\n", cycles_per_round);
647 printf(" nsec per round: %8.2f\n", nsec_per_round);
648 printf(" rounds per cpu: %8.3fM rounds/sec\n", rounds_per_cpu);
649 printf(" total rounds: %8.3fM rounds/sec\n", total_rounds);
650 printf("\n\n");
651}
652
653int main(int argc, char **argv)
654{
655 odph_helper_options_t helper_options;
656 odp_instance_t instance;
657 odp_init_t init;
658 odp_shm_t shm;
659 test_options_t test_options;
660 int num_tests, i;
661 test_common_options_t common_options;
662
663 /* Let helper collect its own arguments (e.g. --odph_proc) */
664 argc = odph_parse_options(argc, argv);
665 if (odph_options(&helper_options)) {
666 ODPH_ERR("Error: reading ODP helper options failed.\n");
667 exit(EXIT_FAILURE);
668 }
669
670 argc = test_common_parse_options(argc, argv);
671 if (test_common_options(&common_options)) {
672 ODPH_ERR("Error: reading test common options failed\n");
673 exit(EXIT_FAILURE);
674 }
675
676 if (parse_options(argc, argv, &test_options))
677 exit(EXIT_FAILURE);
678
679 /* List features not to be used */
680 odp_init_param_init(&init);
681 init.not_used.feat.cls = 1;
682 init.not_used.feat.compress = 1;
683 init.not_used.feat.crypto = 1;
684 init.not_used.feat.ipsec = 1;
685 init.not_used.feat.schedule = 1;
686 init.not_used.feat.stash = 1;
687 init.not_used.feat.timer = 1;
688 init.not_used.feat.tm = 1;
689
690 init.mem_model = helper_options.mem_model;
691
692 /* Init ODP before calling anything else */
693 if (odp_init_global(&instance, &init, NULL)) {
694 ODPH_ERR("Global init failed.\n");
695 exit(EXIT_FAILURE);
696 }
697
698 /* Init this thread */
699 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
700 ODPH_ERR("Local init failed.\n");
701 exit(EXIT_FAILURE);
702 }
703
704 /* Reserve memory for global data from shared mem */
705 shm = odp_shm_reserve("test_global", sizeof(test_global_t),
706 ODP_CACHE_LINE_SIZE, 0);
707
708 if (shm == ODP_SHM_INVALID) {
709 ODPH_ERR("Shared memory reserve failed.\n");
710 exit(EXIT_FAILURE);
711 }
712
713 test_global = odp_shm_addr(shm);
714 if (test_global == NULL) {
715 ODPH_ERR("Shared memory alloc failed.\n");
716 exit(EXIT_FAILURE);
717 }
718 memset(test_global, 0, sizeof(test_global_t));
719 test_global->test_options = test_options;
720 test_global->common_options = common_options;
721
723
724 if (set_num_cpu(test_global))
725 exit(EXIT_FAILURE);
726
727 print_info(&test_global->test_options);
728
729 /* Loop all test cases */
730 num_tests = ODPH_ARRAY_SIZE(test_suite);
731
732 while (1) {
733 for (i = 0; i < num_tests; i++) {
734 if (test_options.type && test_options.type != (uint32_t)i + 1)
735 continue;
736
737 test_global->cur_type = i;
738
739 /* Initialize test variables */
740 if (init_test(test_global, test_suite[i].name)) {
741 ODPH_ERR("Failed to initialize test.\n");
742 exit(EXIT_FAILURE);
743 }
744
745 /* Start workers */
746 if (start_workers(test_global, instance, test_suite[i].test_fn))
747 exit(EXIT_FAILURE);
748
749 /* Wait workers to exit */
750 odph_thread_join(test_global->thread_tbl,
751 test_global->test_options.num_cpu);
752
753 output_results(test_global, i);
754
755 /* Validate test results */
756 if (validate_results(test_global, test_suite[i].validate_fn)) {
757 ODPH_ERR("Test %s result validation failed.\n",
758 test_suite[i].name);
759 if (test_options.repeat != REPEAT_FOREVER)
760 exit(EXIT_FAILURE);
761 }
762 }
763
764 if (test_options.repeat == REPEAT_NO)
765 break;
766 }
767
768 if (output_summary(test_global)) {
769 ODPH_ERR("Outputting summary failed.\n");
770 exit(EXIT_FAILURE);
771 }
772
773 if (odp_shm_free(shm)) {
774 ODPH_ERR("Shm free failed.\n");
775 exit(EXIT_FAILURE);
776 }
777
778 if (odp_term_local()) {
779 ODPH_ERR("Local terminate failed.\n");
780 exit(EXIT_FAILURE);
781 }
782
783 if (odp_term_global(instance)) {
784 ODPH_ERR("Global terminate failed.\n");
785 exit(EXIT_FAILURE);
786 }
787
788 return 0;
789}
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
#define ODP_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
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.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
#define ODP_STATIC_ASSERT(cond, msg)
Compile time assertion macro.
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.
void odp_ticketlock_init(odp_ticketlock_t *tklock)
Initialize ticket lock.
void odp_spinlock_lock(odp_spinlock_t *splock)
Acquire spin lock.
void odp_rwlock_recursive_read_unlock(odp_rwlock_recursive_t *lock)
Release recursive rwlock after reading.
void odp_spinlock_init(odp_spinlock_t *splock)
Initialize spin lock.
void odp_ticketlock_lock(odp_ticketlock_t *tklock)
Acquire ticket lock.
void odp_rwlock_read_lock(odp_rwlock_t *rwlock)
Acquire read permission on a reader/writer lock.
void odp_spinlock_recursive_init(odp_spinlock_recursive_t *lock)
Initialize recursive spinlock.
void odp_rwlock_read_unlock(odp_rwlock_t *rwlock)
Release read permission on a reader/writer lock.
void odp_rwlock_recursive_init(odp_rwlock_recursive_t *lock)
Initialize recursive rwlock.
void odp_rwlock_recursive_write_unlock(odp_rwlock_recursive_t *lock)
Release recursive rwlock after writing.
void odp_ticketlock_unlock(odp_ticketlock_t *tklock)
Release ticket lock.
void odp_rwlock_recursive_read_lock(odp_rwlock_recursive_t *lock)
Acquire recursive rwlock for reading.
void odp_spinlock_recursive_unlock(odp_spinlock_recursive_t *lock)
Release recursive spinlock.
void odp_rwlock_write_unlock(odp_rwlock_t *rwlock)
Release write permission on a reader/writer lock.
void odp_rwlock_write_lock(odp_rwlock_t *rwlock)
Acquire write permission on a reader/writer lock.
void odp_spinlock_unlock(odp_spinlock_t *splock)
Release spin lock.
void odp_spinlock_recursive_lock(odp_spinlock_recursive_t *lock)
Acquire recursive spinlock.
void odp_rwlock_init(odp_rwlock_t *rwlock)
Initialize a reader/writer lock.
void odp_rwlock_recursive_write_lock(odp_rwlock_recursive_t *lock)
Acquire recursive rwlock for writing.
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.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
odp_time_t odp_time_local_strict(void)
Current local time (strict)
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
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 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 schedule
Scheduler APIs, e.g., odp_schedule_xxx()
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()