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