API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_timer_stress.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2025-2026 Nokia
3 */
4
13#ifndef _GNU_SOURCE
14#define _GNU_SOURCE
15#endif
16
17#include <inttypes.h>
18#include <signal.h>
19#include <stdint.h>
20#include <stdio.h>
21#include <unistd.h>
22
23#include <odp_api.h>
24#include <odp/helper/odph_api.h>
25
26#define PROG_NAME "odp_timer_stress"
27
28#define MAX_WORKERS ((uint32_t)(ODP_THREAD_COUNT_MAX - 1))
29#define MULTIPLIER 50U
30#define FMT_RES "4"
31#define MAX_RETRY 10U
32#define WAIT_MULTIPLIER 100U
33
34#define GIGAS 1000000000U
35#define MEGAS 1000000U
36#define KILOS 1000U
37
38enum {
39 SINGLE_SHOT,
40 PERIODIC,
41 CANCEL
42};
43
44enum {
45 SHARED_TMR,
46 PRIV_TMR
47};
48
49#define DEF_MODE SINGLE_SHOT
50#define DEF_CLK_SRC ODP_CLOCK_DEFAULT
51#define DEF_RES 1000000U
52#define DEF_TIMERS 50U
53#define DEF_POLICY SHARED_TMR
54#define DEF_TIME 2U
55#define DEF_WORKERS 1U
56
57typedef enum {
58 PRS_OK,
59 PRS_NOK,
60 PRS_TERM
61} parse_result_t;
62
63typedef struct {
64 uint64_t num_tmo;
65 uint64_t num_retry;
66 uint64_t num_miss;
67 uint64_t tot_tm;
68 uint64_t max_mul;
69} stats_t;
70
71typedef struct prog_config_s prog_config_t;
72
73typedef struct {
74 odp_timer_t tmr;
75 odp_timeout_t tmo;
76 odp_bool_t is_running;
77} tmr_hdls_t;
78
79typedef struct ODP_ALIGNED_CACHE {
80 stats_t stats;
81
82 struct {
85 } scd;
86
87 tmr_hdls_t *tmrs;
88 prog_config_t *prog_config;
89 uint32_t num_boot_tmr;
90} worker_config_t;
91
92typedef struct {
93 uint32_t mode;
94 odp_timer_clk_src_t clk_src;
95 uint64_t res_ns;
96 uint32_t num_tmr;
97 uint32_t policy;
98 uint32_t time_sec;
99 uint32_t num_workers;
100} opts_t;
101
102typedef struct prog_config_s {
103 odph_thread_t thread_tbl[MAX_WORKERS];
104 worker_config_t worker_config[MAX_WORKERS];
106 odp_cpumask_t worker_mask;
107 odp_barrier_t init_barrier;
108 odp_barrier_t term_barrier;
109 odp_atomic_u32_t is_running;
110 opts_t def_opts;
111 opts_t opts;
114 odp_pool_t tmo_pool;
115 odp_timer_pool_t tmr_pool;
116 odp_spinlock_t lock;
117 odp_shm_t tmrs_shm;
118} prog_config_t;
119
120static prog_config_t *prog_conf;
121
122static void terminate(int signal ODP_UNUSED)
123{
124 odp_atomic_store_u32(&prog_conf->is_running, 0U);
125}
126
127static void init_config(prog_config_t *config)
128{
129 opts_t opts;
131 worker_config_t *worker;
132
133 opts.mode = DEF_MODE;
134 opts.clk_src = DEF_CLK_SRC;
135 opts.res_ns = DEF_RES;
136 opts.num_tmr = DEF_TIMERS;
137 opts.policy = DEF_POLICY;
138 opts.time_sec = DEF_TIME;
139 opts.num_workers = DEF_WORKERS;
140
141 if (odp_timer_capability(opts.clk_src, &capa) == 0) {
142 if (opts.res_ns < capa.highest_res_ns)
143 opts.res_ns = capa.highest_res_ns;
144
145 if (opts.mode == SINGLE_SHOT || opts.mode == CANCEL) {
146 if (capa.max_timers > 0U && opts.num_tmr > capa.max_timers)
147 opts.num_tmr = capa.max_timers;
148 } else if (capa.max_pools > 0U && opts.num_tmr > capa.periodic.max_timers) {
149 opts.num_tmr = capa.periodic.max_timers;
150 }
151 }
152
153 for (uint32_t i = 0U; i < MAX_WORKERS; ++i) {
154 worker = &config->worker_config[i];
155 worker->scd.grp = ODP_SCHED_GROUP_INVALID;
156 worker->scd.q = ODP_QUEUE_INVALID;
157 }
158
159 config->def_opts = opts;
160 config->opts = config->def_opts;
161 config->tmo_pool = ODP_POOL_INVALID;
162 config->tmr_pool = ODP_TIMER_POOL_INVALID;
163 odp_spinlock_init(&config->lock);
164 config->tmrs_shm = ODP_SHM_INVALID;
165}
166
167static void print_usage(const opts_t *opts)
168{
169 printf("\n"
170 "Stress test for benchmarking timer related handling performance in different\n"
171 "scenarios.\n"
172 "\n"
173 "Usage: " PROG_NAME " [OPTIONS]\n");
174 printf("\n"
175 " E.g. " PROG_NAME " -m 0 -n 20\n"
176 " " PROG_NAME " -m 2 -p 1 -t 5 -c 4\n");
177 printf("\n"
178 "Optional OPTIONS:\n"
179 "\n"
180 " -m, --mode Timer mode. %u by default. Modes:\n"
181 " 0: single shot\n"
182 " 1: periodic\n"
183 " 2: single shot with cancel\n"
184 " -s, --clock_source Clock source. Use 'odp_timer_clk_src_t' enumeration values.\n"
185 " %u by default.\n"
186 " -r, --resolution Timer resolution in nanoseconds. %" PRIu64 " by default.\n"
187 " -n, --num_timer Number of timers. %u by default.\n"
188 " -p, --policy Timer sharing policy. %u by default. Policies:\n"
189 " 0: Timers shared by workers\n"
190 " 1: Private timers per worker\n"
191 " -t, --time_sec Time in seconds to run. 0 means infinite. %u by default.\n"
192 " -c, --worker_count Number of workers. %u by default.\n"
193 " -h, --help This help.\n"
194 "\n", opts->mode, opts->clk_src, opts->res_ns, opts->num_tmr, opts->policy,
195 opts->time_sec, opts->num_workers);
196}
197
198static odp_fract_u64_t calc_req_hz(uint64_t res_ns)
199{
200 odp_fract_u64_t fract;
201 const double hz = (double)ODP_TIME_SEC_IN_NS / (res_ns * MULTIPLIER);
202 double leftover;
203
204 fract.integer = (uint64_t)hz;
205 leftover = hz - fract.integer;
206 fract.numer = (uint64_t)(leftover * DEF_RES);
207 fract.denom = fract.numer == 0U ? 0U : DEF_RES;
208
209 return fract;
210}
211
212static parse_result_t check_options(prog_config_t *config)
213{
214 opts_t *opts = &config->opts;
215 odp_timer_capability_t tmr_capa;
216 int ret;
217 uint32_t req_tmr, max_workers, req_shm;
219 double hz_d, min_hz_d, max_hz_d;
220 odp_pool_capability_t pool_capa;
221 odp_shm_capability_t shm_capa;
222 uint64_t req_shm_sz;
223
224 if (opts->mode != SINGLE_SHOT && opts->mode != PERIODIC && opts->mode != CANCEL) {
225 ODPH_ERR("Invalid timer mode: %u\n", opts->mode);
226 return PRS_NOK;
227 }
228
229 if (opts->policy != SHARED_TMR && opts->policy != PRIV_TMR) {
230 ODPH_ERR("Invalid pool policy: %d\n", opts->policy);
231 return PRS_NOK;
232 }
233
234 if (opts->mode == CANCEL && opts->policy != PRIV_TMR) {
235 ODPH_ERR("Single shot with cancel mode supported only with worker-private "
236 "timers\n");
237 return PRS_NOK;
238 }
239
240 ret = odp_timer_capability(opts->clk_src, &tmr_capa);
241
242 if (ret < -1) {
243 ODPH_ERR("Error querying timer capabilities\n");
244 return PRS_NOK;
245 }
246
247 if (ret == -1) {
248 ODPH_ERR("Invalid clock source: %d\n", opts->clk_src);
249 return PRS_NOK;
250 }
251
252 if (!tmr_capa.queue_type_sched) {
253 ODPH_ERR("Invalid queue support, scheduled completion queues not supported\n");
254 return PRS_NOK;
255 }
256
257 if (opts->res_ns < tmr_capa.highest_res_ns) {
258 ODPH_ERR("Invalid resolution: %" PRIu64 " ns (max: %" PRIu64 " ns)\n",
259 opts->res_ns, tmr_capa.highest_res_ns);
260 return PRS_NOK;
261 }
262
263 if (opts->num_tmr == 0U) {
264 ODPH_ERR("Invalid number of timers: %u\n", opts->num_tmr);
265 return PRS_NOK;
266 }
267
268 max_workers = ODPH_MIN(MAX_WORKERS, (uint32_t)odp_cpumask_default_worker(NULL, 0));
269
270 if (opts->num_workers == 0U || opts->num_workers > max_workers) {
271 ODPH_ERR("Invalid worker count: %u (min: 1, max: %u)\n", opts->num_workers,
272 max_workers);
273 return PRS_NOK;
274 }
275
276 (void)odp_cpumask_default_worker(&config->worker_mask, opts->num_workers);
277
278 req_tmr = opts->num_tmr * (opts->policy == PRIV_TMR ? opts->num_workers : 1U);
279
280 if (opts->mode == SINGLE_SHOT || opts->mode == CANCEL) {
281 if (tmr_capa.max_pools == 0U) {
282 ODPH_ERR("Single shot timers not supported\n");
283 return PRS_NOK;
284 }
285
286 if (tmr_capa.max_timers > 0U && req_tmr > tmr_capa.max_timers) {
287 ODPH_ERR("Invalid number of timers: %u (max: %u)\n", req_tmr,
288 tmr_capa.max_timers);
289 return PRS_NOK;
290 }
291
292 config->res_capa.res_ns = opts->res_ns;
293
294 if (odp_timer_res_capability(opts->clk_src, &config->res_capa) < 0) {
295 ODPH_ERR("Error querying timer resolution capabilities\n");
296 return PRS_NOK;
297 }
298
299 if (opts->mode == CANCEL) {
300 if (odp_shm_capability(&shm_capa) < 0) {
301 ODPH_ERR("Error querying SHM capabilities");
302 return PRS_NOK;
303 }
304
305 /* One block for program configuration, one block divided between
306 * workers. */
307 req_shm = 2U;
308
309 if (req_shm > shm_capa.max_blocks) {
310 ODPH_ERR("Invalid SHM block count support: %u (max: %u)\n",
311 req_shm, shm_capa.max_blocks);
312 return PRS_NOK;
313 }
314
315 /* Dimensioned so that each structure will always start at a cache line
316 * boundary. */
317 req_shm_sz = ODP_CACHE_LINE_ROUNDUP(sizeof(tmr_hdls_t)) *
318 opts->num_tmr * opts->num_workers;
319
320 if (shm_capa.max_size != 0U && req_shm_sz > shm_capa.max_size) {
321 ODPH_ERR("Invalid total SHM block size: %" PRIu64 ""
322 " (max: %" PRIu64 ")\n", req_shm_sz, shm_capa.max_size);
323 return PRS_NOK;
324 }
325 }
326 } else {
327 if (tmr_capa.periodic.support.base_mul == 0U) {
328 ODPH_ERR("Periodic timers not supported "
329 "(ODP_TIMER_TYPE_PERIODIC_BASE_MUL)\n");
330 return PRS_NOK;
331 }
332
333 if (req_tmr > tmr_capa.periodic.max_timers) {
334 ODPH_ERR("Invalid number of timers: %u (max: %u)\n", req_tmr,
335 tmr_capa.periodic.max_timers);
336 return PRS_NOK;
337 }
338
339 hz = calc_req_hz(opts->res_ns);
340 hz_d = odp_fract_u64_to_dbl(&hz);
341 min_hz_d = odp_fract_u64_to_dbl(&tmr_capa.periodic.min_base_freq_hz);
342 max_hz_d = odp_fract_u64_to_dbl(&tmr_capa.periodic.max_base_freq_hz);
343
344 if (hz_d < min_hz_d || hz_d > max_hz_d) {
345 ODPH_ERR("Invalid requested resolution: %." FMT_RES "f hz "
346 "(min: %." FMT_RES "f hz, max: %." FMT_RES "f hz)\n", hz_d,
347 min_hz_d, max_hz_d);
348 return PRS_NOK;
349 }
350
351 config->per_capa.type = ODP_TIMER_TYPE_PERIODIC_BASE_MUL;
352 config->per_capa.base_mul.base_freq_hz = hz;
353 config->per_capa.base_mul.max_multiplier = MULTIPLIER;
354 config->per_capa.res_ns = opts->res_ns;
355
356 if (odp_timer_periodic_capability(opts->clk_src, &config->per_capa) < 0) {
357 ODPH_ERR("Error querying periodic timer capabilities\n");
358 return PRS_NOK;
359 }
360
361 if (config->per_capa.base_mul.max_multiplier > MULTIPLIER)
362 config->per_capa.base_mul.max_multiplier = MULTIPLIER;
363 }
364
365 if (odp_pool_capability(&pool_capa) < 0) {
366 ODPH_ERR("Error querying pool capabilities\n");
367 return PRS_NOK;
368 }
369
370 if (pool_capa.tmo.max_num > 0U && req_tmr > pool_capa.tmo.max_num) {
371 ODPH_ERR("Invalid timeout event count: %u (max: %u)\n", req_tmr,
372 pool_capa.tmo.max_num);
373 return PRS_NOK;
374 }
375
376 return PRS_OK;
377}
378
379static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
380{
381 int opt;
382 opts_t *opts = &config->opts;
383
384 static const struct option longopts[] = {
385 { "mode", required_argument, NULL, 'm' },
386 { "clock_source", required_argument, NULL, 's' },
387 { "resolution", required_argument, NULL, 'r' },
388 { "num_timer", required_argument, NULL, 'n' },
389 { "policy", required_argument, NULL, 'p' },
390 { "time_sec", required_argument, NULL, 't' },
391 { "worker_count", required_argument, NULL, 'c' },
392 { "help", no_argument, NULL, 'h' },
393 { NULL, 0, NULL, 0 }
394 };
395
396 static const char *shortopts = "m:s:r:n:p:t:c:h";
397
398 init_config(config);
399
400 while (true) {
401 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
402
403 if (opt == -1)
404 break;
405
406 switch (opt) {
407 case 'm':
408 opts->mode = atoi(optarg);
409 break;
410 case 's':
411 opts->clk_src = atoi(optarg);
412 break;
413 case 'r':
414 opts->res_ns = atoll(optarg);
415 break;
416 case 'n':
417 opts->num_tmr = atoi(optarg);
418 break;
419 case 'p':
420 opts->policy = atoi(optarg);
421 break;
422 case 't':
423 opts->time_sec = atoi(optarg);
424 break;
425 case 'c':
426 opts->num_workers = atoi(optarg);
427 break;
428 case 'h':
429 print_usage(&config->def_opts);
430 return PRS_TERM;
431 case '?':
432 default:
433 print_usage(&config->def_opts);
434 return PRS_NOK;
435 }
436 }
437
438 return check_options(config);
439}
440
441static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
442{
443 struct sigaction action = { .sa_handler = terminate };
444
445 odp_atomic_init_u32(&config->is_running, 1U);
446
447 if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
448 sigaddset(&action.sa_mask, SIGTERM) == -1 ||
449 sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
450 sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
451 ODPH_ERR("Error installing signal handler\n");
452 return PRS_NOK;
453 }
454
455 return parse_options(argc, argv, config);
456}
457
458static odp_timer_pool_t create_timer_pool(odp_timer_pool_param_t *param)
459{
460 odp_timer_pool_t pool;
461
462 pool = odp_timer_pool_create(PROG_NAME, param);
463
464 if (pool == ODP_TIMER_POOL_INVALID) {
465 ODPH_ERR("Error creating timer pool\n");
467 }
468
469 if (odp_timer_pool_start_multi(&pool, 1) != 1) {
470 ODPH_ERR("Error starting timer pool\n");
472 }
473
474 return pool;
475}
476
477static odp_bool_t setup_config(prog_config_t *config)
478{
479 opts_t *opts = &config->opts;
480 odp_bool_t is_priv = opts->policy == PRIV_TMR;
481 const uint32_t num_barrier = opts->num_workers + 1,
482 max_tmr = opts->num_tmr * (is_priv ? opts->num_workers : 1U),
483 tmr_size = ODP_CACHE_LINE_ROUNDUP(sizeof(tmr_hdls_t));
484 odp_pool_param_t tmo_param;
485 odp_timer_pool_param_t tmr_param;
486 odp_queue_param_t q_param;
487 odp_thrmask_t zero;
488 void *tmrs_addr = NULL;
489 uint32_t num_tmr_p_w = ODPH_DIV_ROUNDUP(opts->num_tmr, opts->num_workers),
490 num_tmr = opts->num_tmr;
491 worker_config_t *worker;
492
493 if (odp_schedule_config(NULL) < 0) {
494 ODPH_ERR("Error initializing scheduler\n");
495 return false;
496 }
497
498 odp_barrier_init(&config->init_barrier, num_barrier);
499 odp_barrier_init(&config->term_barrier, num_barrier);
500 odp_pool_param_init(&tmo_param);
501 tmo_param.type = ODP_POOL_TIMEOUT;
502 tmo_param.tmo.num = max_tmr;
503 tmo_param.tmo.cache_size = 0U;
504 config->tmo_pool = odp_pool_create(PROG_NAME, &tmo_param);
505
506 if (config->tmo_pool == ODP_POOL_INVALID) {
507 ODPH_ERR("Error creating timeout pool\n");
508 return false;
509 }
510
511 odp_timer_pool_param_init(&tmr_param);
512 tmr_param.clk_src = opts->clk_src;
513 tmr_param.res_ns = opts->res_ns;
514 tmr_param.num_timers = max_tmr;
515
516 if (opts->mode == SINGLE_SHOT || opts->mode == CANCEL) {
518 tmr_param.min_tmo = config->res_capa.min_tmo;
519 tmr_param.max_tmo = config->res_capa.max_tmo;
520 } else {
522 tmr_param.periodic.base_mul.base_freq_hz = config->per_capa.base_mul.base_freq_hz;
524 config->per_capa.base_mul.max_multiplier;
525 }
526
527 config->tmr_pool = create_timer_pool(&tmr_param);
528
529 if (config->tmr_pool == ODP_TIMER_POOL_INVALID)
530 return false;
531
532 odp_queue_param_init(&q_param);
533 q_param.type = ODP_QUEUE_TYPE_SCHED;
535 odp_thrmask_zero(&zero);
536 config->tmrs_shm = odp_shm_reserve(PROG_NAME, tmr_size * max_tmr, ODP_CACHE_LINE_SIZE, 0U);
537
538 if (config->tmrs_shm == ODP_SHM_INVALID) {
539 ODPH_ERR("Error reserving shared memory for timer handles\n");
540 return false;
541 }
542
543 tmrs_addr = odp_shm_addr(config->tmrs_shm);
544
545 if (tmrs_addr == NULL) {
546 ODPH_ERR("Error resolving shared memory address for timer handles\n");
547 return false;
548 }
549
550 for (uint32_t i = 0U; i < opts->num_workers; ++i) {
551 worker = &config->worker_config[i];
552 worker->num_boot_tmr = num_tmr_p_w;
553 num_tmr -= num_tmr_p_w;
554
555 if (num_tmr < num_tmr_p_w)
556 num_tmr_p_w = num_tmr;
557
558 if (is_priv) {
559 worker->num_boot_tmr = opts->num_tmr;
560 worker->scd.grp = odp_schedule_group_create(PROG_NAME, &zero);
561
562 if (worker->scd.grp == ODP_SCHED_GROUP_INVALID) {
563 ODPH_ERR("Error creating schedule group for worker %u\n", i);
564 return false;
565 }
566
567 q_param.sched.group = worker->scd.grp;
568 }
569
570 worker->tmrs = (tmr_hdls_t *)(uintptr_t)((uint8_t *)(uintptr_t)tmrs_addr + i *
571 worker->num_boot_tmr * tmr_size);
572 worker->scd.q = odp_queue_create(PROG_NAME, &q_param);
573
574 if (worker->scd.q == ODP_QUEUE_INVALID) {
575 ODPH_ERR("Error creating completion queue for worker %u\n", i);
576 return false;
577 }
578
579 worker->prog_config = config;
580 }
581
582 return true;
583}
584
585static void get_time_handles(odp_timer_pool_t tmr_pool, odp_pool_t tmo_pool, odp_queue_t q,
586 tmr_hdls_t *time)
587{
588 time->tmr = odp_timer_alloc(tmr_pool, q, time);
589
590 if (time->tmr == ODP_TIMER_INVALID)
591 /* We should have enough timers available, if still somehow there is a failure,
592 * abort. */
593 ODPH_ABORT("Error allocating timers, aborting (tmr pool: %" PRIx64 ")\n",
594 odp_timer_pool_to_u64(tmr_pool));
595
596 time->tmo = odp_timeout_alloc(tmo_pool);
597
598 if (time->tmo == ODP_TIMEOUT_INVALID)
599 /* We should have enough timeouts available, if still somehow there is a failure,
600 * abort. */
601 ODPH_ABORT("Error allocating timeouts, aborting (tmo pool: %" PRIx64 ")\n",
602 odp_pool_to_u64(tmo_pool));
603
604 time->is_running = true;
605}
606
607static inline void start_single_shot(odp_timer_pool_t tmr_pool, odp_timer_t tmr, odp_event_t tmo,
608 uint64_t res_ns, stats_t *stats)
609{
610 odp_timer_start_t start = { .tick_type = ODP_TIMER_TICK_REL, .tmo_ev = tmo };
611 uint32_t retry = MAX_RETRY, mul = 1U;
612 int ret;
613
614 while (retry) {
615 start.tick = odp_timer_ns_to_tick(tmr_pool, mul * res_ns);
616 ret = odp_timer_start(tmr, &start);
617
618 if (ret == ODP_TIMER_SUCCESS)
619 break;
620
621 --retry;
622
623 if (retry > 0U) {
624 if (ret == ODP_TIMER_BUSY) {
625 /* Resources busy, don't increment multiplier, just retry. */
626 ++stats->num_retry;
627 continue;
628 }
629
630 if (ret == ODP_TIMER_TOO_NEAR) {
631 ++mul;
632 ++stats->num_retry;
633 continue;
634 }
635 }
636
637 /* Arming the timer apparently not possible, abort. */
638 ODPH_ABORT("Error starting timers, aborting (tmr: %" PRIx64 ", tmr pool: "
639 "%" PRIx64 ")\n", odp_timer_to_u64(tmr),
640 odp_timer_pool_to_u64(tmr_pool));
641 }
642
643 stats->max_mul = mul > stats->max_mul ? mul : stats->max_mul;
644}
645
646static void boot_single_shot(worker_config_t *worker, odp_timer_pool_t tmr_pool,
647 odp_pool_t tmo_pool, uint64_t res_ns)
648{
649 tmr_hdls_t *time;
650
651 for (uint32_t i = 0U; i < worker->num_boot_tmr; ++i) {
652 time = &worker->tmrs[i];
653 get_time_handles(tmr_pool, tmo_pool, worker->scd.q, time);
654 start_single_shot(tmr_pool, time->tmr, odp_timeout_to_event(time->tmo), res_ns,
655 &worker->stats);
656 }
657}
658
659static int process_single_shot(void *args)
660{
661 worker_config_t *worker = args;
662 odp_thrmask_t mask;
663 prog_config_t *config = worker->prog_config;
664 odp_timer_pool_t tmr_pool = config->tmr_pool;
665 const uint64_t res_ns = prog_conf->opts.res_ns;
666 odp_time_t tm;
667 odp_atomic_u32_t *is_running = &config->is_running;
668 odp_event_t ev;
669 odp_timer_t tmr;
670 stats_t *stats = &worker->stats;
671
672 if (worker->scd.grp != ODP_SCHED_GROUP_INVALID) {
673 odp_thrmask_zero(&mask);
675
676 if (odp_schedule_group_join(worker->scd.grp, &mask) < 0)
677 ODPH_ABORT("Error joining scheduler group, aborting (group: %" PRIu64 ")"
678 "\n", odp_schedule_group_to_u64(worker->scd.grp));
679 }
680
681 boot_single_shot(worker, tmr_pool, config->tmo_pool, res_ns);
682 odp_barrier_wait(&config->init_barrier);
684
685 while (odp_atomic_load_u32(is_running)) {
687
688 if (ev == ODP_EVENT_INVALID)
689 continue;
690
692 start_single_shot(tmr_pool, tmr, ev, res_ns, stats);
693 ++stats->num_tmo;
694 }
695
696 stats->tot_tm = odp_time_diff_ns(odp_time_local_strict(), tm);
697 odp_barrier_wait(&config->term_barrier);
698
699 while (true) {
700 ev = odp_schedule(NULL, odp_schedule_wait_time(stats->max_mul * res_ns *
701 WAIT_MULTIPLIER));
702
703 if (ev == ODP_EVENT_INVALID)
704 break;
705
707 odp_event_free(ev);
708 (void)odp_timer_free(tmr);
709 }
710
711 return 0;
712}
713
714static void get_periodic_time_handles(odp_timer_pool_t tmr_pool, odp_queue_t q, uint64_t mul,
715 tmr_hdls_t *time)
716{
718
720 param.queue = q;
721 param.user_ptr = time;
722 param.base_mul.multiplier = mul;
723 time->tmr = odp_timer_periodic_alloc(tmr_pool, &param);
724
725 if (time->tmr == ODP_TIMER_INVALID)
726 /* We should have enough timers available, if still somehow there is a failure,
727 * abort. */
728 ODPH_ABORT("Error allocating timers, aborting (tmr pool: %" PRIx64 ")\n",
729 odp_timer_pool_to_u64(tmr_pool));
730
731 time->is_running = true;
732}
733
734static void start_periodic(odp_timer_pool_t tmr_pool, odp_timer_t tmr, stats_t *stats)
735{
736 const odp_timer_periodic_start_t start = { .first_tick = 0U };
737 uint32_t retry = MAX_RETRY;
738 int ret;
739
740 while (retry) {
741 ret = odp_timer_periodic_start(tmr, &start);
742
743 if (ret == ODP_TIMER_SUCCESS)
744 break;
745
746 --retry;
747
748 if (retry > 0U && ret == ODP_TIMER_BUSY) {
749 ++stats->num_retry;
750 continue;
751 }
752
753 /* Arming the timer apparently not possible, abort. */
754 ODPH_ABORT("Error starting timer, aborting (tmr: %" PRIx64 ", tmr pool: "
755 "%" PRIx64 ")\n", odp_timer_to_u64(tmr),
756 odp_timer_pool_to_u64(tmr_pool));
757 }
758}
759
760static void boot_periodic(worker_config_t *worker, odp_timer_pool_t tmr_pool, uint64_t mul)
761{
762 tmr_hdls_t *time;
763
764 for (uint32_t i = 0U; i < worker->num_boot_tmr; ++i) {
765 time = &worker->tmrs[i];
766 get_periodic_time_handles(tmr_pool, worker->scd.q, mul, time);
767 start_periodic(tmr_pool, time->tmr, &worker->stats);
768 }
769}
770
771static int process_periodic(void *args)
772{
773 worker_config_t *worker = args;
774 odp_thrmask_t mask;
775 prog_config_t *config = worker->prog_config;
776 odp_time_t tm;
777 odp_atomic_u32_t *is_running = &config->is_running;
778 odp_event_t ev;
779 odp_timeout_t tmo;
780 odp_timer_t tmr;
781 stats_t *stats = &worker->stats;
782 const uint64_t res_ns = prog_conf->opts.res_ns;
783 int ret;
784 tmr_hdls_t *time;
785
786 if (worker->scd.grp != ODP_SCHED_GROUP_INVALID) {
787 odp_thrmask_zero(&mask);
789
790 if (odp_schedule_group_join(worker->scd.grp, &mask) < 0)
791 ODPH_ABORT("Error joining scheduler group, aborting (group: %" PRIu64 ")"
792 "\n", odp_schedule_group_to_u64(worker->scd.grp));
793 }
794
795 boot_periodic(worker, config->tmr_pool, config->per_capa.base_mul.max_multiplier);
796 odp_barrier_wait(&config->init_barrier);
798
799 while (odp_atomic_load_u32(is_running)) {
801
802 if (ev == ODP_EVENT_INVALID)
803 continue;
804
806
807 if (odp_unlikely(odp_timer_periodic_ack(tmr, ev) < 0))
808 ODPH_ABORT("Error acking periodic timer, aborting (tmr: %" PRIx64 ")\n",
809 odp_timer_to_u64(tmr));
810
811 ++stats->num_tmo;
812 }
813
814 stats->tot_tm = odp_time_diff_ns(odp_time_local_strict(), tm);
815 odp_barrier_wait(&config->term_barrier);
816
817 while (true) {
818 ev = odp_schedule(NULL, odp_schedule_wait_time(res_ns *
819 config->per_capa.base_mul.max_multiplier *
820 WAIT_MULTIPLIER));
821
822 if (ev == ODP_EVENT_INVALID)
823 break;
824
825 /* Many workers might be trying to cancel the timer, do it exclusively. */
826 odp_spinlock_lock(&config->lock);
827 tmo = odp_timeout_from_event(ev);
828 tmr = odp_timeout_timer(tmo);
829 time = odp_timeout_user_ptr(tmo);
830 ret = odp_timer_periodic_ack(tmr, ev);
831
832 if (ret < 0)
833 ODPH_ABORT("Error acking periodic timer, aborting (tmr: %" PRIx64 ")\n",
834 odp_timer_to_u64(tmr));
835
836 if (ret == 1) {
837 (void)odp_timer_free(tmr);
838 odp_spinlock_unlock(&config->lock);
839 continue;
840 }
841
842 if (time->is_running) {
843 time->is_running = false;
844
845 if (odp_timer_periodic_cancel(tmr) < 0)
846 ODPH_ABORT("Error cancelling periodic timer, aborting "
847 "(tmr: %" PRIx64 ")\n", odp_timer_to_u64(tmr));
848 }
849
850 odp_spinlock_unlock(&config->lock);
851 }
852
853 return 0;
854}
855
856static int process_cancel(void *args)
857{
858 worker_config_t *worker = args;
859 odp_thrmask_t mask;
860 prog_config_t *config = worker->prog_config;
861 odp_timer_pool_t tmr_pool = config->tmr_pool;
862 const uint64_t res_ns = prog_conf->opts.res_ns;
863 odp_time_t tm;
864 odp_atomic_u32_t *is_running = &config->is_running;
865 odp_event_t ev;
866 stats_t *stats = &worker->stats;
867 const uint32_t num_boot_tmr = worker->num_boot_tmr;
868 tmr_hdls_t *time;
869 int ret;
870 odp_timer_t tmr;
871
872 odp_thrmask_zero(&mask);
874
875 if (odp_schedule_group_join(worker->scd.grp, &mask) < 0)
876 ODPH_ABORT("Error joining scheduler group, aborting (group: %" PRIu64 ")\n",
877 odp_schedule_group_to_u64(worker->scd.grp));
878
879 boot_single_shot(worker, tmr_pool, config->tmo_pool, res_ns);
880 odp_barrier_wait(&config->init_barrier);
882
883 while (odp_atomic_load_u32(is_running)) {
885
886 if (odp_unlikely(ev != ODP_EVENT_INVALID)) {
887 start_single_shot(tmr_pool, odp_timeout_timer(odp_timeout_from_event(ev)),
888 ev, res_ns, stats);
889 ++stats->num_miss;
890 continue;
891 }
892
893 for (uint32_t i = 0U; i < num_boot_tmr; ++i) {
894 time = &worker->tmrs[i];
895 ret = odp_timer_cancel(time->tmr, &ev);
896
898 continue;
899
900 if (odp_unlikely(ret == ODP_TIMER_FAIL))
901 ODPH_ABORT("Error cancelling timer, aborting (tmr: %" PRIx64 ")\n",
902 odp_timer_to_u64(time->tmr));
903
904 time->is_running = false;
905 ++stats->num_tmo;
906 }
907
908 for (uint32_t i = 0U; i < num_boot_tmr; ++i) {
909 time = &worker->tmrs[i];
910
911 if (time->is_running)
912 continue;
913
914 start_single_shot(tmr_pool, time->tmr, odp_timeout_to_event(time->tmo),
915 res_ns, stats);
916 time->is_running = true;
917 }
918 }
919
920 stats->tot_tm = odp_time_diff_ns(odp_time_local_strict(), tm);
921 odp_barrier_wait(&config->term_barrier);
922
923 while (true) {
924 ev = odp_schedule(NULL, odp_schedule_wait_time(stats->max_mul * res_ns *
925 WAIT_MULTIPLIER));
926
927 if (ev == ODP_EVENT_INVALID)
928 break;
929
931 odp_event_free(ev);
932 (void)odp_timer_free(tmr);
933 }
934
935 return 0;
936}
937
938static odp_bool_t setup_workers(prog_config_t *config)
939{
940 odph_thread_common_param_t thr_common;
941 odph_thread_param_t thr_params[config->opts.num_workers], *thr_param;
942 worker_config_t *worker;
943 const uint32_t mode = config->opts.mode;
944 int (*start_fn)(void *) = mode == SINGLE_SHOT ? process_single_shot :
945 mode == PERIODIC ? process_periodic : process_cancel;
946
947 odph_thread_common_param_init(&thr_common);
948 thr_common.instance = config->odp_instance;
949 thr_common.cpumask = &config->worker_mask;
950
951 for (uint32_t i = 0; i < config->opts.num_workers; ++i) {
952 thr_param = &thr_params[i];
953 worker = &config->worker_config[i];
954 odph_thread_param_init(thr_param);
955 thr_param->start = start_fn;
956 thr_param->thr_type = ODP_THREAD_WORKER;
957 thr_param->arg = worker;
958 }
959
960 if ((uint32_t)odph_thread_create(config->thread_tbl, &thr_common, thr_params,
961 config->opts.num_workers) != config->opts.num_workers) {
962 ODPH_ERR("Error configuring worker threads\n");
963 return false;
964 }
965
966 return true;
967}
968
969static odp_bool_t setup_test(prog_config_t *config)
970{
971 return setup_config(config) && setup_workers(config);
972}
973
974static void run_control(prog_config_t *config)
975{
976 const uint32_t time_sec = config->opts.time_sec;
977 odp_atomic_u32_t *is_running = &config->is_running;
978
979 odp_barrier_wait(&config->init_barrier);
980
981 if (time_sec > 0U) {
982 sleep(time_sec);
983 odp_atomic_store_u32(is_running, 0U);
984 } else {
985 while (odp_atomic_load_u32(is_running))
986 sleep(1U);
987 }
988
989 odp_barrier_wait(&config->term_barrier);
990 (void)odph_thread_join(config->thread_tbl, config->opts.num_workers);
991}
992
993static void print_humanised(uint64_t value)
994{
995 if (value > GIGAS)
996 printf("%.2f GOPS\n", (double)value / GIGAS);
997 else if (value > MEGAS)
998 printf("%.2f MOPS\n", (double)value / MEGAS);
999 else if (value > KILOS)
1000 printf("%.2f kOPS\n", (double)value / KILOS);
1001 else
1002 printf("%" PRIu64 " OPS\n", value);
1003}
1004
1005static void print_stats(const prog_config_t *config)
1006{
1007 const stats_t *stats;
1008 const opts_t *opts = &config->opts;
1009 uint64_t tot_tmo = 0U, tot_miss = 0U, tot_retry = 0U, max_mul = 0U, rate, tot_rate = 0U;
1010
1011 printf("=====================\n\n"
1012 "" PROG_NAME " done\n\n"
1013 " mode: %s\n"
1014 " clock source: %d\n"
1015 " resolution: %" PRIu64 " ns\n"
1016 " number of timers: %u (%s)\n\n", opts->mode == SINGLE_SHOT ? "single shot" :
1017 opts->mode == PERIODIC ?
1018 "periodic" : "single shot with cancel",
1019 opts->clk_src, opts->res_ns, opts->num_tmr,
1020 opts->policy == SHARED_TMR ? "shared" : "private");
1021
1022 for (uint32_t i = 0U; i < config->opts.num_workers; ++i) {
1023 stats = &config->worker_config[i].stats;
1024 tot_tmo += stats->num_tmo;
1025 tot_miss += stats->num_miss;
1026 tot_retry += stats->num_retry;
1027 max_mul = ODPH_MAX(max_mul, stats->max_mul);
1028 rate = stats->num_tmo / ((double)stats->tot_tm / ODP_TIME_SEC_IN_NS);
1029 tot_rate += rate;
1030
1031 printf(" worker %u:\n"
1032 " %s%" PRIu64 "\n"
1033 " number of retries: %" PRIu64 "\n", i,
1034 opts->mode == SINGLE_SHOT || opts->mode == PERIODIC ?
1035 "number of timeouts: " : "number of cancels: ",
1036 stats->num_tmo, stats->num_retry);
1037
1038 if (opts->mode == SINGLE_SHOT || opts->mode == CANCEL) {
1039 if (opts->mode == CANCEL)
1040 printf(" number of misses: %" PRIu64 "\n", stats->num_miss);
1041
1042 printf(" max start mul: %" PRIu64 "\n", stats->max_mul);
1043 }
1044
1045 printf(" runtime: %" PRIu64 " ns\n"
1046 " rate: ", stats->tot_tm);
1047 print_humanised(rate);
1048 printf("\n");
1049 }
1050
1051 printf(" total:\n"
1052 " %s%" PRIu64 "\n", opts->mode == SINGLE_SHOT || opts->mode == PERIODIC ?
1053 "number of timeouts: " : "number of cancels: ",
1054 tot_tmo);
1055
1056 if (opts->mode == SINGLE_SHOT || opts->mode == CANCEL) {
1057 if (opts->mode == CANCEL)
1058 printf(" number of misses: %" PRIu64 "\n", tot_miss);
1059
1060 printf(" number of retries: %" PRIu64 "\n"
1061 " max start mul: %" PRIu64 "\n", tot_retry, max_mul);
1062 }
1063
1064 printf(" rate: ");
1065 print_humanised(tot_rate);
1066 printf("\n=====================\n");
1067}
1068
1069static void teardown(const prog_config_t *config)
1070{
1071 const opts_t *opts = &config->opts;
1072 const worker_config_t *worker;
1073
1074 for (uint32_t i = 0U; i < opts->num_workers; ++i) {
1075 worker = &config->worker_config[i];
1076
1077 if (worker->scd.q != ODP_QUEUE_INVALID)
1078 (void)odp_queue_destroy(worker->scd.q);
1079
1080 if (worker->scd.grp != ODP_SCHED_GROUP_INVALID)
1081 (void)odp_schedule_group_destroy(worker->scd.grp);
1082 }
1083
1084 if (config->tmrs_shm != ODP_SHM_INVALID)
1085 (void)odp_shm_free(config->tmrs_shm);
1086
1087 if (config->tmr_pool != ODP_TIMER_POOL_INVALID)
1088 (void)odp_timer_pool_destroy(config->tmr_pool);
1089
1090 if (config->tmo_pool != ODP_POOL_INVALID)
1091 (void)odp_pool_destroy(config->tmo_pool);
1092}
1093
1094int main(int argc, char **argv)
1095{
1096 odph_helper_options_t odph_opts;
1097 odp_init_t init_param;
1099 odp_shm_t shm_cfg = ODP_SHM_INVALID;
1100 int ret = EXIT_SUCCESS;
1101 parse_result_t parse_res;
1102
1103 argc = odph_parse_options(argc, argv);
1104
1105 if (odph_options(&odph_opts) == -1)
1106 ODPH_ABORT("Error while reading ODP helper options, aborting\n");
1107
1108 odp_init_param_init(&init_param);
1109 init_param.mem_model = odph_opts.mem_model;
1110
1111 if (odp_init_global(&odp_instance, &init_param, NULL))
1112 ODPH_ABORT("ODP global init failed, aborting\n");
1113
1115 ODPH_ABORT("ODP local init failed, aborting\n");
1116
1117 shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
1118 0U);
1119
1120 if (shm_cfg == ODP_SHM_INVALID) {
1121 ODPH_ERR("Error reserving shared memory for global config\n");
1122 ret = EXIT_FAILURE;
1123 goto out;
1124 }
1125
1126 prog_conf = odp_shm_addr(shm_cfg);
1127
1128 if (prog_conf == NULL) {
1129 ODPH_ERR("Error resolving shared memory address for global config\n");
1130 ret = EXIT_FAILURE;
1131 goto out;
1132 }
1133
1134 memset(prog_conf, 0, sizeof(*prog_conf));
1135 prog_conf->odp_instance = odp_instance;
1136 parse_res = setup_program(argc, argv, prog_conf);
1137
1138 if (parse_res == PRS_NOK) {
1139 ret = EXIT_FAILURE;
1140 goto out;
1141 }
1142
1143 if (parse_res == PRS_TERM) {
1144 ret = EXIT_SUCCESS;
1145 goto out;
1146 }
1147
1148 if (!setup_test(prog_conf)) {
1149 ret = EXIT_FAILURE;
1150 goto out;
1151 }
1152
1153 run_control(prog_conf);
1154 print_stats(prog_conf);
1155
1156out:
1157 teardown(prog_conf);
1158
1159 if (shm_cfg != ODP_SHM_INVALID)
1160 (void)odp_shm_free(shm_cfg);
1161
1162 if (odp_term_local())
1163 ODPH_ABORT("ODP local terminate failed, aborting\n");
1164
1166 ODPH_ABORT("ODP global terminate failed, aborting\n");
1167
1168 return ret;
1169}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 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.
#define ODP_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition spec/hints.h:54
#define ODP_CACHE_LINE_ROUNDUP(x)
Round up to cache line size.
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.
int odp_instance(odp_instance_t *instance)
Get instance handle.
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.
void odp_spinlock_lock(odp_spinlock_t *splock)
Acquire spin lock.
void odp_spinlock_init(odp_spinlock_t *splock)
Initialize spin lock.
void odp_spinlock_unlock(odp_spinlock_t *splock)
Release spin lock.
uint64_t odp_pool_to_u64(odp_pool_t hdl)
Get printable value for an odp_pool_t.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#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.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
int odp_schedule_group_t
Scheduler thread group.
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_default_prio(void)
Default scheduling priority level.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_group_to_u64(odp_schedule_group_t group)
Get printable value for schedule group handle.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
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.
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.
int odp_shm_capability(odp_shm_capability_t *capa)
Query shared memory capabilities.
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.
bool odp_bool_t
Boolean type.
double odp_fract_u64_to_dbl(const odp_fract_u64_t *fract)
Convert fractional number (u64) to double.
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.
#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.
int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num)
Start timer pools.
void * odp_timeout_user_ptr(odp_timeout_t tmo)
Return user pointer for the timeout.
odp_timer_t odp_timer_periodic_alloc(odp_timer_pool_t timer_pool, const odp_timer_periodic_param_t *params)
Allocate a periodic timer.
odp_timeout_t odp_timeout_alloc(odp_pool_t pool)
Timeout alloc.
int odp_timer_free(odp_timer_t timer)
Free a timer.
int odp_timer_periodic_start(odp_timer_t timer, const odp_timer_periodic_start_t *start_param)
Start a periodic 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.
int odp_timer_periodic_capability(odp_timer_clk_src_t clk_src, odp_timer_periodic_capability_t *capa)
Periodic timer capability.
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.
void odp_timer_periodic_param_init(odp_timer_periodic_param_t *param)
Initialize periodic timer parameters.
int odp_timer_cancel(odp_timer_t timer, odp_event_t *tmo_ev)
Cancel a single shot timer.
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_periodic_ack(odp_timer_t timer, odp_event_t tmo_ev)
Acknowledge timeout from a periodic timer.
odp_timer_clk_src_t
Clock sources for timer pools.
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.
int odp_timer_periodic_cancel(odp_timer_t timer)
Cancel a periodic timer.
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.
uint64_t odp_timer_pool_to_u64(odp_timer_pool_t timer_pool)
Get printable value for an odp_timer_pool_t.
#define ODP_TIMER_INVALID
Invalid timer handle.
uint64_t odp_timer_to_u64(odp_timer_t timer)
Get printable value for an odp_timer_t.
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_BUSY
Timer operation failed, resources temporarily busy.
@ ODP_TIMER_SUCCESS
Timer operation succeeded.
@ ODP_TIMER_TOO_NEAR
Timer operation failed, too near to the current time.
@ ODP_TIMER_FAIL
Timer operation failed.
@ ODP_TIMER_TYPE_PERIODIC_BASE_MUL
Periodic timer, period defined through pool base frequency and a multiplier.
@ ODP_TIMER_TYPE_SINGLE
Single shot timer.
@ ODP_TIMER_TICK_REL
Relative ticks.
The OpenDataPlane API.
Unsigned 64 bit fractional number.
uint64_t integer
Integer part.
uint64_t denom
Denominator of the fraction part.
uint64_t numer
Numerator of the fraction part.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@135 tmo
Timeout pool capabilities
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.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
Shared memory capabilities.
uint32_t max_blocks
Maximum number of shared memory blocks.
uint64_t max_size
Maximum memory block size in bytes.
odp_fract_u64_t min_base_freq_hz
Minimum supported base frequency value.
struct odp_timer_capability_t::@186 periodic
Periodic timer capabilities.
uint32_t max_timers
Maximum number of single shot timers in a pool.
struct odp_timer_capability_t::@186::@187 support
Supported period configuration types.
uint32_t max_pools
Maximum number of timer pools for single shot timers (per clock source)
odp_fract_u64_t max_base_freq_hz
Maximum supported base frequency value.
uint32_t base_mul
ODP_TIMER_TYPE_PERIODIC_BASE_MUL supported.
uint64_t highest_res_ns
Highest timer resolution in nanoseconds.
odp_bool_t queue_type_sched
Scheduled queue destination support.
Periodic timer parameters.
const void * user_ptr
User pointer.
struct odp_timer_periodic_param_t::@191 base_mul
Parameters for ODP_TIMER_TYPE_PERIODIC_BASE_MUL timers.
odp_queue_t queue
Destination queue.
uint64_t multiplier
Base frequency multiplier.
Periodic timer start parameters.
Timer pool parameters.
uint64_t res_ns
Timeout resolution in nanoseconds.
odp_timer_type_t timer_type
Timer type.
uint64_t max_multiplier
Maximum base frequency multiplier.
struct odp_timer_pool_param_t::@188 periodic
Periodic timer pool parameters.
odp_fract_u64_t base_freq_hz
Timer pool base frequency in hertz.
uint64_t min_tmo
Minimum relative timeout in nanoseconds.
uint32_t num_timers
Number of timers in the pool.
struct odp_timer_pool_param_t::@188::@189 base_mul
Pool parameters for ODP_TIMER_TYPE_PERIODIC_BASE_MUL.
odp_timer_clk_src_t clk_src
Clock source for timers.
uint64_t max_tmo
Maximum relative timeout in nanoseconds.
Timer resolution capability.
Timer start parameters.
uint64_t tick
Expiration time in ticks.
odp_timer_tick_type_t tick_type
Tick type.