API Reference Manual  1.46.0
odp_timer_accuracy.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2018 Linaro Limited
3  * Copyright (c) 2019-2025 Nokia
4  */
5 
14 #include <errno.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <inttypes.h>
18 #include <stdlib.h>
19 
20 #include <unistd.h>
21 #include <getopt.h>
22 
23 #include <odp_api.h>
24 #include <odp/helper/odph_api.h>
25 
26 #include <export_results.h>
27 
28 #define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
29 #define MAX_QUEUES 1024
30 #define MAX_FILENAME 128
31 
32 enum mode_e {
33  MODE_ONESHOT = 0,
34  MODE_RESTART_ABS,
35  MODE_RESTART_REL,
36  MODE_PERIODIC,
37  MODE_CONCURRENCY,
38 };
39 
40 typedef struct test_opt_t {
41  int cpu_count;
42  unsigned long long period_ns;
43  long long res_ns;
44  unsigned long long res_hz;
45  unsigned long long offset_ns;
46  unsigned long long max_tmo_ns;
47  unsigned long long num;
48  unsigned long long num_warmup;
49  unsigned long long burst;
50  unsigned long long burst_gap;
51  odp_fract_u64_t freq;
52  unsigned long long max_multiplier;
53  unsigned long long multiplier;
54  enum mode_e mode;
55  int clk_src;
56  odp_queue_type_t queue_type;
57  int num_queue;
58  int groups;
59  int init;
60  int output;
61  int early_retry;
62  int interval;
63  int max_diff;
64  int cancel;
65  int switch_timer;
66  int switch_event;
67  int cancel_start;
68  int exit_on_error;
69  uint64_t warmup_timers;
70  uint64_t tot_timers;
71  uint64_t alloc_timers;
72  char filename[MAX_FILENAME];
73 } test_opt_t;
74 
75 typedef struct ODP_ALIGNED_CACHE {
76  odp_timer_t timer;
77  odp_event_t event;
78  uint64_t nsec;
79  int64_t nsec_final;
80  odp_ticketlock_t lock;
81  uint64_t events;
82  uint64_t starts;
83  uint64_t first_period;
84  int64_t first_tmo_diff;
85  int tmo_tick;
86 
87 } timer_ctx_t;
88 
89 typedef struct ODP_ALIGNED_CACHE {
90  uint64_t nsec_before_sum;
91  uint64_t nsec_before_min;
92  uint64_t nsec_before_min_idx;
93  uint64_t nsec_before_max;
94  uint64_t nsec_before_max_idx;
95 
96  uint64_t nsec_after_sum;
97  uint64_t nsec_after_min;
98  uint64_t nsec_after_min_idx;
99  uint64_t nsec_after_max;
100  uint64_t nsec_after_max_idx;
101 
102  uint64_t num_before;
103  uint64_t num_exact;
104  uint64_t num_after;
105 
106  uint64_t num_too_near;
107 
108 } test_stat_t;
109 
110 typedef struct test_log_t {
111  uint64_t tmo_ns;
112  int64_t diff_ns;
113  int tid;
114 
115 } test_log_t;
116 
117 typedef struct test_global_t {
118  test_opt_t opt;
119 
120  test_stat_t stat[MAX_WORKERS];
121 
122  odp_queue_t queue[MAX_QUEUES];
123  odp_schedule_group_t group[MAX_WORKERS];
124  odp_timer_pool_t timer_pool;
125  odp_pool_t timeout_pool;
126  timer_ctx_t *timer_ctx;
127  double res_ns;
128  uint64_t start_tick;
129  uint64_t start_ns;
130  uint64_t period_tick;
131  double period_dbl;
132  odp_fract_u64_t base_freq;
133  test_log_t *log;
134  FILE *file;
135  odp_barrier_t barrier;
136  odp_atomic_u64_t events;
137  odp_atomic_u64_t last_events;
138  test_common_options_t common_options;
139 } test_global_t;
140 
141 static void print_usage(void)
142 {
143  printf("\n"
144  "Timer accuracy test application.\n"
145  "\n"
146  "OPTIONS:\n"
147  " -c, --count <num> CPU count, 0=all available, default=1\n"
148  " -p, --period <nsec> Timeout period in nsec. Not used in periodic mode. Default: 200 msec\n"
149  " -r, --res_ns <nsec> Timeout resolution in nsec. Default value is 0. Special values:\n"
150  " 0: Use period / 10 as the resolution\n"
151  " -1: In periodic mode, use resolution from capabilities\n"
152  " -R, --res_hz <hertz> Timeout resolution in hertz. Set resolution either with -r (nsec) or -R (hertz),\n"
153  " and leave other to 0. Default: 0 (not used)\n"
154  " -f, --first <nsec> First timer offset in nsec. Not used in concurrency mode. Default: 0 for\n"
155  " periodic mode, otherwise 300 msec\n"
156  " -x, --max_tmo <nsec> Maximum timeout in nsec. Not used in periodic mode.\n"
157  " When 0, max tmo is calculated from other options. Default: 0\n"
158  " -n, --num <number> Number of timeout periods. Default: 50\n"
159  " -w, --warmup <number> Number of warmup periods. Default: 0\n"
160  " -b, --burst <number> Number of timers per a timeout period. Default: 1\n"
161  " -g, --burst_gap <nsec> Gap (in nsec) between timers within a burst. Default: 0\n"
162  " In periodic mode, first + burst * burst_gap must be less than period length.\n"
163  " -m, --mode <number> Test mode select (default: 0):\n"
164  " 0: One-shot. Start all timers at init phase.\n"
165  " 1: One-shot. Each period, restart timers with absolute time.\n"
166  " 2: One-shot. Each period, restart timers with relative time.\n"
167  " 3: Periodic.\n"
168  " 4: Concurrency.\n"
169  " -o, --output <file> Output file for measurement logs\n"
170  " -s, --clk_src Clock source select (default 0):\n"
171  " 0: ODP_CLOCK_DEFAULT\n"
172  " 1: ODP_CLOCK_SRC_1, ...\n"
173  " -t, --queue_type Queue sync type. Default is 0 (PARALLEL).\n"
174  " 0: PARALLEL\n"
175  " 1: ATOMIC\n"
176  " 2: ORDERED\n"
177  " -q, --num_queue Number of queues. Default is 1.\n"
178  " -G, --sched_groups Use dedicated schedule group for each worker.\n"
179  " -i, --init Set global init parameters. Default: init params not set.\n"
180  " -h, --help Display help and exit.\n"
181  "\n"
182  "ONE-SHOT MODE OPTIONS:\n"
183  " -e, --early_retry <num> When timer restart fails due to ODP_TIMER_TOO_NEAR, retry this many times\n"
184  " with expiration time incremented by the period. Default: 0\n"
185  "\n"
186  "PERIODIC MODE OPTIONS:\n"
187  " -P, --periodic <freq_integer:freq_numer:freq_denom:max_multiplier>\n"
188  " Periodic timer pool parameters. Default: 5:0:0:1 (5 Hz)\n"
189  " -M, --multiplier Periodic timer multiplier. Default: 1\n"
190  "\n"
191  "CONCURRENCY MODE OPTIONS:\n"
192  " -I, --interval <sec> Print interval information every <sec> seconds. Default: 1\n"
193  " -D, --max_diff <nsec> Print error if event is more than <nsec> nanoseconds late. Default: 0 (disabled)\n"
194  " -C, --cancel <n> Every <n> events, cancel the timer. Default: 0 (disabled)\n"
195  " -E, --switch_event <n> Every <n> events, free the received event and allocate a new one.\n"
196  " Default: 0 (disabled)\n"
197  " -T, --switch_timer <n> Every <n> events, free the timer and allocate a new one. Default: 0 (disabled)\n"
198  " -S, --cancel_start <n> Every <n> events, after starting a timer, immediately cancel and start again.\n"
199  " Default: 0 (disabled)\n"
200  " -X, --exit_on_error Exit on duplicate events and late timeouts.\n"
201  "\n");
202 }
203 
204 static int parse_options(int argc, char *argv[], test_opt_t *test_opt)
205 {
206  int opt;
207  const struct option longopts[] = {
208  {"count", required_argument, NULL, 'c'},
209  {"period", required_argument, NULL, 'p'},
210  {"res_ns", required_argument, NULL, 'r'},
211  {"res_hz", required_argument, NULL, 'R'},
212  {"first", required_argument, NULL, 'f'},
213  {"max_tmo", required_argument, NULL, 'x'},
214  {"num", required_argument, NULL, 'n'},
215  {"warmup", required_argument, NULL, 'w'},
216  {"burst", required_argument, NULL, 'b'},
217  {"burst_gap", required_argument, NULL, 'g'},
218  {"mode", required_argument, NULL, 'm'},
219  {"periodic", required_argument, NULL, 'P'},
220  {"multiplier", required_argument, NULL, 'M'},
221  {"output", required_argument, NULL, 'o'},
222  {"early_retry", required_argument, NULL, 'e'},
223  {"clk_src", required_argument, NULL, 's'},
224  {"queue_type", required_argument, NULL, 't'},
225  {"num_queue", required_argument, NULL, 'q'},
226  {"interval", required_argument, NULL, 'I'},
227  {"max_diff", required_argument, NULL, 'D'},
228  {"cancel", required_argument, NULL, 'C'},
229  {"switch_event", required_argument, NULL, 'E'},
230  {"switch_timer", required_argument, NULL, 'T'},
231  {"cancel_start", required_argument, NULL, 'S'},
232  {"exit_on_error", no_argument, NULL, 'X'},
233  {"sched_groups", no_argument, NULL, 'G'},
234  {"init", no_argument, NULL, 'i'},
235  {"help", no_argument, NULL, 'h'},
236  {NULL, 0, NULL, 0}
237  };
238  const char *shortopts = "+c:p:r:R:f:x:n:w:b:g:m:P:M:o:e:s:t:q:I:D:C:E:T:S:XGih";
239  int ret = 0;
240 
241  memset(test_opt, 0, sizeof(*test_opt));
242 
243  test_opt->cpu_count = 1;
244  test_opt->period_ns = 200 * ODP_TIME_MSEC_IN_NS;
245  test_opt->offset_ns = UINT64_MAX;
246  test_opt->num = 50;
247  test_opt->burst = 1;
248  test_opt->mode = MODE_ONESHOT;
249  test_opt->freq.integer = ODP_TIME_SEC_IN_NS / test_opt->period_ns;
250  test_opt->max_multiplier = 1;
251  test_opt->multiplier = 1;
252  test_opt->clk_src = ODP_CLOCK_DEFAULT;
253  test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL;
254  test_opt->num_queue = 1;
255  test_opt->interval = 1;
256 
257  while (1) {
258  opt = getopt_long(argc, argv, shortopts, longopts, NULL);
259 
260  if (opt == -1)
261  break; /* No more options */
262 
263  switch (opt) {
264  case 'c':
265  test_opt->cpu_count = atoi(optarg);
266  break;
267  case 'p':
268  test_opt->period_ns = strtoull(optarg, NULL, 0);
269  break;
270  case 'r':
271  test_opt->res_ns = strtoll(optarg, NULL, 0);
272  break;
273  case 'R':
274  test_opt->res_hz = strtoull(optarg, NULL, 0);
275  break;
276  case 'f':
277  test_opt->offset_ns = strtoull(optarg, NULL, 0);
278  break;
279  case 'x':
280  test_opt->max_tmo_ns = strtoull(optarg, NULL, 0);
281  break;
282  case 'n':
283  test_opt->num = strtoull(optarg, NULL, 0);
284  break;
285  case 'w':
286  test_opt->num_warmup = strtoull(optarg, NULL, 0);
287  break;
288  case 'b':
289  test_opt->burst = strtoull(optarg, NULL, 0);
290  break;
291  case 'g':
292  test_opt->burst_gap = strtoull(optarg, NULL, 0);
293  break;
294  case 'm':
295  test_opt->mode = atoi(optarg);
296  break;
297  case 'P':
298  sscanf(optarg, "%" SCNu64 ":%" SCNu64 ":%" SCNu64 ":%llu",
299  &test_opt->freq.integer, &test_opt->freq.numer,
300  &test_opt->freq.denom, &test_opt->max_multiplier);
301  break;
302  case 'M':
303  test_opt->multiplier = strtoull(optarg, NULL, 0);
304  break;
305  case 'o':
306  test_opt->output = 1;
307  if (strlen(optarg) >= MAX_FILENAME) {
308  ODPH_ERR("Filename too long\n");
309  return -1;
310  }
311  odph_strcpy(test_opt->filename, optarg, MAX_FILENAME);
312  break;
313  case 'e':
314  test_opt->early_retry = atoi(optarg);
315  break;
316  case 's':
317  test_opt->clk_src = atoi(optarg);
318  break;
319  case 't':
320  switch (atoi(optarg)) {
321  case 1:
322  test_opt->queue_type = ODP_SCHED_SYNC_ATOMIC;
323  break;
324  case 2:
325  test_opt->queue_type = ODP_SCHED_SYNC_ORDERED;
326  break;
327  default:
328  test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL;
329  break;
330  }
331  break;
332  case 'q':
333  test_opt->num_queue = atoi(optarg);
334  break;
335  case 'I':
336  test_opt->interval = atoi(optarg);
337  break;
338  case 'D':
339  test_opt->max_diff = atoi(optarg);
340  break;
341  case 'C':
342  test_opt->cancel = atoi(optarg);
343  break;
344  case 'E':
345  test_opt->switch_event = atoi(optarg);
346  break;
347  case 'T':
348  test_opt->switch_timer = atoi(optarg);
349  break;
350  case 'S':
351  test_opt->cancel_start = atoi(optarg);
352  break;
353  case 'X':
354  test_opt->exit_on_error = 1;
355  break;
356  case 'G':
357  test_opt->groups = 1;
358  break;
359  case 'i':
360  test_opt->init = 1;
361  break;
362  case 'h':
363  print_usage();
364  ret = -1;
365  break;
366  default:
367  print_usage();
368  ret = -1;
369  break;
370  }
371  }
372 
373  if (test_opt->mode == MODE_PERIODIC) {
374  if ((test_opt->freq.integer == 0 && test_opt->freq.numer == 0) ||
375  (test_opt->freq.numer != 0 && test_opt->freq.denom == 0)) {
376  ODPH_ERR("Bad frequency\n");
377  return -1;
378  }
379 
380  test_opt->period_ns =
381  ODP_TIME_SEC_IN_NS / odp_fract_u64_to_dbl(&test_opt->freq);
382 
383  if (test_opt->offset_ns == UINT64_MAX)
384  test_opt->offset_ns = 0;
385  } else {
386  if (test_opt->res_ns < 0) {
387  ODPH_ERR("Resolution (res_ns) must be >= 0 with single shot timer\n");
388  return -1;
389  }
390 
391  if (test_opt->offset_ns == UINT64_MAX)
392  test_opt->offset_ns = 300 * ODP_TIME_MSEC_IN_NS;
393 
394  if (test_opt->mode == MODE_CONCURRENCY)
395  test_opt->offset_ns = 0;
396  }
397 
398  test_opt->warmup_timers = test_opt->num_warmup * test_opt->burst;
399  test_opt->tot_timers =
400  test_opt->warmup_timers + test_opt->num * test_opt->burst;
401 
402  if (test_opt->mode == MODE_ONESHOT)
403  test_opt->alloc_timers = test_opt->tot_timers;
404  else
405  test_opt->alloc_timers = test_opt->burst;
406 
407  return ret;
408 }
409 
410 static int single_shot_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param,
411  odp_timer_capability_t *timer_capa)
412 {
413  uint64_t res_ns, res_hz;
414  uint64_t max_res_ns, max_res_hz;
415  uint64_t period_ns = test_global->opt.period_ns;
416  uint64_t num_tmo = test_global->opt.num + test_global->opt.num_warmup;
417  uint64_t offset_ns = test_global->opt.offset_ns;
418  enum mode_e mode = test_global->opt.mode;
419 
420  max_res_ns = timer_capa->max_res.res_ns;
421  max_res_hz = timer_capa->max_res.res_hz;
422 
423  /* Default resolution */
424  if (test_global->opt.res_ns == 0 && test_global->opt.res_hz == 0) {
425  res_ns = test_global->opt.period_ns / 10;
426  res_hz = 0;
427  } else if (test_global->opt.res_ns) {
428  res_ns = test_global->opt.res_ns;
429  res_hz = 0;
430  } else {
431  res_ns = 0;
432  res_hz = test_global->opt.res_hz;
433  }
434 
435  if (res_ns && res_ns < max_res_ns) {
436  ODPH_ERR("Resolution %" PRIu64 " nsec too high.\n"
437  "Highest resolution %" PRIu64
438  " nsec. Default resolution is period / 10.\n\n",
439  res_ns, max_res_ns);
440  return -1;
441  }
442 
443  if (res_hz && res_hz > max_res_hz) {
444  ODPH_ERR("Resolution %" PRIu64 " hz too high.\n"
445  "Highest resolution %" PRIu64
446  " hz. Default resolution is period / 10.\n\n",
447  res_hz, max_res_hz);
448  return -1;
449  }
450 
451  if (res_ns)
452  timer_param->res_ns = res_ns;
453  else
454  timer_param->res_hz = res_hz;
455 
456  if (mode == MODE_ONESHOT) {
457  timer_param->min_tmo = offset_ns / 2;
458  timer_param->max_tmo = offset_ns + ((num_tmo + 1) * period_ns);
459  } else {
460  if (mode == MODE_RESTART_ABS)
461  timer_param->min_tmo = period_ns / 10;
462  else
463  timer_param->min_tmo = period_ns;
464  timer_param->max_tmo = offset_ns + (2 * period_ns);
465  }
466 
467  if (test_global->opt.max_tmo_ns) {
468  if (test_global->opt.max_tmo_ns < timer_param->max_tmo) {
469  ODPH_ERR("Max tmo is too small. Must be at least %" PRIu64 " nsec.\n",
470  timer_param->max_tmo);
471  return -1;
472  }
473 
474  timer_param->max_tmo = test_global->opt.max_tmo_ns;
475  }
476 
477  printf(" period: %" PRIu64 " nsec\n", period_ns);
478  printf(" max res nsec: %" PRIu64 "\n", max_res_ns);
479  printf(" max res hertz: %" PRIu64 "\n", max_res_hz);
480 
481  test_global->period_dbl = period_ns;
482 
483  return 0;
484 }
485 
486 static int periodic_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param,
487  odp_timer_capability_t *timer_capa)
488 {
489  int ret;
490  uint64_t res_ns;
492  double freq_dbl, min_freq, max_freq;
493  double opt_freq = odp_fract_u64_to_dbl(&test_global->opt.freq);
494  odp_fract_u64_t freq = test_global->opt.freq;
495  uint64_t res_hz = test_global->opt.res_hz;
496  uint64_t max_multiplier = test_global->opt.max_multiplier;
497  uint64_t multiplier = test_global->opt.multiplier;
498 
499  if (res_hz) {
500  res_ns = ODP_TIME_SEC_IN_NS / res_hz;
501  } else {
502  res_ns = test_global->opt.res_ns;
503 
504  /* Default resolution */
505  if (res_ns == 0)
506  res_ns = ODP_TIME_SEC_IN_NS / (10 * multiplier * opt_freq);
507  }
508 
509  if (res_ns == 0) {
510  ODPH_ERR("Resolution too high\n");
511  return -1;
512  }
513 
514  /* Resolution from capa */
515  if (test_global->opt.res_ns < 0)
516  res_ns = 0;
517 
518  min_freq = odp_fract_u64_to_dbl(&timer_capa->periodic.min_base_freq_hz);
519  max_freq = odp_fract_u64_to_dbl(&timer_capa->periodic.max_base_freq_hz);
520 
521  capa.base_freq_hz = freq;
522  capa.max_multiplier = max_multiplier;
523  capa.res_ns = res_ns;
524 
525  ret = odp_timer_periodic_capability(test_global->opt.clk_src, &capa);
526 
527  if (ret < 0) {
528  ODPH_ERR("Requested periodic timer capabilities are not supported.\n"
529  "Capabilities: min base freq %g Hz, max base freq %g Hz, "
530  "max res %" PRIu64 " Hz\n",
531  min_freq, max_freq, timer_capa->max_res.res_hz);
532  return -1;
533  }
534 
535  if (ret == 0) {
536  printf("Requested base frequency is not met. Using %.2f Hz instead of %.2f Hz.\n",
537  odp_fract_u64_to_dbl(&capa.base_freq_hz), opt_freq);
538 
539  freq = capa.base_freq_hz;
540  }
541 
542  if (res_ns == 0)
543  res_ns = capa.res_ns;
544 
545  freq_dbl = odp_fract_u64_to_dbl(&freq);
546  test_global->base_freq = freq;
547  test_global->period_dbl = ODP_TIME_SEC_IN_NS / (multiplier * freq_dbl);
548 
549  /* Min/max tmo are ignored, leave those to default values */
550  timer_param->timer_type = ODP_TIMER_TYPE_PERIODIC;
551  timer_param->periodic.base_freq_hz = freq;
552  timer_param->periodic.max_multiplier = max_multiplier;
553 
554  if (res_hz)
555  timer_param->res_hz = res_hz;
556  else
557  timer_param->res_ns = res_ns;
558 
559  printf(" min freq capa: %.2f hz\n", min_freq);
560  printf(" max freq capa: %.2f hz\n", max_freq);
561  printf(" freq option: %.2f hz\n", opt_freq);
562  printf(" freq: %.2f hz\n", freq_dbl);
563  printf(" freq integer: %" PRIu64 "\n", freq.integer);
564  printf(" freq numer: %" PRIu64 "\n", freq.numer);
565  printf(" freq denom: %" PRIu64 "\n", freq.denom);
566  printf(" max_multiplier: %" PRIu64 "\n", max_multiplier);
567  printf(" multiplier: %" PRIu64 "\n", multiplier);
568  printf(" timer freq: %.2f hz\n", multiplier * freq_dbl);
569  printf(" timer period: %.2f nsec\n", test_global->period_dbl);
570  printf(" resolution capa: %" PRIu64 " nsec\n", capa.res_ns);
571 
572  return 0;
573 }
574 
575 static int create_timers(test_global_t *test_global)
576 {
577  odp_pool_t pool;
578  odp_pool_param_t pool_param;
579  odp_timer_pool_t timer_pool;
580  odp_timer_pool_param_t timer_param;
581  odp_timer_capability_t timer_capa;
582  odp_timer_t timer;
583  odp_queue_t *queue;
584  odp_schedule_group_t *group;
585  odp_queue_param_t queue_param;
586  uint64_t offset_ns;
587  uint32_t max_timers;
588  odp_event_t event;
589  odp_timeout_t timeout;
590  uint64_t i, num_tmo, num_warmup, burst, burst_gap;
591  uint64_t tot_timers, alloc_timers;
592  enum mode_e mode;
593  odp_timer_clk_src_t clk_src;
594  int ret;
595 
596  mode = test_global->opt.mode;
597  alloc_timers = test_global->opt.alloc_timers;
598  tot_timers = test_global->opt.tot_timers;
599  num_warmup = test_global->opt.num_warmup;
600  num_tmo = num_warmup + test_global->opt.num;
601  burst = test_global->opt.burst;
602  burst_gap = test_global->opt.burst_gap;
603  offset_ns = test_global->opt.offset_ns;
604  queue = test_global->queue;
605  group = test_global->group;
606 
607  /* Always init globals for destroy calls */
608  test_global->timer_pool = ODP_TIMER_POOL_INVALID;
609  test_global->timeout_pool = ODP_POOL_INVALID;
610 
611  for (i = 0; i < alloc_timers; i++) {
612  test_global->timer_ctx[i].timer = ODP_TIMER_INVALID;
613  test_global->timer_ctx[i].event = ODP_EVENT_INVALID;
614  }
615 
616  if (test_global->opt.groups) {
617  /* Create groups */
618 
619  odp_thrmask_t zero;
620 
621  odp_thrmask_zero(&zero);
622 
623  for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) {
624  group[i] = odp_schedule_group_create(NULL, &zero);
625 
626  if (group[i] == ODP_SCHED_GROUP_INVALID) {
627  ODPH_ERR("Group create failed.\n");
628  return -1;
629  }
630  }
631  }
632 
633  odp_queue_param_init(&queue_param);
634  queue_param.type = ODP_QUEUE_TYPE_SCHED;
635  queue_param.sched.prio = odp_schedule_default_prio();
636  queue_param.sched.sync = test_global->opt.queue_type;
637  queue_param.sched.group = ODP_SCHED_GROUP_ALL;
638 
639  for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) {
640  if (test_global->opt.groups)
641  queue_param.sched.group = group[i % test_global->opt.cpu_count];
642 
643  queue[i] = odp_queue_create(NULL, &queue_param);
644  if (queue[i] == ODP_QUEUE_INVALID) {
645  ODPH_ERR("Queue create failed.\n");
646  return -1;
647  }
648  }
649 
650  odp_pool_param_init(&pool_param);
651  pool_param.type = ODP_POOL_TIMEOUT;
652  pool_param.tmo.num = alloc_timers + pool_param.tmo.cache_size * test_global->opt.cpu_count;
653  if (mode == MODE_CONCURRENCY)
654  pool_param.tmo.num += test_global->opt.cpu_count;
655 
656  pool = odp_pool_create("timeout pool", &pool_param);
657 
658  if (pool == ODP_POOL_INVALID) {
659  ODPH_ERR("Timeout pool create failed.\n");
660  return -1;
661  }
662 
663  test_global->timeout_pool = pool;
664  clk_src = test_global->opt.clk_src;
665 
666  if (odp_timer_capability(clk_src, &timer_capa)) {
667  ODPH_ERR("Timer capa failed\n");
668  return -1;
669  }
670 
671  max_timers = timer_capa.max_timers;
672 
673  if (mode == MODE_PERIODIC) {
674  if (timer_capa.periodic.max_pools < 1) {
675  ODPH_ERR("Periodic timers not supported.\n");
676  return -1;
677  }
678  max_timers = timer_capa.periodic.max_timers;
679  }
680 
681  printf("\nTest parameters:\n");
682  printf(" clock source: %i\n", clk_src);
683  printf(" max timers capa: %" PRIu32 "\n", max_timers);
684  printf(" mode: %i\n", mode);
685  printf(" queue type: %i\n", test_global->opt.queue_type);
686  printf(" num queue: %i\n", test_global->opt.num_queue);
687  printf(" sched groups: %s\n", test_global->opt.groups ? "yes" : "no");
688 
689  odp_timer_pool_param_init(&timer_param);
690 
691  if (mode == MODE_PERIODIC)
692  ret = periodic_params(test_global, &timer_param, &timer_capa);
693  else
694  ret = single_shot_params(test_global, &timer_param, &timer_capa);
695 
696  if (ret)
697  return ret;
698 
699  if (timer_param.res_hz) {
700  test_global->res_ns = 1000000000.0 / timer_param.res_hz;
701  printf(" resolution: %" PRIu64 " Hz\n", timer_param.res_hz);
702  } else {
703  test_global->res_ns = timer_param.res_ns;
704  printf(" resolution: %" PRIu64 " nsec\n", timer_param.res_ns);
705  }
706 
707  timer_param.num_timers = alloc_timers;
708  if (mode == MODE_CONCURRENCY)
709  timer_param.num_timers += test_global->opt.cpu_count;
710 
711  if (max_timers && timer_param.num_timers > max_timers) {
712  ODPH_ERR("Too many timers: %" PRIu64 " (max %u)\n", test_global->opt.alloc_timers,
713  max_timers);
714  return -1;
715  }
716 
717  timer_param.clk_src = clk_src;
718 
719  printf(" restart retries: %i\n", test_global->opt.early_retry);
720  if (test_global->opt.output)
721  printf(" log file: %s\n", test_global->opt.filename);
722  printf(" start offset: %" PRIu64 " nsec\n", offset_ns);
723  printf(" min timeout: %" PRIu64 " nsec\n", timer_param.min_tmo);
724  printf(" max timeout: %" PRIu64 " nsec\n", timer_param.max_tmo);
725  printf(" num timeout: %" PRIu64 "\n", num_tmo);
726  printf(" num warmup: %" PRIu64 "\n", num_warmup);
727  printf(" burst size: %" PRIu64 "\n", burst);
728  printf(" burst gap: %" PRIu64 "\n", burst_gap);
729  printf(" total timers: %" PRIu64 "\n", tot_timers);
730  printf(" warmup timers: %" PRIu64 "\n", test_global->opt.warmup_timers);
731  printf(" alloc timers: %" PRIu64 "\n", alloc_timers);
732  printf(" warmup time: %.2f sec\n",
733  (offset_ns + (num_warmup * test_global->period_dbl)) / 1000000000.0);
734  printf(" test run time: %.2f sec\n\n",
735  (offset_ns + (num_tmo * test_global->period_dbl)) / 1000000000.0);
736 
737  timer_pool = odp_timer_pool_create("timer_accuracy", &timer_param);
738 
739  if (timer_pool == ODP_TIMER_POOL_INVALID) {
740  ODPH_ERR("Timer pool create failed\n");
741  return -1;
742  }
743 
744  if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) {
745  ODPH_ERR("Timer pool start failed\n");
746  return -1;
747  }
748 
749  odp_timer_pool_print(timer_pool);
750 
751  /* Spend some time so that current tick would not be zero */
753 
754  test_global->timer_pool = timer_pool;
755 
756  for (i = 0; i < alloc_timers; i++) {
757  timer_ctx_t *ctx = &test_global->timer_ctx[i];
758 
759  timer = odp_timer_alloc(timer_pool, queue[i % test_global->opt.num_queue], ctx);
760 
761  if (timer == ODP_TIMER_INVALID) {
762  ODPH_ERR("Timer alloc failed.\n");
763  return -1;
764  }
765 
766  ctx->timer = timer;
767 
768  timeout = odp_timeout_alloc(pool);
769  if (timeout == ODP_TIMEOUT_INVALID) {
770  ODPH_ERR("Timeout alloc failed\n");
771  return -1;
772  }
773 
774  ctx->event = odp_timeout_to_event(timeout);
775 
776  odp_ticketlock_init(&ctx->lock);
777  }
778 
779  /* Run scheduler few times to ensure that (software) timer is active */
780  for (i = 0; i < 1000; i++) {
781  event = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
782 
783  if (event != ODP_EVENT_INVALID) {
784  ODPH_ERR("Spurious event received\n");
785  odp_event_free(event);
786  return -1;
787  }
788  }
789 
790  return 0;
791 }
792 
793 static int start_timers(test_global_t *test_global)
794 {
795  odp_timer_pool_t timer_pool;
796  uint64_t start_tick;
797  uint64_t period_ns, start_ns, nsec, offset_ns;
798  odp_time_t time;
799  uint64_t i, j, idx, num_tmo, num_warmup, burst, burst_gap;
800  enum mode_e mode;
801 
802  mode = test_global->opt.mode;
803  num_warmup = test_global->opt.num_warmup;
804  num_tmo = num_warmup + test_global->opt.num;
805  burst = test_global->opt.burst;
806  burst_gap = test_global->opt.burst_gap;
807  period_ns = test_global->opt.period_ns;
808  offset_ns = test_global->opt.offset_ns;
809  timer_pool = test_global->timer_pool;
810  idx = 0;
811 
812  /* Record test start time and tick. Memory barriers forbid compiler and out-of-order
813  * CPU to move samples apart. */
814  odp_mb_full();
815  start_tick = odp_timer_current_tick(timer_pool);
816  time = odp_time_global();
817  odp_mb_full();
818 
819  start_ns = odp_time_to_ns(time);
820  test_global->start_tick = start_tick;
821  test_global->start_ns = start_ns;
822  test_global->period_tick = odp_timer_ns_to_tick(timer_pool, period_ns);
823 
824  /* When mode is not one-shot, set only one burst of timers initially */
825  if (mode != MODE_ONESHOT)
826  num_tmo = 1;
827 
828  for (i = 0; i < num_tmo; i++) {
829  odp_timer_retval_t retval;
830 
831  for (j = 0; j < burst; j++) {
832  timer_ctx_t *ctx = &test_global->timer_ctx[idx];
833  odp_timer_start_t start_param;
834 
835  if (mode == MODE_PERIODIC) {
836  odp_timer_periodic_start_t periodic_start;
837 
838  nsec = offset_ns + (j * burst_gap);
839 
840  /* By default, timer starts one period after current time. Round
841  * floating point to closest integer number. */
842  ctx->nsec = start_ns + test_global->period_dbl + 0.5;
843  if (nsec)
844  ctx->nsec = start_ns + nsec;
845 
846  ctx->first_period = start_tick +
847  odp_timer_ns_to_tick(timer_pool,
848  test_global->period_dbl + 0.5);
849  periodic_start.freq_multiplier = test_global->opt.multiplier;
850  periodic_start.first_tick = 0;
851  if (nsec)
852  periodic_start.first_tick =
853  start_tick + odp_timer_ns_to_tick(timer_pool, nsec);
854  periodic_start.tmo_ev = ctx->event;
855  retval = odp_timer_periodic_start(ctx->timer, &periodic_start);
856  } else if (mode == MODE_CONCURRENCY) {
857  ctx->nsec = start_ns + test_global->opt.period_ns;
858  start_param.tick_type = ODP_TIMER_TICK_REL;
859  start_param.tick = test_global->period_tick;
860  start_param.tmo_ev = ctx->event;
861  ctx->starts++;
862  retval = odp_timer_start(ctx->timer, &start_param);
863  } else {
864  nsec = offset_ns + (i * period_ns) + (j * burst_gap);
865  ctx->nsec = start_ns + nsec;
866  start_param.tick_type = ODP_TIMER_TICK_ABS;
867  start_param.tick =
868  start_tick + odp_timer_ns_to_tick(timer_pool, nsec);
869  start_param.tmo_ev = ctx->event;
870  retval = odp_timer_start(ctx->timer, &start_param);
871  }
872 
873  if (retval != ODP_TIMER_SUCCESS) {
874  ODPH_ERR("Timer[%" PRIu64 "] set failed: %i\n", idx, retval);
875  return -1;
876  }
877 
878  idx++;
879  }
880  }
881 
882  printf("\nStarting timers took %" PRIu64 " nsec\n", odp_time_global_ns() - start_ns);
883 
884  return 0;
885 }
886 
887 static int destroy_timers(test_global_t *test_global)
888 {
889  uint64_t i, alloc_timers;
890  odp_timer_t timer;
891  int ret = 0;
892 
893  alloc_timers = test_global->opt.alloc_timers;
894 
895  for (i = 0; i < alloc_timers; i++) {
896  timer = test_global->timer_ctx[i].timer;
897 
898  if (timer == ODP_TIMER_INVALID)
899  break;
900 
901  if (odp_timer_free(timer)) {
902  ODPH_ERR("Timer free failed: %" PRIu64 "\n", i);
903  ret = -1;
904  }
905  }
906 
907  if (test_global->timer_pool != ODP_TIMER_POOL_INVALID)
908  odp_timer_pool_destroy(test_global->timer_pool);
909 
910  if (test_global->timeout_pool != ODP_POOL_INVALID) {
911  if (odp_pool_destroy(test_global->timeout_pool)) {
912  ODPH_ERR("Pool destroy failed.\n");
913  ret = -1;
914  }
915  }
916 
917  for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) {
918  if (odp_queue_destroy(test_global->queue[i])) {
919  ODPH_ERR("Queue destroy failed.\n");
920  ret = -1;
921  }
922  }
923 
924  if (test_global->opt.groups) {
925  for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) {
926  if (odp_schedule_group_destroy(test_global->group[i])) {
927  ODPH_ERR("Group destroy failed.\n");
928  ret = -1;
929  }
930  }
931  }
932 
933  return ret;
934 }
935 
936 static void print_nsec_error(const char *str, int64_t nsec, double res_ns,
937  int tid, int idx)
938 {
939  printf(" %s: %12" PRIi64 " / %.3fx resolution",
940  str, nsec, (double)nsec / res_ns);
941  if (tid >= 0)
942  printf(", thread %d", tid);
943  if (idx >= 0)
944  printf(", event %d", idx);
945  printf("\n");
946 }
947 
948 static int print_stat(test_global_t *test_global)
949 {
950  test_stat_t test_stat;
951  test_stat_t *stat = &test_stat;
952  uint64_t tot_timers;
953  test_stat_t *s = test_global->stat;
954  test_log_t *log = test_global->log;
955  double res_ns = test_global->res_ns;
956  uint64_t ave_after = 0;
957  uint64_t ave_before = 0;
958  uint64_t nsec_before_min_tid = 0;
959  uint64_t nsec_before_max_tid = 0;
960  uint64_t nsec_after_min_tid = 0;
961  uint64_t nsec_after_max_tid = 0;
962 
963  memset(stat, 0, sizeof(*stat));
964  stat->nsec_before_min = UINT64_MAX;
965  stat->nsec_after_min = UINT64_MAX;
966 
967  for (int i = 1; i < test_global->opt.cpu_count + 1; i++) {
968  stat->nsec_before_sum += s[i].nsec_before_sum;
969  stat->nsec_after_sum += s[i].nsec_after_sum;
970  stat->num_before += s[i].num_before;
971  stat->num_exact += s[i].num_exact;
972  stat->num_after += s[i].num_after;
973  stat->num_too_near += s[i].num_too_near;
974 
975  if (s[i].nsec_before_min < stat->nsec_before_min) {
976  stat->nsec_before_min = s[i].nsec_before_min;
977  stat->nsec_before_min_idx = s[i].nsec_before_min_idx;
978  nsec_before_min_tid = i;
979  }
980 
981  if (s[i].nsec_after_min < stat->nsec_after_min) {
982  stat->nsec_after_min = s[i].nsec_after_min;
983  stat->nsec_after_min_idx = s[i].nsec_after_min_idx;
984  nsec_after_min_tid = i;
985  }
986 
987  if (s[i].nsec_before_max > stat->nsec_before_max) {
988  stat->nsec_before_max = s[i].nsec_before_max;
989  stat->nsec_before_max_idx = s[i].nsec_before_max_idx;
990  nsec_before_max_tid = i;
991  }
992 
993  if (s[i].nsec_after_max > stat->nsec_after_max) {
994  stat->nsec_after_max = s[i].nsec_after_max;
995  stat->nsec_after_max_idx = s[i].nsec_after_max_idx;
996  nsec_after_max_tid = i;
997  }
998  }
999 
1000  if (stat->num_after)
1001  ave_after = stat->nsec_after_sum / stat->num_after;
1002  else
1003  stat->nsec_after_min = 0;
1004 
1005  if (stat->num_before)
1006  ave_before = stat->nsec_before_sum / stat->num_before;
1007  else
1008  stat->nsec_before_min = 0;
1009 
1010  tot_timers = stat->num_before + stat->num_after + stat->num_exact;
1011 
1012  if (log) {
1013  FILE *file = test_global->file;
1014 
1015  fprintf(file, " Timer thread tmo(ns) diff(ns)\n");
1016 
1017  for (uint64_t i = 0; i < tot_timers; i++) {
1018  fprintf(file, "%8" PRIu64 " %7u %12" PRIu64 " %10"
1019  PRIi64 "\n", i, log[i].tid, log[i].tmo_ns, log[i].diff_ns);
1020  }
1021 
1022  fprintf(file, "\n");
1023  }
1024 
1025  printf("\nTest results:\n");
1026  printf(" num after: %12" PRIu64 " / %.2f%%\n",
1027  stat->num_after, 100.0 * stat->num_after / tot_timers);
1028  printf(" num before: %12" PRIu64 " / %.2f%%\n",
1029  stat->num_before, 100.0 * stat->num_before / tot_timers);
1030  printf(" num exact: %12" PRIu64 " / %.2f%%\n",
1031  stat->num_exact, 100.0 * stat->num_exact / tot_timers);
1032  printf(" num retry: %12" PRIu64 " / %.2f%%\n",
1033  stat->num_too_near, 100.0 * stat->num_too_near / tot_timers);
1034  printf(" error after (nsec):\n");
1035  print_nsec_error("min", stat->nsec_after_min, res_ns, nsec_after_min_tid,
1036  stat->nsec_after_min_idx);
1037  print_nsec_error("max", stat->nsec_after_max, res_ns, nsec_after_max_tid,
1038  stat->nsec_after_max_idx);
1039  print_nsec_error("ave", ave_after, res_ns, -1, -1);
1040  printf(" error before (nsec):\n");
1041  print_nsec_error("min", stat->nsec_before_min, res_ns, nsec_before_min_tid,
1042  stat->nsec_before_min_idx);
1043  print_nsec_error("max", stat->nsec_before_max, res_ns, nsec_before_max_tid,
1044  stat->nsec_before_max_idx);
1045  print_nsec_error("ave", ave_before, res_ns, -1, -1);
1046 
1047  if (test_global->opt.mode == MODE_PERIODIC && !test_global->opt.offset_ns) {
1048  int idx = 0;
1049  int64_t max = 0;
1050 
1051  for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) {
1052  timer_ctx_t *t = &test_global->timer_ctx[i];
1053  int64_t v = t->first_tmo_diff;
1054 
1055  if (ODPH_ABS(v) > ODPH_ABS(max)) {
1056  max = v;
1057  idx = i;
1058  }
1059  }
1060 
1061  printf(" first timeout difference to one period, based on %s (nsec):\n",
1062  test_global->timer_ctx[idx].tmo_tick ? "timeout tick" : "time");
1063  print_nsec_error("max", max, res_ns, -1, -1);
1064  }
1065 
1066  int64_t max = 0;
1067 
1068  for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) {
1069  timer_ctx_t *t = &test_global->timer_ctx[i];
1070  int64_t v = t->nsec_final;
1071 
1072  if (ODPH_ABS(v) > ODPH_ABS(max))
1073  max = v;
1074  }
1075 
1076  printf(" final timeout error (nsec):\n");
1077  print_nsec_error("max", max, res_ns, -1, -1);
1078 
1079  printf("\n");
1080 
1081  if (test_global->common_options.is_export) {
1082  if (test_common_write("num after,num before,num exact,num retry,"
1083  "error after min (nsec),error after min resolution,"
1084  "error after max (nsec),error after max resolution,"
1085  "error after ave (nsec),error after ave resolution,"
1086  "error before min (nsec),error before min resolution,"
1087  "error before max (nsec),error before max resolution,"
1088  "error before ave (nsec),error before ave resolution,"
1089  "final timeout error max (nsec),"
1090  "final timeout error max resolution\n")) {
1091  ODPH_ERR("Export failed\n");
1092  test_common_write_term();
1093  return -1;
1094  }
1095 
1096  if (test_common_write("%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ","
1097  "%f,%" PRIu64 ",%f,%" PRIu64 ",%f,%" PRIu64 ",%f,%" PRIu64 ","
1098  "%f,%" PRIu64 ",%f,%" PRId64 ",%f\n",
1099  stat->num_after, stat->num_before,
1100  stat->num_exact, stat->num_too_near,
1101  stat->nsec_after_min, (double)stat->nsec_after_min / res_ns,
1102  stat->nsec_after_max, (double)stat->nsec_after_max / res_ns,
1103  ave_after, (double)ave_after / res_ns,
1104  stat->nsec_before_min, (double)stat->nsec_before_min / res_ns,
1105  stat->nsec_before_max, (double)stat->nsec_before_max / res_ns,
1106  ave_before, (double)ave_before / res_ns,
1107  max, (double)max / res_ns
1108  )) {
1109  ODPH_ERR("Export failed\n");
1110  test_common_write_term();
1111  return -1;
1112  }
1113 
1114  test_common_write_term();
1115  }
1116 
1117  return 0;
1118 }
1119 
1120 static void cancel_periodic_timers(test_global_t *test_global)
1121 {
1122  uint64_t i, alloc_timers;
1123  odp_timer_t timer;
1124 
1125  alloc_timers = test_global->opt.alloc_timers;
1126 
1127  for (i = 0; i < alloc_timers; i++) {
1128  timer = test_global->timer_ctx[i].timer;
1129 
1130  if (timer == ODP_TIMER_INVALID)
1131  break;
1132 
1133  if (odp_timer_periodic_cancel(timer))
1134  ODPH_ERR("Failed to cancel periodic timer.\n");
1135  }
1136 }
1137 
1138 static void process_event_concurrency(uint64_t events, test_global_t *test_global, timer_ctx_t *ctx,
1139  uint64_t time_ns, odp_event_t ev)
1140 {
1141  odp_timer_retval_t ret;
1142  odp_timer_start_t start_param;
1143  odp_timer_t tim;
1144  int locked = 0;
1145  const uint64_t period_ns = test_global->opt.period_ns;
1146  const uint64_t tick = test_global->period_tick;
1147  const int cancel_start =
1148  test_global->opt.cancel_start && !(events % test_global->opt.cancel_start);
1149  const int cancel = test_global->opt.cancel && !(events % test_global->opt.cancel);
1150  const int switch_timer =
1151  test_global->opt.switch_timer && !(events % test_global->opt.switch_timer);
1152  const int switch_event =
1153  test_global->opt.switch_event && !(events % test_global->opt.switch_event);
1154 
1155  if (cancel_start) {
1156  /*
1157  * During the start-cancel-start sequence another thread could receive the event
1158  * immediately after the first start() call. Use a lock to prevent the other thread
1159  * from proceeding while we are still using the timer.
1160  */
1161  locked = 1;
1162  odp_ticketlock_lock(&ctx->lock);
1163  }
1164 
1165  tim = ctx->timer;
1166  ctx->nsec = time_ns + period_ns;
1167 
1168  if (cancel) {
1169  odp_event_t event = NULL;
1170  /*
1171  * Cancel the timer which originated this event. This should never succeed, since
1172  * the timer already expired.
1173  */
1174  ret = odp_timer_cancel(tim, &event);
1175  if (ret == ODP_TIMER_SUCCESS) {
1176  void *ev_ctx = odp_timeout_user_ptr(odp_timeout_from_event(event));
1177 
1178  ODPH_ERR(
1179  "odp_timer_cancel() succeeded: event %p ctx %p sched event %p sched ctx %p\n",
1180  event, ev_ctx, ev, ctx);
1181  goto error;
1182  }
1183  }
1184 
1185  if (switch_timer) {
1186  /*
1187  * Switch timer.
1188  */
1189  odp_timer_t timer =
1190  odp_timer_alloc(test_global->timer_pool,
1191  test_global->queue[events % test_global->opt.num_queue],
1192  ctx);
1193  if (timer == ODP_TIMER_INVALID) {
1194  ODPH_ERR("odp_timer_alloc() failed\n");
1195  goto error;
1196  }
1197  if (odp_timer_free(tim)) {
1198  ODPH_ERR("odp_timer_free()\n");
1199  goto error;
1200  }
1201  tim = timer;
1202  ctx->timer = tim;
1203  }
1204 
1205  if (switch_event) {
1206  /*
1207  * Switch event.
1208  */
1209  odp_timeout_t timeout = odp_timeout_alloc(test_global->timeout_pool);
1210 
1211  if (timeout == ODP_TIMEOUT_INVALID) {
1212  ODPH_ERR("odp_timeout_alloc() failed\n");
1213  goto error;
1214  }
1215  odp_event_free(ev);
1216  ev = odp_timeout_to_event(timeout);
1217  }
1218 
1219  start_param.tick_type = ODP_TIMER_TICK_REL;
1220  start_param.tmo_ev = ev;
1221  start_param.tick = tick;
1222  ctx->starts++;
1223 
1224  ret = odp_timer_start(tim, &start_param);
1225  if (ret != ODP_TIMER_SUCCESS) {
1226  ODPH_ERR("odp_timer_start(): %d\n", ret);
1227  goto error;
1228  }
1229 
1230  if (cancel_start) {
1231  /*
1232  * Timer started, now immediately cancel and start again.
1233  */
1234  if (odp_timer_cancel(tim, &ev) == ODP_TIMER_SUCCESS) {
1235  odp_timeout_t timeout = odp_timeout_alloc(test_global->timeout_pool);
1236 
1237  if (timeout == ODP_TIMEOUT_INVALID) {
1238  ODPH_ERR("odp_timeout_alloc() failed\n");
1239  goto error;
1240  }
1241  odp_event_free(ev);
1242  ev = odp_timeout_to_event(timeout);
1243  start_param.tmo_ev = ev;
1244  ret = odp_timer_start(tim, &start_param);
1245  if (ret != ODP_TIMER_SUCCESS) {
1246  ODPH_ERR("odp_timer_start(): %d\n", ret);
1247  goto error;
1248  }
1249  }
1250  }
1251 
1252  if (locked)
1253  odp_ticketlock_unlock(&ctx->lock);
1254 
1255  return;
1256 
1257 error:
1258  if (locked)
1259  odp_ticketlock_unlock(&ctx->lock);
1260 
1261  _exit(1);
1262 }
1263 
1264 static int run_test(void *arg)
1265 {
1266  test_global_t *test_global = (test_global_t *)arg;
1267  odp_event_t ev;
1268  odp_time_t time;
1269  uint64_t time_ns, diff_ns;
1270  odp_timeout_t tmo;
1271  uint64_t tmo_ns;
1272  timer_ctx_t *ctx;
1273  odp_thrmask_t mask;
1274  uint64_t wait = odp_schedule_wait_time(10 * ODP_TIME_MSEC_IN_NS);
1276  test_log_t *log = test_global->log;
1277  enum mode_e mode = test_global->opt.mode;
1278  uint64_t tot_timers = test_global->opt.tot_timers;
1279  double period_dbl = test_global->period_dbl;
1280  odp_timer_pool_t tp = test_global->timer_pool;
1281  int tid = odp_thread_id();
1282 
1283  if (tid > test_global->opt.cpu_count) {
1284  ODPH_ERR("tid %d is larger than cpu_count %d.\n", tid, test_global->opt.cpu_count);
1285  return 0;
1286  }
1287 
1288  test_stat_t *stat = &test_global->stat[tid];
1289 
1290  memset(stat, 0, sizeof(*stat));
1291  stat->nsec_before_min = UINT64_MAX;
1292  stat->nsec_after_min = UINT64_MAX;
1293 
1294  if (test_global->opt.groups) {
1295  odp_thrmask_zero(&mask);
1296  odp_thrmask_set(&mask, tid);
1297  group = test_global->group[tid - 1];
1298 
1299  if (odp_schedule_group_join(group, &mask)) {
1300  ODPH_ERR("odp_schedule_group_join() failed\n");
1301  return 0;
1302  }
1303  }
1304 
1305  odp_barrier_wait(&test_global->barrier);
1306 
1307  while (1) {
1308  ev = odp_schedule(NULL, wait);
1309  time = odp_time_global_strict();
1310 
1311  if (ev == ODP_EVENT_INVALID) {
1312  if (mode == MODE_PERIODIC) {
1313  if (odp_atomic_load_u64(&test_global->last_events) >=
1314  test_global->opt.alloc_timers)
1315  break;
1316 
1317  } else if (odp_atomic_load_u64(&test_global->events) >= tot_timers) {
1318  break;
1319  }
1320 
1321  continue;
1322  }
1323 
1324  time_ns = odp_time_to_ns(time);
1325  tmo = odp_timeout_from_event(ev);
1326  ctx = odp_timeout_user_ptr(tmo);
1327  tmo_ns = ctx->nsec;
1328 
1329  if (mode == MODE_PERIODIC) {
1330  if (!ctx->events && !test_global->opt.offset_ns) {
1331  /*
1332  * If first_tick is zero, the API allows the implementation to
1333  * place the timer where it can, so we have to adjust our
1334  * expectation of the timeout time.
1335  */
1336 
1337  uint64_t tmo_tick = odp_timeout_tick(tmo);
1338 
1339  if (tmo_tick) {
1340  /*
1341  * Adjust by the difference between one period after start
1342  * time and the timeout tick.
1343  */
1344  ctx->tmo_tick = 1;
1345  ctx->first_tmo_diff =
1346  (int64_t)odp_timer_tick_to_ns(tp, tmo_tick) -
1347  (int64_t)odp_timer_tick_to_ns(tp, ctx->first_period);
1348  tmo_ns += ctx->first_tmo_diff;
1349  } else {
1350  /*
1351  * Timeout tick is not provided, so the best we can do is
1352  * to just take the current time as a baseline.
1353  */
1354  ctx->first_tmo_diff = (int64_t)time_ns - (int64_t)tmo_ns;
1355  tmo_ns = ctx->nsec = time_ns;
1356  }
1357 
1358  ctx->nsec = tmo_ns;
1359  }
1360 
1361  /* round to closest integer number */
1362  tmo_ns += ctx->events * period_dbl + 0.5;
1363  ctx->events++;
1364  } else if (mode == MODE_CONCURRENCY) {
1365  uint64_t events = ++ctx->events;
1366  uint64_t starts = ctx->starts;
1367 
1368  if (events > starts) {
1369  ODPH_ERR("ctx %p timer %p time %" PRIu64 " starts %" PRIu64
1370  " events %" PRIu64 "\n",
1371  ctx, ctx->timer, time_ns, starts, events);
1372  if (test_global->opt.exit_on_error)
1373  _exit(1);
1374  }
1375 
1376  int64_t diff = (int64_t)time_ns - (int64_t)tmo_ns;
1377 
1378  if (test_global->opt.max_diff &&
1379  diff > (int64_t)test_global->opt.max_diff) {
1380  ODPH_ERR("ctx %p timer %p time %" PRIu64 " diff %" PRIi64 "\n", ctx,
1381  ctx->timer, time_ns, diff);
1382  if (test_global->opt.exit_on_error)
1383  _exit(1);
1384  }
1385  }
1386 
1387  uint64_t events = odp_atomic_fetch_inc_u64(&test_global->events);
1388 
1389  if (events >= test_global->opt.warmup_timers && events < tot_timers) {
1390  uint64_t i = events - test_global->opt.warmup_timers;
1391 
1392  ctx->nsec_final = (int64_t)time_ns - (int64_t)tmo_ns;
1393 
1394  if (log) {
1395  log[i].tmo_ns = tmo_ns;
1396  log[i].tid = tid;
1397  }
1398 
1399  if (time_ns > tmo_ns) {
1400  diff_ns = time_ns - tmo_ns;
1401  stat->num_after++;
1402  stat->nsec_after_sum += diff_ns;
1403  if (diff_ns < stat->nsec_after_min) {
1404  stat->nsec_after_min = diff_ns;
1405  stat->nsec_after_min_idx = i;
1406  }
1407  if (diff_ns > stat->nsec_after_max) {
1408  stat->nsec_after_max = diff_ns;
1409  stat->nsec_after_max_idx = i;
1410  }
1411  if (log)
1412  log[i].diff_ns = diff_ns;
1413 
1414  } else if (time_ns < tmo_ns) {
1415  diff_ns = tmo_ns - time_ns;
1416  stat->num_before++;
1417  stat->nsec_before_sum += diff_ns;
1418  if (diff_ns < stat->nsec_before_min) {
1419  stat->nsec_before_min = diff_ns;
1420  stat->nsec_before_min_idx = i;
1421  }
1422  if (diff_ns > stat->nsec_before_max) {
1423  stat->nsec_before_max = diff_ns;
1424  stat->nsec_before_max_idx = i;
1425  }
1426  if (log)
1427  log[i].diff_ns = -diff_ns;
1428  } else {
1429  stat->num_exact++;
1430  }
1431  }
1432 
1433  if ((mode == MODE_RESTART_ABS || mode == MODE_RESTART_REL) &&
1434  events < tot_timers - 1) {
1435  /* Reset timer for next period */
1436  odp_timer_t tim;
1437  uint64_t nsec, tick;
1439  unsigned int j;
1440  unsigned int retries = test_global->opt.early_retry;
1441  uint64_t start_ns = test_global->start_ns;
1442  uint64_t period_ns = test_global->opt.period_ns;
1443  odp_timer_start_t start_param;
1444 
1445  tim = ctx->timer;
1446 
1447  /* Depending on the option, retry when expiration
1448  * time is too early */
1449  for (j = 0; j < retries + 1; j++) {
1450  if (mode == MODE_RESTART_ABS) {
1451  /* Absolute time */
1452  ctx->nsec += period_ns;
1453  nsec = ctx->nsec - start_ns;
1454  tick = test_global->start_tick +
1455  odp_timer_ns_to_tick(tp, nsec);
1456  start_param.tick_type = ODP_TIMER_TICK_ABS;
1457  } else {
1458  /* Relative time */
1459  tick = test_global->period_tick;
1460  time = odp_time_local();
1461  time_ns = odp_time_to_ns(time);
1462  ctx->nsec = time_ns + period_ns;
1463  start_param.tick_type = ODP_TIMER_TICK_REL;
1464  }
1465 
1466  start_param.tmo_ev = ev;
1467  start_param.tick = tick;
1468 
1469  ret = odp_timer_start(tim, &start_param);
1470  if (ret == ODP_TIMER_TOO_NEAR) {
1471  if (events >= test_global->opt.warmup_timers)
1472  stat->num_too_near++;
1473  } else {
1474  break;
1475  }
1476  }
1477 
1478  if (ret != ODP_TIMER_SUCCESS) {
1479  ODPH_ERR("Timer set failed: %i. Timeout nsec "
1480  "%" PRIu64 "\n",
1481  ret, ctx->nsec);
1482  return 0;
1483  }
1484  } else if (mode == MODE_PERIODIC) {
1485  int ret = odp_timer_periodic_ack(ctx->timer, ev);
1486 
1487  if (ret < 0)
1488  ODPH_ERR("Failed to ack a periodic timer.\n");
1489 
1490  if (ret == 2)
1491  odp_atomic_inc_u64(&test_global->last_events);
1492 
1493  if (ret == 2 || ret < 0)
1494  odp_event_free(ev);
1495  } else if (mode == MODE_CONCURRENCY && events < tot_timers - 1) {
1496  process_event_concurrency(events, test_global, ctx, time_ns, ev);
1497  } else {
1498  odp_event_free(ev);
1499  }
1500  }
1501 
1502  if (test_global->opt.groups) {
1503  if (odp_schedule_group_leave(group, &mask))
1504  ODPH_ERR("odp_schedule_group_leave() failed\n");
1505  }
1506 
1507  return 0;
1508 }
1509 
1510 static void interval_loop_concurrency(test_global_t *test_global)
1511 {
1512  uint64_t events_prev = 0;
1513  uint64_t events = 0;
1514  uint64_t start_ns = odp_time_global_strict_ns();
1515  uint64_t prev_ns = start_ns;
1516 
1517  while (events < test_global->opt.tot_timers) {
1518  odp_time_wait_ns(test_global->opt.interval * ODP_TIME_SEC_IN_NS);
1519 
1520  events = odp_atomic_load_u64(&test_global->events);
1521 
1522  uint64_t ns = odp_time_global_strict_ns();
1523  uint64_t msec = (ns - prev_ns) / ODP_TIME_MSEC_IN_NS;
1524  uint64_t sec_total = (ns - start_ns) / ODP_TIME_SEC_IN_NS;
1525  uint64_t e = events - events_prev;
1526 
1527  printf("sec %" PRIu64 " total events %" PRIu64 " events %" PRIu64
1528  " events/s %" PRIu64 "\n", sec_total, events, e, e * 1000 / msec);
1529  events_prev = events;
1530  prev_ns = ns;
1531  }
1532 }
1533 
1534 int main(int argc, char *argv[])
1535 {
1536  odp_instance_t instance;
1537  odp_init_t init;
1538  test_opt_t test_opt;
1539  test_global_t *test_global;
1540  odph_helper_options_t helper_options;
1541  test_common_options_t common_options;
1542  odp_init_t *init_ptr = NULL;
1543  int ret = 0;
1544 
1545  /* Let helper collect its own arguments (e.g. --odph_proc) */
1546  argc = odph_parse_options(argc, argv);
1547  if (odph_options(&helper_options)) {
1548  ODPH_ERR("Reading ODP helper options failed.\n");
1549  exit(EXIT_FAILURE);
1550  }
1551 
1552  argc = test_common_parse_options(argc, argv);
1553  if (test_common_options(&common_options)) {
1554  ODPH_ERR("Reading test options failed\n");
1555  exit(EXIT_FAILURE);
1556  }
1557 
1558  if (parse_options(argc, argv, &test_opt))
1559  return -1;
1560 
1561  /* List features not to be used (may optimize performance) */
1562  odp_init_param_init(&init);
1563  init.not_used.feat.cls = 1;
1564  init.not_used.feat.compress = 1;
1565  init.not_used.feat.crypto = 1;
1566  init.not_used.feat.ipsec = 1;
1567  init.not_used.feat.tm = 1;
1568 
1569  init.mem_model = helper_options.mem_model;
1570 
1571  if (test_opt.init)
1572  init_ptr = &init;
1573 
1574  /* Init ODP before calling anything else */
1575  if (odp_init_global(&instance, init_ptr, NULL)) {
1576  ODPH_ERR("Global init failed.\n");
1577  return -1;
1578  }
1579 
1580  /* Init this thread */
1581  if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
1582  ODPH_ERR("Local init failed.\n");
1583  return -1;
1584  }
1585 
1587 
1588  /* Configure scheduler */
1589  odp_schedule_config(NULL);
1590 
1591  odp_shm_t shm = ODP_SHM_INVALID, shm_ctx = ODP_SHM_INVALID, shm_log = ODP_SHM_INVALID;
1592  uint64_t size = sizeof(test_global_t);
1593 
1594  shm = odp_shm_reserve("timer_accuracy", size,
1595  ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA);
1596 
1597  if (shm == ODP_SHM_INVALID) {
1598  ODPH_ERR("Shm alloc failed.\n");
1599  return -1;
1600  }
1601 
1602  test_global = odp_shm_addr(shm);
1603  memset(test_global, 0, size);
1604  memcpy(&test_global->opt, &test_opt, sizeof(test_opt_t));
1605 
1606  test_global->common_options = common_options;
1607 
1608  size = test_global->opt.alloc_timers * sizeof(timer_ctx_t);
1609  shm_ctx = odp_shm_reserve("timer_accuracy_ctx", size,
1610  ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA);
1611 
1612  if (shm_ctx == ODP_SHM_INVALID) {
1613  ODPH_ERR("Timer context alloc failed.\n");
1614  ret = -1;
1615  goto quit;
1616  }
1617 
1618  test_global->timer_ctx = odp_shm_addr(shm_ctx);
1619  memset(test_global->timer_ctx, 0, size);
1620 
1621  if (test_global->opt.output) {
1622  test_global->file = fopen(test_global->opt.filename, "w");
1623  if (test_global->file == NULL) {
1624  ODPH_ERR("Failed to open output file %s: %s\n", test_global->opt.filename,
1625  strerror(errno));
1626  ret = -1;
1627  goto quit;
1628  }
1629 
1630  size = (test_global->opt.tot_timers - test_global->opt.warmup_timers) *
1631  sizeof(test_log_t);
1632  shm_log = odp_shm_reserve("timer_accuracy_log", size, sizeof(test_log_t),
1634 
1635  if (shm_log == ODP_SHM_INVALID) {
1636  ODPH_ERR("Test log alloc failed.\n");
1637  ret = -1;
1638  goto quit;
1639  }
1640 
1641  test_global->log = odp_shm_addr(shm_log);
1642  memset(test_global->log, 0, size);
1643  }
1644 
1645  odph_thread_t thread_tbl[MAX_WORKERS];
1646  int num_workers;
1647  odp_cpumask_t cpumask;
1648  char cpumaskstr[ODP_CPUMASK_STR_SIZE];
1649  odph_thread_common_param_t thr_common;
1650  odph_thread_param_t thr_param;
1651 
1652  memset(thread_tbl, 0, sizeof(thread_tbl));
1653 
1654  num_workers = MAX_WORKERS;
1655  if (test_global->opt.cpu_count && test_global->opt.cpu_count < MAX_WORKERS)
1656  num_workers = test_global->opt.cpu_count;
1657  num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
1658  test_global->opt.cpu_count = num_workers;
1659  odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
1660 
1661  printf("num worker threads: %i\n", num_workers);
1662  printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
1663  printf("cpu mask: %s\n", cpumaskstr);
1664 
1665  ret = create_timers(test_global);
1666  if (ret)
1667  goto quit;
1668 
1669  odp_barrier_init(&test_global->barrier, num_workers + 1);
1670  odp_atomic_init_u64(&test_global->events, 0);
1671  odp_atomic_init_u64(&test_global->last_events, 0);
1672 
1673  odph_thread_param_init(&thr_param);
1674  thr_param.start = run_test;
1675  thr_param.arg = (void *)test_global;
1676  thr_param.thr_type = ODP_THREAD_WORKER;
1677 
1678  odph_thread_common_param_init(&thr_common);
1679  thr_common.instance = instance;
1680  thr_common.cpumask = &cpumask;
1681  thr_common.share_param = 1;
1682 
1683  odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
1684  odp_barrier_wait(&test_global->barrier);
1685 
1686  ret = start_timers(test_global);
1687  if (ret)
1688  goto quit;
1689 
1690  if (test_global->opt.mode == MODE_PERIODIC) {
1691  while (odp_atomic_load_u64(&test_global->events) < test_global->opt.tot_timers)
1693 
1694  cancel_periodic_timers(test_global);
1695  } else if (test_global->opt.mode == MODE_CONCURRENCY) {
1696  interval_loop_concurrency(test_global);
1697  }
1698 
1699  odph_thread_join(thread_tbl, num_workers);
1700 
1701  ret = print_stat(test_global);
1702  if (ret)
1703  goto quit;
1704 
1705 quit:
1706  if (test_global->file)
1707  fclose(test_global->file);
1708 
1709  if (destroy_timers(test_global))
1710  ret = -1;
1711 
1712  if (shm_log != ODP_SHM_INVALID && odp_shm_free(shm_log))
1713  ret = -1;
1714 
1715  if (shm_ctx != ODP_SHM_INVALID && odp_shm_free(shm_ctx))
1716  ret = -1;
1717 
1718  if (odp_shm_free(shm))
1719  ret = -1;
1720 
1721  if (odp_term_local()) {
1722  ODPH_ERR("Term local failed.\n");
1723  ret = -1;
1724  }
1725 
1726  if (odp_term_global(instance)) {
1727  ODPH_ERR("Term global failed.\n");
1728  ret = -1;
1729  }
1730 
1731  return ret;
1732 }
void odp_atomic_init_u64(odp_atomic_u64_t *atom, uint64_t val)
Initialize atomic uint64 variable.
void odp_atomic_inc_u64(odp_atomic_u64_t *atom)
Increment atomic uint64 variable.
uint64_t odp_atomic_fetch_inc_u64(odp_atomic_u64_t *atom)
Fetch and increment atomic uint64 variable.
uint64_t odp_atomic_load_u64(odp_atomic_u64_t *atom)
Load value of atomic uint64 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_mb_full(void)
Full memory barrier.
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.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_cpumask_first(const odp_cpumask_t *mask)
Find first set CPU in mask.
int32_t odp_cpumask_to_str(const odp_cpumask_t *mask, char *str, int32_t size)
Format a string from CPU mask.
#define ODP_CPUMASK_STR_SIZE
The maximum number of characters needed to record any CPU mask as a string (output of odp_cpumask_to_...
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.
void odp_ticketlock_init(odp_ticketlock_t *tklock)
Initialize ticket lock.
void odp_ticketlock_lock(odp_ticketlock_t *tklock)
Acquire ticket lock.
void odp_ticketlock_unlock(odp_ticketlock_t *tklock)
Release ticket lock.
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.
odp_queue_type_t
Queue type.
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_SYNC_PARALLEL
Parallel scheduled queues.
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.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
int odp_schedule_group_destroy(odp_schedule_group_t group)
Schedule group destroy.
int odp_schedule_group_leave(odp_schedule_group_t group, const odp_thrmask_t *mask)
Leave a schedule group.
#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_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.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#define ODP_SHM_SINGLE_VA
Single virtual address.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
double odp_fract_u64_to_dbl(const odp_fract_u64_t *fract)
Convert fractional number (u64) to double.
void odp_sys_info_print(void)
Print system info.
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.
uint64_t odp_time_to_ns(odp_time_t time)
Convert time to nanoseconds.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_global(void)
Current global time.
odp_time_t odp_time_local(void)
Current local time.
uint64_t odp_time_global_strict_ns(void)
Current global time in nanoseconds (strict)
odp_time_t odp_time_global_strict(void)
Current global time (strict)
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
uint64_t odp_time_global_ns(void)
Current global time in nanoseconds.
uint64_t odp_timer_tick_to_ns(odp_timer_pool_t timer_pool, uint64_t ticks)
Convert timer ticks to nanoseconds.
int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num)
Start timer pools.
void odp_timer_pool_print(odp_timer_pool_t timer_pool)
Print timer pool debug information.
odp_timeout_t odp_timeout_alloc(odp_pool_t pool)
Timeout alloc.
uint64_t odp_timeout_tick(odp_timeout_t tmo)
Timeout expiration tick.
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.
int odp_timer_cancel(odp_timer_t timer, odp_event_t *tmo_ev)
Cancel a timer.
uint64_t odp_timer_current_tick(odp_timer_pool_t timer_pool)
Current tick value.
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 timer.
odp_timer_retval_t
Return values for timer start, restart and cancel calls.
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.
#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.
void * odp_timeout_user_ptr(odp_timeout_t tmo)
Return user pointer for the timeout.
@ 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_TICK_REL
Relative ticks.
@ ODP_TIMER_TICK_ABS
Absolute 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.
odp_feature_t not_used
Unused features.
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.
odp_schedule_sync_t sync
Synchronization method.
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.
odp_timer_res_capability_t max_res
Maximum resolution.
struct odp_timer_capability_t::@156 periodic
Periodic timer capabilities.
Periodic timer capability.
odp_fract_u64_t base_freq_hz
Periodic timer pool base frequency in hertz.
uint64_t res_ns
Timeout resolution in nanoseconds.
uint64_t max_multiplier
Maximum base frequency multiplier.
Periodic timer start parameters.
odp_event_t tmo_ev
Timeout event.
uint64_t freq_multiplier
Base frequency multiplier.
uint64_t first_tick
First expiration time.
Timer pool parameters.
uint64_t res_ns
Timeout resolution in nanoseconds.
uint64_t res_hz
Timeout resolution in hertz.
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.
uint64_t res_hz
Timeout resolution in hertz.
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.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
struct odp_feature_t::@148 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()