API Reference Manual  1.46.0
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 
36 typedef enum repeat_t {
37  REPEAT_NO,
38  REPEAT_UNTIL_FAIL,
39  REPEAT_FOREVER,
40 } repeat_t;
41 
42 typedef enum place_t {
43  PLACE_PACK,
44  PLACE_SEPARATE,
45  PLACE_ALL_SEPARATE,
46 } place_t;
47 
48 /* Command line options */
49 typedef 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 */
59 static 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 
68 typedef struct test_global_t test_global_t;
69 
70 /* Test function template */
71 typedef void (*test_fn_t)(test_global_t *g, uint64_t **counter,
72  uint32_t num_counter);
73 /* Test result validation function template */
74 typedef int (*validate_fn_t)(test_global_t *g, uint64_t **counter,
75  uint32_t num_counter);
76 
77 /* Worker thread context */
78 typedef 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 
86 typedef 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 */
95 struct 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 
134 typedef struct {
135  const char *name;
136  test_fn_t test_fn;
137  validate_fn_t validate_fn;
138 } test_case_t;
139 
140 static test_global_t *test_global;
141 
142 static 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])++;
151  odp_spinlock_unlock(lock);
152  }
153 }
154 
155 static 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 
170 static 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++) {
176  odp_rwlock_write_lock(lock);
177  for (uint32_t j = 0; j < num_counter; j++)
178  (*counter[j])++;
180  odp_rwlock_read_lock(lock);
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 
191 static 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 
217 static 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++) {
223  odp_ticketlock_lock(lock);
224  for (uint32_t j = 0; j < num_counter; j++)
225  (*counter[j])++;
226  odp_ticketlock_unlock(lock);
227  }
228 }
229 
230 static 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 
247 static 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 
278 static 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 
290 static 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 
329 static 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 
399 static 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 
441 static 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 
455 static 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 
494 static 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 
511  t1 = odp_time_local_strict();
512  c1 = odp_cpu_cycles();
513 
514  test_func(global, counter, test_options->num_counter);
515 
516  c2 = odp_cpu_cycles();
517  t2 = odp_time_local_strict();
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 
529 static 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 
565 static 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 
581 static 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 
589 ODP_STATIC_ASSERT(ODPH_ARRAY_SIZE(test_suite) < TEST_MAX_BENCH,
590  "Result array is too small to hold all the results");
591 
592 static 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 
653 int 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.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
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.
char lock
lock flag, should match odp_atomic_flag_t
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()
uint32_t schedule
Scheduler APIs, e.g., odp_schedule_xxx()
struct odp_feature_t::@148 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()