API Reference Manual 1.51.0
Loading...
Searching...
No Matches
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
32typedef 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
43typedef 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
51typedef 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
58typedef struct thread_arg_t {
59 void *global;
60 int worker_idx;
61
62} thread_arg_t;
63
64typedef 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
85test_global_t *test_global;
86
87/* 250 random numbers: values between 100 and 20000 */
88static 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
116static 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
142static 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
222static 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
252static int join_group(test_global_t *global, int worker_idx, int thr)
253{
254 odp_thrmask_t thrmask;
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
269static int create_timer_pool(test_global_t *global, odp_timer_pool_t *timer_pool)
270{
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
291static 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
323static 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;
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
501static 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
532static 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
610static 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
663static 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
707static 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
727static 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
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
759static 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
769static 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
798static 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
814static 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
883int 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) {
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.
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.
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 an 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 single shot 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 single shot 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.
uint32_t num
Number of buffers in the pool.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
odp_pool_type_t type
Pool type.
struct odp_pool_param_t::@140 tmo
Parameters for timeout pools.
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::@169 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.