API Reference Manual  1.46.0
odp_stress.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2022-2025 Nokia
3  */
4 
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdint.h>
16 #include <inttypes.h>
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <getopt.h>
20 
21 #include <odp_api.h>
22 #include <odp/helper/odph_api.h>
23 
24 #define MODE_MEMCPY 0x1
25 #define MODE_COPY_U32 0x2
26 #define MODE_SQRT_U32 0x4
27 #define MODE_SQRT_F32 0x8
28 
29 #define TMODE_SHARED_REL 0
30 #define TMODE_PRIVATE_REL 1
31 
32 typedef struct test_options_t {
33  uint32_t num_cpu;
34  uint64_t period_ns;
35  uint64_t rounds;
36  uint64_t mem_size;
37  int mode;
38  int group_mode;
39  int timer_mode;
40 
41 } test_options_t;
42 
43 typedef struct test_stat_t {
44  uint64_t rounds;
45  uint64_t tot_nsec;
46  uint64_t work_nsec;
47  uint64_t dummy_sum;
48 
49 } test_stat_t;
50 
51 typedef struct test_stat_sum_t {
52  uint64_t rounds;
53  uint64_t tot_nsec;
54  uint64_t work_nsec;
55 
56 } test_stat_sum_t;
57 
58 typedef struct thread_arg_t {
59  void *global;
60  int worker_idx;
61 
62 } thread_arg_t;
63 
64 typedef struct test_global_t {
65  test_options_t test_options;
66  odp_atomic_u32_t exit_test;
67  odp_barrier_t barrier;
68  odp_cpumask_t cpumask;
69  odp_pool_t tmo_pool;
70  uint64_t period_ticks;
71  void *worker_mem;
72  odp_timer_pool_t timer_pool;
73  odp_timer_pool_param_t timer_pool_param;
77  odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
78  test_stat_t stat[ODP_THREAD_COUNT_MAX];
79  thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
80  test_stat_sum_t stat_sum;
81  odp_atomic_u64_t tot_rounds;
82 
83 } test_global_t;
84 
85 test_global_t *test_global;
86 
87 /* 250 random numbers: values between 100 and 20000 */
88 static const uint32_t pseudo_rand[] = {
89  14917, 9914, 5313, 4092, 16041, 7757, 17247, 14804, 3255, 7675,
90  13149, 7288, 5665, 7095, 9594, 1296, 2058, 6013, 17779, 11788,
91  14855, 760, 16891, 2483, 10937, 16385, 13593, 10674, 4080, 2392,
92  12218, 11475, 6009, 5798, 7582, 8358, 4520, 14655, 10555, 6598,
93  10598, 16097, 16634, 17102, 16296, 17142, 5748, 11079, 14569, 10961,
94  16693, 17775, 19155, 14102, 16132, 19561, 8746, 4521, 8280, 355,
95  10655, 14539, 5641, 2343, 19213, 9187, 570, 15096, 780, 1711,
96  8007, 8128, 17416, 14123, 4713, 13774, 11450, 9031, 1194, 16531,
97  9349, 3496, 19130, 19458, 12412, 9168, 9508, 10607, 5952, 19375,
98  14934, 18276, 12116, 510, 14272, 10362, 4095, 6789, 1600, 18509,
99  9274, 2815, 3175, 1122, 6495, 7991, 18831, 17550, 7056, 16185,
100  18594, 19178, 10028, 1182, 13410, 16173, 3548, 8013, 6099, 2619,
101  7359, 6889, 15227, 4910, 12341, 18904, 671, 5851, 9836, 18105,
102  13624, 8138, 5751, 15590, 17415, 15330, 697, 11439, 7008, 10676,
103  9863, 17163, 10885, 5581, 8078, 4689, 9870, 18370, 19323, 8831,
104  11444, 3602, 10125, 6244, 13171, 19335, 15635, 19684, 17581, 9513,
105  8444, 13724, 5243, 9987, 19886, 5087, 17292, 16294, 19627, 14985,
106  1999, 9889, 1311, 5589, 10084, 911, 301, 2260, 15305, 8265,
107  409, 1732, 1463, 17680, 15038, 2440, 4239, 9554, 14045, 924,
108  13997, 3472, 18304, 4848, 10601, 18604, 6459, 19394, 2962, 11218,
109  5405, 9869, 133, 2512, 13440, 4350, 625, 6580, 5082, 12908,
110  11517, 8919, 354, 14216, 3190, 15515, 1277, 1028, 507, 9525,
111  10115, 811, 1268, 17587, 5192, 7240, 17371, 4902, 19908, 1027,
112  3475, 8658, 11782, 13701, 13034, 154, 4940, 12679, 14067, 2707,
113  10180, 4669, 17756, 6602, 6727, 818, 8644, 580, 16988, 19127
114 };
115 
116 static void print_usage(void)
117 {
118  printf("\n"
119  "Stress test options:\n"
120  "\n"
121  " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1\n"
122  " -p, --period_ns Timeout period in nsec. Default: 100 ms\n"
123  " -r, --rounds Number of timeout rounds. Default: 2\n"
124  " -m, --mode Test mode flags, multiple may be selected. Default: 0x1\n"
125  " 0: No stress, just wait for timeouts\n"
126  " 0x1: memcpy()\n"
127  " 0x2: Memory copy loop\n"
128  " 0x4: Integer square root\n"
129  " 0x8: Floating point square root\n"
130  " -s, --mem_size Memory size per worker in bytes. Default: 2048\n"
131  " -g, --group_mode Select schedule group mode: Default: 1\n"
132  " 0: Use GROUP_ALL group. Scheduler load balances timeout events.\n"
133  " 1: Create a group per CPU. Dedicated timeout event per CPU.\n"
134  " -t, --timer_mode Select timer mode: Default: 0\n"
135  " 0: Shared timer pool with relative timers\n"
136  " 1: Private (per worker) timer pools with relative timers. Requires\n"
137  " private schedule group mode (-g 1).\n"
138  " -h, --help This help\n"
139  "\n");
140 }
141 
142 static int parse_options(int argc, char *argv[], test_options_t *test_options)
143 {
144  int opt;
145  int ret = 0;
146 
147  static const struct option longopts[] = {
148  {"num_cpu", required_argument, NULL, 'c'},
149  {"period_ns", required_argument, NULL, 'p'},
150  {"rounds", required_argument, NULL, 'r'},
151  {"mode", required_argument, NULL, 'm'},
152  {"mem_size", required_argument, NULL, 's'},
153  {"group_mode", required_argument, NULL, 'g'},
154  {"timer_mode", required_argument, NULL, 't'},
155  {"help", no_argument, NULL, 'h'},
156  {NULL, 0, NULL, 0}
157  };
158 
159  static const char *shortopts = "+c:p:r:m:s:t:g:h";
160 
161  test_options->num_cpu = 1;
162  test_options->period_ns = 100 * ODP_TIME_MSEC_IN_NS;
163  test_options->rounds = 2;
164  test_options->mode = MODE_MEMCPY;
165  test_options->mem_size = 2048;
166  test_options->group_mode = 1;
167  test_options->timer_mode = TMODE_SHARED_REL;
168 
169  while (1) {
170  opt = getopt_long(argc, argv, shortopts, longopts, NULL);
171 
172  if (opt == -1)
173  break;
174 
175  switch (opt) {
176  case 'c':
177  test_options->num_cpu = atoi(optarg);
178  break;
179  case 'p':
180  test_options->period_ns = atoll(optarg);
181  break;
182  case 'r':
183  test_options->rounds = atoll(optarg);
184  break;
185  case 'm':
186  test_options->mode = strtoul(optarg, NULL, 0);
187  break;
188  case 's':
189  test_options->mem_size = atoll(optarg);
190  break;
191  case 'g':
192  test_options->group_mode = atoi(optarg);
193  break;
194  case 't':
195  test_options->timer_mode = atoi(optarg);
196  break;
197  case 'h':
198  /* fall through */
199  default:
200  print_usage();
201  ret = -1;
202  break;
203  }
204  }
205 
206  if (test_options->mode) {
207  if (test_options->mem_size < sizeof(uint32_t)) {
208  ODPH_ERR("Too small memory size. Minimum is %zu bytes.\n",
209  sizeof(uint32_t));
210  return -1;
211  }
212  }
213 
214  if (test_options->timer_mode == TMODE_PRIVATE_REL && test_options->group_mode == 0) {
215  ODPH_ERR("Private timer mode requires private schedule group mode\n");
216  return -1;
217  }
218 
219  return ret;
220 }
221 
222 static int set_num_cpu(test_global_t *global)
223 {
224  int ret;
225  test_options_t *test_options = &global->test_options;
226  int num_cpu = test_options->num_cpu;
227 
228  /* One thread used for the main thread */
229  if (num_cpu < 0 || num_cpu > ODP_THREAD_COUNT_MAX - 1) {
230  ODPH_ERR("Bad number of workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
231  return -1;
232  }
233 
234  ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
235 
236  if (num_cpu && ret != num_cpu) {
237  ODPH_ERR("Too many workers. Max supported %i\n.", ret);
238  return -1;
239  }
240 
241  /* Zero: all available workers */
242  if (num_cpu == 0) {
243  num_cpu = ret;
244  test_options->num_cpu = num_cpu;
245  }
246 
247  odp_barrier_init(&global->barrier, num_cpu + 1);
248 
249  return 0;
250 }
251 
252 static int join_group(test_global_t *global, int worker_idx, int thr)
253 {
254  odp_thrmask_t thrmask;
255  odp_schedule_group_t group;
256 
257  odp_thrmask_zero(&thrmask);
258  odp_thrmask_set(&thrmask, thr);
259  group = global->group[worker_idx];
260 
261  if (odp_schedule_group_join(group, &thrmask)) {
262  ODPH_ERR("Thread %i failed to join group %i\n", thr, worker_idx);
263  return -1;
264  }
265 
266  return 0;
267 }
268 
269 static int create_timer_pool(test_global_t *global, odp_timer_pool_t *timer_pool)
270 {
271  odp_timer_pool_t tp;
272 
273  *timer_pool = ODP_TIMER_POOL_INVALID;
274 
275  tp = odp_timer_pool_create("Stress timers", &global->timer_pool_param);
276  if (tp == ODP_TIMER_POOL_INVALID) {
277  ODPH_ERR("Timer pool create failed\n");
278  return -1;
279  }
280 
281  if (odp_timer_pool_start_multi(&tp, 1) != 1) {
282  ODPH_ERR("Timer pool start failed\n");
283  return -1;
284  }
285 
286  *timer_pool = tp;
287 
288  return 0;
289 }
290 
291 static int start_timer(test_global_t *global, odp_timer_pool_t timer_pool,
292  odp_timer_start_t *start_param, int worker_idx, odp_timer_t *timer_out)
293 {
294  odp_timeout_t tmo;
295  odp_timer_t timer;
296 
297  tmo = odp_timeout_alloc(global->tmo_pool);
298 
299  if (tmo == ODP_TIMEOUT_INVALID) {
300  ODPH_ERR("Timeout alloc failed (%u)\n", worker_idx);
301  return -1;
302  }
303 
304  timer = odp_timer_alloc(timer_pool, global->tmo_queue[worker_idx], NULL);
305 
306  if (timer == ODP_TIMER_INVALID) {
307  ODPH_ERR("Timer alloc failed (%u)\n", worker_idx);
308  return -1;
309  }
310 
311  *timer_out = timer;
312 
313  start_param->tmo_ev = odp_timeout_to_event(tmo);
314 
315  if (odp_timer_start(timer, start_param) != ODP_TIMER_SUCCESS) {
316  ODPH_ERR("Timer start failed (%i)\n", worker_idx);
317  return -1;
318  }
319 
320  return 0;
321 }
322 
323 static int worker_thread(void *arg)
324 {
325  int thr, timer_ret;
326  uint32_t exit_test;
327  odp_event_t ev;
328  odp_timeout_t tmo;
329  odp_timer_t timer;
330  uint64_t tot_nsec, work_sum, max_nsec, i;
331  odp_timer_start_t start_param;
332  odp_time_t t1, t2, max_time;
333  odp_time_t work_t1, work_t2;
334  odp_timer_pool_t priv_timer_pool = ODP_TIMER_POOL_INVALID;
335  odp_timer_t priv_timer = ODP_TIMER_INVALID;
336  uint8_t *src = NULL, *dst = NULL;
337  uint32_t *src_u32 = NULL, *dst_u32 = NULL;
338  thread_arg_t *thread_arg = arg;
339  int worker_idx = thread_arg->worker_idx;
340  test_global_t *global = thread_arg->global;
341  test_options_t *test_options = &global->test_options;
342  const int group_mode = test_options->group_mode;
343  const int mode = test_options->mode;
344  const int data_mode = mode & (MODE_SQRT_U32 | MODE_SQRT_F32);
345  const uint64_t mem_size = test_options->mem_size;
346  const uint64_t copy_size = mem_size / 2;
347  const uint64_t num_words = mem_size / sizeof(uint32_t);
348  const uint64_t copy_words = num_words / 2;
349  uint64_t rounds = 0;
350  uint64_t dummy_sum = 0;
351  uint32_t done = 0;
352  uint64_t wait = ODP_SCHED_WAIT;
353  uint64_t tot_rounds = test_options->rounds * test_options->num_cpu;
354 
355  thr = odp_thread_id();
356  max_nsec = 2 * test_options->rounds * test_options->period_ns;
357  max_time = odp_time_local_from_ns(max_nsec);
358  printf("Thread %i starting on CPU %i\n", thr, odp_cpu_id());
359 
360  if (group_mode == 0) {
361  /* Timeout events are load balanced. Using this
362  * period to poll exit status. */
364  } else {
365  if (join_group(global, worker_idx, thr)) {
366  /* Join failed, exit after barrier */
367  wait = ODP_SCHED_NO_WAIT;
368  done = 1;
369  }
370  }
371 
372  if (mode) {
373  src = (uint8_t *)global->worker_mem + worker_idx * mem_size;
374  dst = src + copy_size;
375  src_u32 = (uint32_t *)(uintptr_t)src;
376  dst_u32 = (uint32_t *)(uintptr_t)dst;
377  }
378 
379  start_param.tick_type = ODP_TIMER_TICK_REL;
380  start_param.tick = global->period_ticks;
381 
382  if (test_options->timer_mode == TMODE_PRIVATE_REL) {
383  int ret;
384 
385  ret = create_timer_pool(global, &priv_timer_pool);
386 
387  if (ret == 0) {
388  start_param.tick = odp_timer_ns_to_tick(priv_timer_pool,
389  test_options->period_ns);
390  ret = start_timer(global, priv_timer_pool, &start_param, worker_idx,
391  &priv_timer);
392  }
393 
394  if (ret) {
395  /* Timer create or start failed, exit after barrier */
396  wait = ODP_SCHED_NO_WAIT;
397  done = 1;
398  }
399  }
400 
401  /* Start all workers at the same time */
402  odp_barrier_wait(&global->barrier);
403 
404  work_sum = 0;
405  t1 = odp_time_local();
406  max_time = odp_time_sum(t1, max_time);
407 
408  while (1) {
409  ev = odp_schedule(NULL, wait);
410 
411  exit_test = odp_atomic_load_u32(&global->exit_test);
412  exit_test += done;
413 
414  if (ev == ODP_EVENT_INVALID) {
415  odp_time_t cur_time = odp_time_local();
416 
417  if (odp_time_cmp(cur_time, max_time) > 0)
418  exit_test += 1;
419 
420  if (exit_test) {
421  /* Exit loop without schedule context */
422  break;
423  }
424 
425  continue;
426  }
427 
428  rounds++;
429 
430  if (group_mode) {
431  if (rounds >= test_options->rounds)
432  done = 1;
433  } else {
434  if (odp_atomic_fetch_inc_u64(&global->tot_rounds) >= (tot_rounds - 1))
435  done = 1;
436  }
437 
438  if (done == 0) {
439  tmo = odp_timeout_from_event(ev);
440  timer = odp_timeout_timer(tmo);
441  start_param.tmo_ev = ev;
442 
443  timer_ret = odp_timer_start(timer, &start_param);
444 
445  if (timer_ret != ODP_TIMER_SUCCESS) {
446  ODPH_ERR("Timer start failed (%" PRIu64 ")\n", rounds);
447  done = 1;
448  }
449  }
450 
451  /* Do work */
452  if (mode) {
453  work_t1 = odp_time_local();
454 
455  if (mode & MODE_MEMCPY)
456  memcpy(dst, src, copy_size);
457 
458  if (mode & MODE_COPY_U32)
459  for (i = 0; i < copy_words; i++)
460  dst_u32[i] = src_u32[i];
461 
462  if (data_mode) {
463  for (i = 0; i < num_words; i++) {
464  if (mode & MODE_SQRT_U32)
465  dummy_sum += odph_stress_sqrt_u32(src_u32[i]);
466 
467  if (mode & MODE_SQRT_F32)
468  dummy_sum += odph_stress_sqrt_f32(src_u32[i]);
469  }
470  }
471 
472  work_t2 = odp_time_local();
473  work_sum += odp_time_diff_ns(work_t2, work_t1);
474  }
475 
476  if (done) {
477  /* Stop timer and do not wait events */
478  wait = ODP_SCHED_NO_WAIT;
479  odp_event_free(ev);
480  }
481  }
482 
483  t2 = odp_time_local();
484  tot_nsec = odp_time_diff_ns(t2, t1);
485 
486  /* Update stats*/
487  global->stat[thr].rounds = rounds;
488  global->stat[thr].tot_nsec = tot_nsec;
489  global->stat[thr].work_nsec = work_sum;
490  global->stat[thr].dummy_sum = dummy_sum;
491 
492  if (priv_timer != ODP_TIMER_INVALID)
493  odp_timer_free(priv_timer);
494 
495  if (priv_timer_pool != ODP_TIMER_POOL_INVALID)
496  odp_timer_pool_destroy(priv_timer_pool);
497 
498  return 0;
499 }
500 
501 static int start_workers(test_global_t *global, odp_instance_t instance)
502 {
503  odph_thread_common_param_t thr_common;
504  int i, ret;
505  test_options_t *test_options = &global->test_options;
506  int num_cpu = test_options->num_cpu;
507  odph_thread_param_t thr_param[num_cpu];
508 
509  memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
510  odph_thread_common_param_init(&thr_common);
511 
512  thr_common.instance = instance;
513  thr_common.cpumask = &global->cpumask;
514 
515  for (i = 0; i < num_cpu; i++) {
516  odph_thread_param_init(&thr_param[i]);
517  thr_param[i].start = worker_thread;
518  thr_param[i].arg = &global->thread_arg[i];
519  thr_param[i].thr_type = ODP_THREAD_WORKER;
520  }
521 
522  ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param, num_cpu);
523 
524  if (ret != num_cpu) {
525  ODPH_ERR("Thread create failed %i\n", ret);
526  return -1;
527  }
528 
529  return 0;
530 }
531 
532 static int create_timeout_pool(test_global_t *global)
533 {
534  odp_timer_capability_t timer_capa;
535  odp_timer_res_capability_t timer_res_capa;
536  odp_pool_param_t pool_param;
537  odp_pool_t pool;
538  double duration;
539  odp_timer_pool_param_t *timer_pool_param = &global->timer_pool_param;
540  test_options_t *test_options = &global->test_options;
541  uint32_t num_cpu = test_options->num_cpu;
542  uint64_t period_ns = test_options->period_ns;
543  uint64_t res_ns = period_ns / 1000;
544  uint32_t num_tp = 1;
545 
546  if (test_options->timer_mode == TMODE_PRIVATE_REL)
547  num_tp = num_cpu;
548 
549  if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
550  ODPH_ERR("Timer capability failed\n");
551  return -1;
552  }
553 
554  if (timer_capa.queue_type_sched == 0) {
555  ODPH_ERR("Timer does not support sched queues\n");
556  return -1;
557  }
558 
559  if (timer_capa.max_pools < num_tp) {
560  ODPH_ERR("Too many timer pools requested %u (max %u)\n", num_tp,
561  timer_capa.max_pools);
562  return -1;
563  }
564 
565  memset(&timer_res_capa, 0, sizeof(odp_timer_res_capability_t));
566  timer_res_capa.max_tmo = 2 * period_ns;
567  if (odp_timer_res_capability(ODP_CLOCK_DEFAULT, &timer_res_capa)) {
568  ODPH_ERR("Timer resolution capability failed. Too long period.\n");
569  return -1;
570  }
571 
572  if (res_ns < timer_res_capa.res_ns)
573  res_ns = timer_res_capa.res_ns;
574 
575  duration = test_options->rounds * (double)period_ns / ODP_TIME_SEC_IN_NS;
576 
577  printf(" num timers %u\n", num_cpu);
578  printf(" resolution %" PRIu64 " nsec\n", res_ns);
579  printf(" period %" PRIu64 " nsec\n", period_ns);
580  printf(" test duration %.2f sec\n", duration);
581  if (test_options->group_mode == 0)
582  printf(" force stop after %.2f sec\n", 2 * duration);
583  printf("\n");
584 
585  odp_pool_param_init(&pool_param);
586  pool_param.type = ODP_POOL_TIMEOUT;
587  pool_param.tmo.num = 4 * num_cpu; /* extra for stop events */
588  pool_param.tmo.cache_size = 4;
589 
590  pool = odp_pool_create("Timeout pool", &pool_param);
591  global->tmo_pool = pool;
592  if (pool == ODP_POOL_INVALID) {
593  ODPH_ERR("Pool create failed\n");
594  return -1;
595  }
596 
597  odp_timer_pool_param_init(timer_pool_param);
598  timer_pool_param->res_ns = res_ns;
599  timer_pool_param->min_tmo = period_ns / 2;
600  timer_pool_param->max_tmo = 2 * period_ns;
601  timer_pool_param->num_timers = 2 * num_cpu;
602  timer_pool_param->clk_src = ODP_CLOCK_DEFAULT;
603 
604  if (test_options->timer_mode == TMODE_PRIVATE_REL)
605  timer_pool_param->priv = 1;
606 
607  return 0;
608 }
609 
610 static int create_queues(test_global_t *global)
611 {
612  odp_schedule_capability_t sched_capa;
613  odp_thrmask_t thrmask;
614  odp_queue_param_t queue_param;
615  uint32_t i;
616  test_options_t *test_options = &global->test_options;
617  uint32_t num_cpu = test_options->num_cpu;
618 
619  if (odp_schedule_capability(&sched_capa)) {
620  ODPH_ERR("Schedule capability failed\n");
621  return -1;
622  }
623 
624  if (test_options->group_mode) {
625  if ((sched_capa.max_groups - 1) < num_cpu) {
626  ODPH_ERR("Too many workers. Not enough schedule groups.\n");
627  return -1;
628  }
629 
630  odp_thrmask_zero(&thrmask);
631 
632  /* A group per worker thread */
633  for (i = 0; i < num_cpu; i++) {
634  global->group[i] = odp_schedule_group_create(NULL, &thrmask);
635 
636  if (global->group[i] == ODP_SCHED_GROUP_INVALID) {
637  ODPH_ERR("Schedule group create failed (%u)\n", i);
638  return -1;
639  }
640  }
641  }
642 
643  odp_queue_param_init(&queue_param);
644  queue_param.type = ODP_QUEUE_TYPE_SCHED;
645  queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
646  queue_param.sched.group = ODP_SCHED_GROUP_ALL;
647 
648  for (i = 0; i < num_cpu; i++) {
649  if (test_options->group_mode)
650  queue_param.sched.group = global->group[i];
651 
652  global->tmo_queue[i] = odp_queue_create(NULL, &queue_param);
653 
654  if (global->tmo_queue[i] == ODP_QUEUE_INVALID) {
655  ODPH_ERR("Timeout dest queue create failed (%u)\n", i);
656  return -1;
657  }
658  }
659 
660  return 0;
661 }
662 
663 static int start_shared_timers(test_global_t *global)
664 {
665  odp_timer_start_t start_param;
666  uint32_t i;
667  test_options_t *test_options = &global->test_options;
668  uint32_t num_cpu = test_options->num_cpu;
669  odp_timeout_t tmo[num_cpu];
670  odp_timer_t timer[num_cpu];
671 
672  for (i = 0; i < num_cpu; i++) {
673  tmo[i] = odp_timeout_alloc(global->tmo_pool);
674 
675  if (tmo[i] == ODP_TIMEOUT_INVALID) {
676  ODPH_ERR("Timeout alloc failed (%u)\n", i);
677  return -1;
678  }
679  }
680 
681  for (i = 0; i < num_cpu; i++) {
682  timer[i] = odp_timer_alloc(global->timer_pool, global->tmo_queue[i], NULL);
683 
684  if (timer[i] == ODP_TIMER_INVALID) {
685  ODPH_ERR("Timer alloc failed (%u)\n", i);
686  return -1;
687  }
688 
689  global->timer[i] = timer[i];
690  }
691 
692  start_param.tick_type = ODP_TIMER_TICK_REL;
693  start_param.tick = global->period_ticks;
694 
695  for (i = 0; i < num_cpu; i++) {
696  start_param.tmo_ev = odp_timeout_to_event(tmo[i]);
697 
698  if (odp_timer_start(timer[i], &start_param) != ODP_TIMER_SUCCESS) {
699  ODPH_ERR("Timer start failed (%u)\n", i);
700  return -1;
701  }
702  }
703 
704  return 0;
705 }
706 
707 static void destroy_timers(test_global_t *global)
708 {
709  uint32_t i;
710  test_options_t *test_options = &global->test_options;
711  uint32_t num_cpu = test_options->num_cpu;
712 
713  for (i = 0; i < num_cpu; i++) {
714  odp_timer_t timer = global->timer[i];
715 
716  if (timer == ODP_TIMER_INVALID)
717  continue;
718 
719  if (odp_timer_free(timer))
720  ODPH_ERR("Timer free failed (%u)\n", i);
721  }
722 
723  if (global->timer_pool != ODP_TIMER_POOL_INVALID)
724  odp_timer_pool_destroy(global->timer_pool);
725 }
726 
727 static void destroy_queues(test_global_t *global)
728 {
729  uint32_t i;
730  test_options_t *test_options = &global->test_options;
731  uint32_t num_cpu = test_options->num_cpu;
732 
733  for (i = 0; i < num_cpu; i++) {
734  odp_queue_t queue = global->tmo_queue[i];
735 
736  if (queue == ODP_QUEUE_INVALID)
737  continue;
738 
739  if (odp_queue_destroy(queue))
740  ODPH_ERR("Queue destroy failed (%u)\n", i);
741  }
742 
743  if (test_options->group_mode) {
744  for (i = 0; i < num_cpu; i++) {
745  odp_schedule_group_t group = global->group[i];
746 
747  if (group == ODP_SCHED_GROUP_INVALID)
748  continue;
749 
750  if (odp_schedule_group_destroy(group))
751  ODPH_ERR("Schedule group destroy failed (%u)\n", i);
752  }
753  }
754 
755  if (global->tmo_pool != ODP_POOL_INVALID)
756  odp_pool_destroy(global->tmo_pool);
757 }
758 
759 static void sig_handler(int signo)
760 {
761  (void)signo;
762 
763  if (test_global == NULL)
764  return;
765 
766  odp_atomic_add_u32(&test_global->exit_test, 1);
767 }
768 
769 static void stop_workers(test_global_t *global)
770 {
771  uint32_t i;
772  odp_timeout_t tmo;
773  odp_event_t ev;
774  odp_queue_t queue;
775  test_options_t *test_options = &global->test_options;
776  uint32_t num_cpu = test_options->num_cpu;
777 
778  odp_atomic_add_u32(&test_global->exit_test, 1);
779 
780  for (i = 0; i < num_cpu; i++) {
781  queue = global->tmo_queue[i];
782  if (queue == ODP_QUEUE_INVALID)
783  continue;
784 
785  tmo = odp_timeout_alloc(global->tmo_pool);
786 
787  if (tmo == ODP_TIMEOUT_INVALID)
788  continue;
789 
790  ev = odp_timeout_to_event(tmo);
791  if (odp_queue_enq(queue, ev)) {
792  ODPH_ERR("Enqueue failed %u\n", i);
793  odp_event_free(ev);
794  }
795  }
796 }
797 
798 static void sum_stat(test_global_t *global)
799 {
800  uint32_t i;
801  test_options_t *test_options = &global->test_options;
802  uint32_t num_cpu = test_options->num_cpu;
803  test_stat_sum_t *sum = &global->stat_sum;
804 
805  memset(sum, 0, sizeof(test_stat_sum_t));
806 
807  for (i = 1; i < num_cpu + 1 ; i++) {
808  sum->rounds += global->stat[i].rounds;
809  sum->tot_nsec += global->stat[i].tot_nsec;
810  sum->work_nsec += global->stat[i].work_nsec;
811  }
812 }
813 
814 static void print_stat(test_global_t *global)
815 {
816  uint32_t i;
817  test_options_t *test_options = &global->test_options;
818  uint32_t num_cpu = test_options->num_cpu;
819  int mode = test_options->mode;
820  test_stat_sum_t *sum = &global->stat_sum;
821  double sec_ave, work_ave, perc;
822  double round_ave = 0.0;
823  double rate_ave = 0.0;
824  double rate_tot = 0.0;
825  double cpu_load = 0.0;
826  const double mega = 1000000.0;
827  const double giga = 1000000000.0;
828  uint32_t num = 0;
829 
830  if (num_cpu == 0)
831  return;
832 
833  sec_ave = (sum->tot_nsec / giga) / num_cpu;
834  work_ave = (sum->work_nsec / giga) / num_cpu;
835 
836  printf("\n");
837  printf("CPU load from work (percent) per thread:\n");
838  printf("----------------------------------------------\n");
839  printf(" 1 2 3 4 5 6 7 8 9 10");
840 
841  for (i = 1; i < num_cpu + 1; i++) {
842  if (global->stat[i].tot_nsec == 0)
843  continue;
844 
845  if ((num % 10) == 0)
846  printf("\n ");
847 
848  perc = 100.0 * ((double)global->stat[i].work_nsec) / global->stat[i].tot_nsec;
849 
850  printf("%6.2f ", perc);
851  num++;
852  }
853 
854  if (sec_ave > 0.0) {
855  round_ave = (double)sum->rounds / num_cpu;
856  cpu_load = 100.0 * (work_ave / sec_ave);
857 
858  if (mode) {
859  uint64_t data_bytes;
860 
861  if (mode == MODE_MEMCPY || mode == MODE_COPY_U32 ||
862  mode == (MODE_COPY_U32 | MODE_MEMCPY))
863  data_bytes = sum->rounds * test_options->mem_size / 2;
864  else
865  data_bytes = sum->rounds * test_options->mem_size;
866 
867  rate_ave = data_bytes / (sum->work_nsec / giga);
868  rate_tot = rate_ave * num_cpu;
869  }
870  }
871 
872  printf("\n\n");
873  printf("TOTAL (%i workers)\n", num_cpu);
874  printf(" ave time: %.2f sec\n", sec_ave);
875  printf(" ave work: %.2f sec\n", work_ave);
876  printf(" ave CPU load: %.2f\n", cpu_load);
877  printf(" ave rounds per sec: %.2f\n", round_ave / sec_ave);
878  printf(" ave data rate: %.2f MB/sec\n", rate_ave / mega);
879  printf(" total data rate: %.2f MB/sec\n", rate_tot / mega);
880  printf("\n");
881 }
882 
883 int main(int argc, char **argv)
884 {
885  odph_helper_options_t helper_options;
886  odp_instance_t instance;
887  odp_init_t init;
888  odp_shm_t shm, shm_global;
889  odp_schedule_config_t sched_config;
890  test_global_t *global;
891  test_options_t *test_options;
892  int i, mode;
893  uint32_t num_cpu;
894  uint64_t mem_size;
895  odp_shm_t shm_work = ODP_SHM_INVALID;
896  int shared_timers = 1;
897 
898  signal(SIGINT, sig_handler);
899 
900  /* Let helper collect its own arguments (e.g. --odph_proc) */
901  argc = odph_parse_options(argc, argv);
902  if (odph_options(&helper_options)) {
903  ODPH_ERR("Reading ODP helper options failed.\n");
904  exit(EXIT_FAILURE);
905  }
906 
907  odp_init_param_init(&init);
908  init.mem_model = helper_options.mem_model;
909 
910  if (odp_init_global(&instance, &init, NULL)) {
911  ODPH_ERR("Global init failed.\n");
912  exit(EXIT_FAILURE);
913  }
914 
915  if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
916  ODPH_ERR("Local init failed.\n");
917  exit(EXIT_FAILURE);
918  }
919 
920  shm = odp_shm_reserve("Stress global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
921  shm_global = shm;
922  if (shm == ODP_SHM_INVALID) {
923  ODPH_ERR("SHM reserve failed.\n");
924  exit(EXIT_FAILURE);
925  }
926 
927  global = odp_shm_addr(shm);
928  if (global == NULL) {
929  ODPH_ERR("SHM addr failed\n");
930  exit(EXIT_FAILURE);
931  }
932  test_global = global;
933 
934  memset(global, 0, sizeof(test_global_t));
935  odp_atomic_init_u32(&global->exit_test, 0);
936  odp_atomic_init_u64(&global->tot_rounds, 0);
937 
938  global->timer_pool = ODP_TIMER_POOL_INVALID;
939  global->tmo_pool = ODP_POOL_INVALID;
940 
941  for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
942  global->timer[i] = ODP_TIMER_INVALID;
943  global->tmo_queue[i] = ODP_QUEUE_INVALID;
944  global->group[i] = ODP_SCHED_GROUP_INVALID;
945 
946  global->thread_arg[i].global = global;
947  global->thread_arg[i].worker_idx = i;
948  }
949 
950  if (parse_options(argc, argv, &global->test_options))
951  exit(EXIT_FAILURE);
952 
953  test_options = &global->test_options;
954  mode = test_options->mode;
955 
957 
958  odp_schedule_config_init(&sched_config);
959  sched_config.sched_group.all = 1;
960  sched_config.sched_group.control = 0;
961  sched_config.sched_group.worker = 0;
962 
963  odp_schedule_config(&sched_config);
964 
965  if (set_num_cpu(global))
966  exit(EXIT_FAILURE);
967 
968  num_cpu = test_options->num_cpu;
969 
970  /* Memory for workers */
971  if (mode) {
972  uint64_t num_words;
973  uint32_t *word;
974  uint32_t num_rand = ODPH_ARRAY_SIZE(pseudo_rand);
975 
976  mem_size = test_options->mem_size * num_cpu;
977 
978  shm = odp_shm_reserve("Test memory", mem_size, ODP_CACHE_LINE_SIZE, 0);
979  shm_work = shm;
980  if (shm == ODP_SHM_INVALID) {
981  ODPH_ERR("SHM reserve failed.\n");
982  exit(EXIT_FAILURE);
983  }
984 
985  global->worker_mem = odp_shm_addr(shm);
986  if (global->worker_mem == NULL) {
987  ODPH_ERR("SHM addr failed\n");
988  exit(EXIT_FAILURE);
989  }
990 
991  num_words = mem_size / sizeof(uint32_t);
992  word = (uint32_t *)global->worker_mem;
993 
994  for (uint64_t j = 0; j < num_words; j++)
995  word[j] = pseudo_rand[j % num_rand];
996 
997  }
998 
999  printf("\n");
1000  printf("Test parameters\n");
1001  printf(" num workers %u\n", num_cpu);
1002  printf(" mode 0x%x\n", mode);
1003  printf(" group mode %i\n", test_options->group_mode);
1004  printf(" timer mode %i\n", test_options->timer_mode);
1005  printf(" mem size per worker %" PRIu64 " bytes\n", test_options->mem_size);
1006 
1007  if (test_options->timer_mode != TMODE_SHARED_REL)
1008  shared_timers = 0;
1009 
1010  if (create_timeout_pool(global))
1011  exit(EXIT_FAILURE);
1012 
1013  if (shared_timers) {
1014  odp_timer_pool_t tp;
1015 
1016  /* Create shared timer pool */
1017  if (create_timer_pool(global, &tp))
1018  exit(EXIT_FAILURE);
1019 
1020  global->timer_pool = tp;
1021  global->period_ticks = odp_timer_ns_to_tick(tp, test_options->period_ns);
1022  }
1023 
1024  if (create_queues(global))
1025  exit(EXIT_FAILURE);
1026 
1027  /* Start worker threads */
1028  start_workers(global, instance);
1029 
1030  /* Wait until all workers are ready */
1031  odp_barrier_wait(&global->barrier);
1032 
1033  if (shared_timers) {
1034  if (start_shared_timers(global)) {
1035  /* Stop all workers, if some timer did not start */
1036  ODPH_ERR("Timers did not start. Stopping workers.\n");
1037  stop_workers(global);
1038  }
1039  }
1040 
1041  /* Wait workers to exit */
1042  odph_thread_join(global->thread_tbl, num_cpu);
1043 
1044  sum_stat(global);
1045 
1046  print_stat(global);
1047 
1048  if (shared_timers)
1049  destroy_timers(global);
1050 
1051  destroy_queues(global);
1052 
1053  if (mode) {
1054  if (odp_shm_free(shm_work)) {
1055  ODPH_ERR("SHM free failed.\n");
1056  exit(EXIT_FAILURE);
1057  }
1058  }
1059 
1060  if (odp_shm_free(shm_global)) {
1061  ODPH_ERR("SHM free failed.\n");
1062  exit(EXIT_FAILURE);
1063  }
1064 
1065  if (odp_term_local()) {
1066  ODPH_ERR("Term local failed.\n");
1067  exit(EXIT_FAILURE);
1068  }
1069 
1070  if (odp_term_global(instance)) {
1071  ODPH_ERR("Term global failed.\n");
1072  exit(EXIT_FAILURE);
1073  }
1074 
1075  return 0;
1076 }
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
void odp_atomic_add_u32(odp_atomic_u32_t *atom, uint32_t val)
Add to 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.
uint64_t odp_atomic_fetch_inc_u64(odp_atomic_u64_t *atom)
Fetch and increment 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.
int odp_cpu_id(void)
CPU identifier.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
void odp_event_free(odp_event_t event)
Free event.
#define ODP_EVENT_INVALID
Invalid event.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_TIMEOUT
Timeout pool.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
int odp_queue_enq(odp_queue_t queue, odp_event_t ev)
Enqueue an event to a queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
#define ODP_SCHED_WAIT
Wait infinitely.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
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.
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_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(const char *name, const odp_thrmask_t *mask)
Schedule group create.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
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.
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.
odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2)
Time sum.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
odp_time_t odp_time_local_from_ns(uint64_t ns)
Convert nanoseconds to local time.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
int odp_time_cmp(odp_time_t t2, odp_time_t t1)
Compare two times.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num)
Start timer pools.
odp_timeout_t odp_timeout_alloc(odp_pool_t pool)
Timeout alloc.
int odp_timer_free(odp_timer_t timer)
Free a timer.
odp_timeout_t odp_timeout_from_event(odp_event_t ev)
Get timeout handle from a ODP_EVENT_TIMEOUT type event.
#define ODP_TIMER_POOL_INVALID
Invalid timer pool handle.
odp_timer_pool_t odp_timer_pool_create(const char *name, const odp_timer_pool_param_t *params)
Create a timer pool.
odp_timer_t odp_timeout_timer(odp_timeout_t tmo)
Return timer handle for the timeout.
int odp_timer_capability(odp_timer_clk_src_t clk_src, odp_timer_capability_t *capa)
Query timer capabilities per clock source.
uint64_t odp_timer_ns_to_tick(odp_timer_pool_t timer_pool, uint64_t ns)
Convert nanoseconds to timer ticks.
int odp_timer_start(odp_timer_t timer, const odp_timer_start_t *start_param)
Start a timer.
int odp_timer_res_capability(odp_timer_clk_src_t clk_src, odp_timer_res_capability_t *res_capa)
Timer resolution capability.
odp_event_t odp_timeout_to_event(odp_timeout_t tmo)
Convert timeout handle to event handle.
#define ODP_TIMEOUT_INVALID
Invalid timeout handle.
odp_timer_t odp_timer_alloc(odp_timer_pool_t timer_pool, odp_queue_t queue, const void *user_ptr)
Allocate a timer.
#define ODP_CLOCK_DEFAULT
The default clock source.
#define ODP_TIMER_INVALID
Invalid timer handle.
void odp_timer_pool_param_init(odp_timer_pool_param_t *param)
Initialize timer pool parameters.
void odp_timer_pool_destroy(odp_timer_pool_t timer_pool)
Destroy a timer pool.
@ ODP_TIMER_SUCCESS
Timer operation succeeded.
@ ODP_TIMER_TICK_REL
Relative ticks.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Pool parameters.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@127 tmo
Parameters for timeout pools.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
odp_pool_type_t type
Pool type.
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
uint32_t max_groups
Maximum number of scheduling groups.
Schedule configuration.
odp_bool_t worker
ODP_SCHED_GROUP_WORKER.
odp_bool_t control
ODP_SCHED_GROUP_CONTROL.
struct odp_schedule_config_t::@143 sched_group
Enable/disable predefined scheduling groups.
odp_bool_t all
ODP_SCHED_GROUP_ALL.
odp_schedule_group_t group
Thread group.
odp_schedule_sync_t sync
Synchronization method.
uint32_t max_pools
Maximum number of timer pools for single shot timers (per clock source)
odp_bool_t queue_type_sched
Scheduled queue destination support.
Timer pool parameters.
uint64_t res_ns
Timeout resolution in nanoseconds.
int priv
Thread private timer pool.
uint64_t min_tmo
Minimum relative timeout in nanoseconds.
uint32_t num_timers
Number of timers in the pool.
odp_timer_clk_src_t clk_src
Clock source for timers.
uint64_t max_tmo
Maximum relative timeout in nanoseconds.
Timer resolution capability.
uint64_t max_tmo
Maximum relative timeout in nanoseconds.
uint64_t res_ns
Timeout resolution in nanoseconds.
Timer start parameters.
uint64_t tick
Expiration time in ticks.
odp_event_t tmo_ev
Timeout event.
odp_timer_tick_type_t tick_type
Tick type.