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