API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_pool_latency.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2024 Nokia
3 */
4
14#ifndef _GNU_SOURCE
15#define _GNU_SOURCE
16#endif
17
18#include <inttypes.h>
19#include <signal.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23
24#include <odp_api.h>
25#include <odp/helper/odph_api.h>
26
27#define PROG_NAME "odp_pool_latency"
28#define DELIMITER ","
29#define ALLOC '+'
30#define FREE '-'
31#define TOP 't'
32#define BOTTOM 'b'
33#define DELAY 'd'
34
35enum {
36 BUFFER = 0U,
37 PACKET,
38 TMO,
39 VECTOR
40};
41
42enum {
43 SINGLE = 0U,
44 MANY
45};
46
47#define DEF_ALLOC 1U
48#define DEF_FREE 1U
49#define DEF_DIR TOP
50#define DEF_TYPE BUFFER
51#define DEF_CNT 32768U
52#define DEF_SIZE 1024U
53#define DEF_POLICY MANY
54#define DEF_ROUNDS 100000U
55#define DEF_IGNORE 0U
56#define DEF_WORKERS 1U
57#define DEF_UA_SIZE 0U
58
59#define MAX_PATTERN_LEN 32U
60#define MAX_WORKERS ((uint32_t)(ODP_THREAD_COUNT_MAX - 1))
61#define MAX_RETRIES 10U
62
63#define COND_MIN(a, b) ((a) > 0U ? ODPH_MIN((a), (b)) : (b))
64#define UA_DATA 0xAA
65
66ODP_STATIC_ASSERT(MAX_PATTERN_LEN < UINT8_MAX, "Too long pattern length");
67
68typedef struct {
69 uint32_t num_evs_buf;
70 uint32_t num_evs_pkt;
71 uint32_t num_evs_tmo;
72 uint32_t num_evs_vec;
73 uint32_t data_size_buf;
74 uint32_t data_size_pkt;
75 uint32_t data_size_vec;
76 uint32_t cache_size_buf;
77 uint32_t cache_size_pkt;
78 uint32_t cache_size_tmo;
79 uint32_t cache_size_vec;
80} dynamic_defs_t;
81
82typedef enum {
83 PRS_OK,
84 PRS_NOK,
85 PRS_TERM
86} parse_result_t;
87
88typedef struct {
89 uint64_t tot_tm;
90 uint64_t alloc_tm;
91 uint64_t max_alloc_tm;
92 uint64_t min_alloc_tm;
93 uint64_t max_alloc_rnd;
94 uint64_t min_alloc_rnd;
95 uint64_t alloc_cnt;
96 uint64_t alloc_b_cnt;
97 uint64_t uarea_tm;
98 uint64_t max_uarea_tm;
99 uint64_t min_uarea_tm;
100 uint64_t max_uarea_rnd;
101 uint64_t min_uarea_rnd;
102 uint64_t free_tm;
103 uint64_t max_free_tm;
104 uint64_t min_free_tm;
105 uint64_t max_free_rnd;
106 uint64_t min_free_rnd;
107 uint64_t free_b_cnt;
108 uint64_t reallocs;
109 uint64_t alloc_errs;
110 uint64_t pattern_errs;
111 uint64_t act_num_rounds;
112 uint8_t max_alloc_pt;
113 uint8_t min_alloc_pt;
114 uint8_t max_uarea_pt;
115 uint8_t min_uarea_pt;
116 uint8_t max_free_pt;
117 uint8_t min_free_pt;
118} stats_t;
119
120typedef struct {
121 uint32_t val;
122 uint8_t op;
123 uint8_t opt;
124} alloc_elem_t;
125
126typedef struct prog_config_s prog_config_t;
127
128typedef struct ODP_ALIGNED_CACHE {
129 stats_t stats;
130 odp_pool_t pool;
131 void *data;
132 prog_config_t *prog_config;
133 odp_shm_t shm;
134 uint32_t data_size;
135 uint32_t uarea_size;
136} worker_config_t;
137
138typedef uint32_t (*alloc_fn_t)(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
139 uint64_t round, uint8_t pattern, odp_bool_t is_saved);
140typedef void (*free_fn_t)(void *data, uint32_t idx, uint32_t num, stats_t *stats,
141 uint64_t round, uint8_t pattern, odp_bool_t is_saved);
142
143typedef struct prog_config_s {
144 odph_thread_t thread_tbl[MAX_WORKERS];
145 worker_config_t worker_config[MAX_WORKERS];
146 alloc_elem_t alloc_elems[MAX_PATTERN_LEN];
147 dynamic_defs_t dyn_defs;
149 odp_cpumask_t worker_mask;
150 odp_barrier_t init_barrier;
151 odp_barrier_t term_barrier;
152 alloc_fn_t alloc_fn;
153 free_fn_t free_fn;
154 int64_t cache_size;
155 uint64_t num_rounds;
156 uint64_t num_ignore;
157 odp_atomic_u32_t is_running;
158 uint32_t num_data_elems;
159 uint32_t seg_len;
160 uint32_t handle_size;
161 uint32_t num_evs;
162 uint32_t data_size;
163 uint32_t num_workers;
164 uint32_t uarea_size;
165 uint8_t num_elems;
166 uint8_t type;
167 uint8_t policy;
168} prog_config_t;
169
170static prog_config_t *prog_conf;
171
172static void terminate(int signal ODP_UNUSED)
173{
174 odp_atomic_store_u32(&prog_conf->is_running, 0U);
175}
176
177static void init_config(prog_config_t *config)
178{
179 alloc_elem_t *alloc_elem;
181 odp_pool_param_t param;
182 worker_config_t *worker;
183
184 memset(config, 0, sizeof(*config));
185 alloc_elem = &config->alloc_elems[0];
186 alloc_elem->val = DEF_ALLOC;
187 alloc_elem->op = ALLOC;
188 alloc_elem = &config->alloc_elems[1];
189 alloc_elem->val = DEF_FREE;
190 alloc_elem->op = FREE;
191 alloc_elem->opt = DEF_DIR;
192 config->num_elems = 2U;
193
194 if (odp_pool_capability(&capa) == 0) {
195 config->dyn_defs.num_evs_buf = COND_MIN(capa.buf.max_num, DEF_CNT);
196 config->dyn_defs.num_evs_pkt = COND_MIN(capa.pkt.max_num, DEF_CNT);
197 config->dyn_defs.num_evs_tmo = COND_MIN(capa.tmo.max_num, DEF_CNT);
198 config->dyn_defs.num_evs_vec = COND_MIN(capa.vector.max_num, DEF_CNT);
199 config->dyn_defs.data_size_buf = COND_MIN(capa.buf.max_size, DEF_SIZE);
200 config->dyn_defs.data_size_pkt = COND_MIN(capa.pkt.max_len, DEF_SIZE);
201 config->dyn_defs.data_size_vec = COND_MIN(capa.vector.max_size, DEF_SIZE);
202 odp_pool_param_init(&param);
203 config->dyn_defs.cache_size_buf = param.buf.cache_size;
204 config->dyn_defs.cache_size_pkt = param.pkt.cache_size;
205 config->dyn_defs.cache_size_tmo = param.tmo.cache_size;
206 config->dyn_defs.cache_size_vec = param.vector.cache_size;
207 }
208
209 config->cache_size = -1;
210 config->num_rounds = DEF_ROUNDS;
211 config->num_ignore = DEF_IGNORE;
212 config->num_workers = DEF_WORKERS;
213 config->uarea_size = DEF_UA_SIZE;
214 config->type = DEF_TYPE;
215 config->policy = DEF_POLICY;
216
217 for (uint32_t i = 0U; i < MAX_WORKERS; ++i) {
218 worker = &config->worker_config[i];
219 worker->stats.min_alloc_tm = UINT64_MAX;
220 worker->stats.min_uarea_tm = UINT64_MAX;
221 worker->stats.min_free_tm = UINT64_MAX;
222 worker->pool = ODP_POOL_INVALID;
223 worker->shm = ODP_SHM_INVALID;
224 }
225}
226
227static void parse_burst_pattern(prog_config_t *config, const char *optarg)
228{
229 char *tmp_str = strdup(optarg), *tmp, op, opt;
230 uint8_t num_elems = 0U;
231 alloc_elem_t *elem;
232 uint32_t val;
233 int ret;
234
235 if (tmp_str == NULL)
236 return;
237
238 tmp = strtok(tmp_str, DELIMITER);
239
240 while (tmp && num_elems < MAX_PATTERN_LEN) {
241 elem = &config->alloc_elems[num_elems];
242 ret = sscanf(tmp, "%c%u%c", &op, &val, &opt);
243
244 if (ret == 2 || ret == 3) {
245 if (op == ALLOC || (op == FREE && (opt == TOP || opt == BOTTOM)) ||
246 op == DELAY) {
247 if (op == FREE)
248 elem->opt = opt;
249
250 elem->val = val;
251 elem->op = op;
252 ++num_elems;
253 }
254 }
255
256 tmp = strtok(NULL, DELIMITER);
257 }
258
259 free(tmp_str);
260 config->num_elems = num_elems;
261}
262
263static void print_usage(const dynamic_defs_t *dyn_defs)
264{
265 printf("\n"
266 "Pool latency tester. Allocate from different kind of pools with a varying set of\n"
267 "configurations and record latencies.\n"
268 "\n"
269 "Usage: " PROG_NAME " [OPTIONS]\n");
270 printf("\n"
271 " E.g. " PROG_NAME "\n"
272 " " PROG_NAME " -b %c7" DELIMITER "%c1%c" DELIMITER "%c3" DELIMITER "%c9%c\n",
273 ALLOC, FREE, TOP, ALLOC, FREE, BOTTOM);
274 printf(" " PROG_NAME " -b %c10" DELIMITER "%c1000" DELIMITER "%c10%c -t 1 -d 2048 "
275 "-p 0 -w 64\n", ALLOC, DELAY, FREE, TOP);
276 printf("\n"
277 "Optional OPTIONS:\n"
278 "\n"
279 " -b, --burst_pattern Burst pattern for allocations, frees and delays per round,\n"
280 " delimited by '%s', no spaces. Allocations are indicated\n"
281 " with a '%c' prefix, frees with a '%c' prefix. The location\n"
282 " of frees are indicated from the top of a previously\n"
283 " allocated array of events with a '%c' suffix and from the\n"
284 " bottom with a '%c' suffix. Delays are indicated with a\n"
285 " '%c' prefix, followed by a delay in nanoseconds.\n"
286 " Allocations and frees should be equal in the aggregate and\n"
287 " frees should never outnumber allocations at any instant.\n"
288 " '%c%u%s%c%u%c' by default. Maximum pattern length is %u.\n"
289 " -t, --type Pool type. %u by default.\n"
290 " 0: buffer\n"
291 " 1: packet\n"
292 " 2: timeout\n"
293 " 3: vector\n"
294 " -e, --event_count Number of events. Defaults:\n"
295 " buffer: %u\n"
296 " packet: %u\n"
297 " timeout: %u\n"
298 " vector: %u\n"
299 " -d, --data_size Data size in bytes, ignored in case of timeout pools, with\n"
300 " vector pools, defines the vector size.\n"
301 " Defaults:\n"
302 " buffer: %u\n"
303 " packet: %u\n"
304 " vector: %u\n"
305 " -p, --policy Pool allocation policy. %u by default.\n"
306 " Policies:\n"
307 " 0: One pool shared by workers\n"
308 " 1: One pool per worker\n"
309 " -r, --round_count Number of rounds to run. Use 0 to run indefinitely. %u by\n"
310 " default.\n"
311 " -i, --ignore_rounds Ignore an amount of initial rounds. %u by default.\n"
312 " -c, --worker_count Number of workers. %u by default.\n"
313 " -C, --cache_size Maximum cache size for pools. Defaults:\n"
314 " buffer: %u\n"
315 " packet: %u\n"
316 " timeout: %u\n"
317 " vector: %u\n"
318 " -w, --write_uarea Write data to allocated event user areas. 0 bytes disables\n"
319 " user area write. %u by default.\n"
320 " -h, --help This help.\n"
321 "\n", DELIMITER, ALLOC, FREE, TOP, BOTTOM, DELAY, ALLOC, DEF_ALLOC, DELIMITER, FREE,
322 DEF_FREE, DEF_DIR, MAX_PATTERN_LEN, DEF_TYPE, dyn_defs->num_evs_buf,
323 dyn_defs->num_evs_pkt, dyn_defs->num_evs_tmo, dyn_defs->num_evs_vec,
324 dyn_defs->data_size_buf, dyn_defs->data_size_pkt, dyn_defs->data_size_vec,
325 DEF_POLICY, DEF_ROUNDS, DEF_IGNORE, DEF_WORKERS, dyn_defs->cache_size_buf,
326 dyn_defs->cache_size_pkt, dyn_defs->cache_size_tmo, dyn_defs->cache_size_vec,
327 DEF_UA_SIZE);
328}
329
330static parse_result_t check_options(prog_config_t *config)
331{
332 odp_pool_capability_t pool_capa;
333 uint32_t max_workers, num_pools;
334 alloc_elem_t *elem;
335 int64_t num_tot = 0;
336 odp_shm_capability_t shm_capa;
337 uint64_t shm_size;
338
339 if (config->type != BUFFER && config->type != PACKET && config->type != TMO &&
340 config->type != VECTOR) {
341 ODPH_ERR("Invalid pool type: %u\n", config->type);
342 return PRS_NOK;
343 }
344
345 if (odp_pool_capability(&pool_capa) < 0) {
346 ODPH_ERR("Error querying pool capabilities\n");
347 return PRS_NOK;
348 }
349
350 max_workers = ODPH_MIN(MAX_WORKERS, (uint32_t)odp_cpumask_default_worker(NULL, 0));
351
352 if (config->num_workers == 0U || config->num_workers > max_workers) {
353 ODPH_ERR("Invalid worker count: %u (min: 1, max: %u)\n", config->num_workers,
354 max_workers);
355 return PRS_NOK;
356 }
357
358 (void)odp_cpumask_default_worker(&config->worker_mask, config->num_workers);
359 num_pools = config->policy == SINGLE ? 1U : config->num_workers;
360
361 if (config->type == BUFFER) {
362 if (config->num_evs == 0U)
363 config->num_evs = config->dyn_defs.num_evs_buf;
364
365 if (config->data_size == 0U)
366 config->data_size = config->dyn_defs.data_size_buf;
367
368 if (config->cache_size == -1)
369 config->cache_size = config->dyn_defs.cache_size_buf;
370
371 if (config->num_evs > pool_capa.buf.max_num) {
372 ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
373 pool_capa.buf.max_num);
374 return PRS_NOK;
375 }
376
377 if (config->data_size > pool_capa.buf.max_size) {
378 ODPH_ERR("Invalid data size: %u (max: %u)\n", config->data_size,
379 pool_capa.buf.max_size);
380 return PRS_NOK;
381 }
382
383 if (config->cache_size < pool_capa.buf.min_cache_size ||
384 config->cache_size > pool_capa.buf.max_cache_size) {
385 ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
386 config->cache_size, pool_capa.buf.min_cache_size,
387 pool_capa.buf.max_cache_size);
388 return PRS_NOK;
389 }
390
391 if (num_pools > pool_capa.buf.max_pools) {
392 ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
393 pool_capa.buf.max_pools);
394 return PRS_NOK;
395 }
396
397 config->handle_size = sizeof(odp_buffer_t);
398 config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.buf.max_uarea_size);
399 } else if (config->type == PACKET) {
400 if (config->num_evs == 0U)
401 config->num_evs = config->dyn_defs.num_evs_pkt;
402
403 if (config->data_size == 0U)
404 config->data_size = config->dyn_defs.data_size_pkt;
405
406 if (config->cache_size == -1)
407 config->cache_size = config->dyn_defs.cache_size_pkt;
408
409 if (config->num_evs > pool_capa.pkt.max_num) {
410 ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
411 pool_capa.pkt.max_num);
412 return PRS_NOK;
413 }
414
415 if (config->data_size > pool_capa.pkt.max_len) {
416 ODPH_ERR("Invalid data size: %u (max: %u)\n", config->data_size,
417 pool_capa.pkt.max_len);
418 return PRS_NOK;
419 }
420
421 if (config->cache_size < pool_capa.pkt.min_cache_size ||
422 config->cache_size > pool_capa.pkt.max_cache_size) {
423 ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
424 config->cache_size, pool_capa.pkt.min_cache_size,
425 pool_capa.pkt.max_cache_size);
426 return PRS_NOK;
427 }
428
429 if (num_pools > pool_capa.pkt.max_pools) {
430 ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
431 pool_capa.pkt.max_pools);
432 return PRS_NOK;
433 }
434
435 config->seg_len = pool_capa.pkt.max_seg_len > config->data_size ?
436 config->data_size : pool_capa.pkt.max_seg_len;
437 config->handle_size = sizeof(odp_packet_t);
438 config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.pkt.max_uarea_size);
439 } else if (config->type == TMO) {
440 if (config->num_evs == 0U)
441 config->num_evs = config->dyn_defs.num_evs_tmo;
442
443 if (config->cache_size == -1)
444 config->cache_size = config->dyn_defs.cache_size_tmo;
445
446 if (config->num_evs > pool_capa.tmo.max_num) {
447 ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
448 pool_capa.tmo.max_num);
449 return PRS_NOK;
450 }
451
452 if (config->cache_size < pool_capa.tmo.min_cache_size ||
453 config->cache_size > pool_capa.tmo.max_cache_size) {
454 ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
455 config->cache_size, pool_capa.tmo.min_cache_size,
456 pool_capa.tmo.max_cache_size);
457 return PRS_NOK;
458 }
459
460 if (num_pools > pool_capa.tmo.max_pools) {
461 ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
462 pool_capa.tmo.max_pools);
463 return PRS_NOK;
464 }
465
466 config->handle_size = sizeof(odp_timeout_t);
467 config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.tmo.max_uarea_size);
468 } else {
469 if (config->num_evs == 0U)
470 config->num_evs = config->dyn_defs.num_evs_vec;
471
472 if (config->data_size == 0U)
473 config->data_size = config->dyn_defs.data_size_vec;
474
475 if (config->cache_size == -1)
476 config->cache_size = config->dyn_defs.cache_size_vec;
477
478 if (config->num_evs > pool_capa.vector.max_num) {
479 ODPH_ERR("Invalid event count: %u (max: %u)\n", config->num_evs,
480 pool_capa.vector.max_num);
481 return PRS_NOK;
482 }
483
484 if (config->data_size > pool_capa.vector.max_size) {
485 ODPH_ERR("Invalid vector size: %u (max: %u)\n", config->data_size,
486 pool_capa.vector.max_size);
487 return PRS_NOK;
488 }
489
490 if (config->cache_size < pool_capa.vector.min_cache_size ||
491 config->cache_size > pool_capa.vector.max_cache_size) {
492 ODPH_ERR("Invalid cache size: %" PRIi64 " (min: %u, max: %u)\n",
493 config->cache_size, pool_capa.vector.min_cache_size,
494 pool_capa.vector.max_cache_size);
495 return PRS_NOK;
496 }
497
498 if (num_pools > pool_capa.vector.max_pools) {
499 ODPH_ERR("Invalid pool count: %u (max: %u)\n", num_pools,
500 pool_capa.vector.max_pools);
501 return PRS_NOK;
502 }
503
504 config->handle_size = sizeof(odp_packet_vector_t);
505 config->uarea_size = ODPH_MIN(config->uarea_size, pool_capa.vector.max_uarea_size);
506 }
507
508 if (config->num_elems == 0U) {
509 ODPH_ERR("Invalid burst pattern, no elements\n");
510 return PRS_NOK;
511 }
512
513 for (uint8_t i = 0U; i < config->num_elems; ++i) {
514 elem = &config->alloc_elems[i];
515
516 if (elem->op == ALLOC)
517 num_tot += elem->val;
518 else if (elem->op == FREE)
519 num_tot -= elem->val;
520
521 if (num_tot < 0) {
522 ODPH_ERR("Invalid burst pattern, frees exceed allocations "
523 "instantaneously\n");
524 return PRS_NOK;
525 }
526
527 config->num_data_elems += (elem->op == ALLOC ? elem->val : 0U);
528 }
529
530 if (num_tot != 0) {
531 ODPH_ERR("Invalid burst pattern, cumulative sum not zero: %" PRId64 "\n", num_tot);
532 return PRS_NOK;
533 }
534
535 if (odp_shm_capability(&shm_capa) < 0) {
536 ODPH_ERR("Error querying SHM capabilities\n");
537 return PRS_NOK;
538 }
539
540 if (shm_capa.max_blocks < config->num_workers + 1U) {
541 ODPH_ERR("Invalid amount of SHM blocks: %u (max: %u)\n", config->num_workers + 1U,
542 shm_capa.max_blocks);
543 return PRS_NOK;
544 }
545
546 shm_size = (uint64_t)config->num_data_elems * config->handle_size;
547
548 if (shm_capa.max_size != 0U && shm_size > shm_capa.max_size) {
549 ODPH_ERR("Invalid total SHM block size: %" PRIu64 " (max: %" PRIu64 ")\n",
550 shm_size, shm_capa.max_size);
551 return PRS_NOK;
552 }
553
554 if (config->policy != SINGLE && config->policy != MANY) {
555 ODPH_ERR("Invalid pool policy: %u\n", config->policy);
556 return PRS_NOK;
557 }
558
559 if (config->num_rounds > 0U && config->num_ignore >= config->num_rounds) {
560 ODPH_ERR("Invalid round ignore count: %" PRIu64 " (max: %" PRIu64 ")\n",
561 config->num_ignore, config->num_rounds - 1U);
562 return PRS_NOK;
563 }
564
565 return PRS_OK;
566}
567
568static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
569{
570 int opt;
571
572 static const struct option longopts[] = {
573 { "burst_pattern", required_argument, NULL, 'b' },
574 { "type", required_argument, NULL, 't' },
575 { "event_count", required_argument, NULL, 'e' },
576 { "data_size", required_argument, NULL, 'd' },
577 { "policy", required_argument, NULL, 'p' },
578 { "round_count", required_argument, NULL, 'r' },
579 { "ignore_rounds", required_argument, NULL, 'i' },
580 { "worker_count", required_argument, NULL, 'c' },
581 { "cache_size", required_argument, NULL, 'C' },
582 { "write_uarea", required_argument, NULL, 'w' },
583 { "help", no_argument, NULL, 'h' },
584 { NULL, 0, NULL, 0 }
585 };
586
587 static const char *shortopts = "b:t:e:d:p:r:i:c:C:w:h";
588
589 init_config(config);
590
591 while (1) {
592 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
593
594 if (opt == -1)
595 break;
596
597 switch (opt) {
598 case 'b':
599 parse_burst_pattern(config, optarg);
600 break;
601 case 't':
602 config->type = atoi(optarg);
603 break;
604 case 'e':
605 config->num_evs = atoi(optarg);
606 break;
607 case 'd':
608 config->data_size = atoi(optarg);
609 break;
610 case 'p':
611 config->policy = atoi(optarg);
612 break;
613 case 'r':
614 config->num_rounds = atoll(optarg);
615 break;
616 case 'i':
617 config->num_ignore = atoll(optarg);
618 break;
619 case 'c':
620 config->num_workers = atoi(optarg);
621 break;
622 case 'C':
623 config->cache_size = atoi(optarg);
624 break;
625 case 'w':
626 config->uarea_size = atoi(optarg);
627 break;
628 case 'h':
629 print_usage(&config->dyn_defs);
630 return PRS_TERM;
631 case '?':
632 default:
633 print_usage(&config->dyn_defs);
634 return PRS_NOK;
635 }
636 }
637
638 return check_options(config);
639}
640
641static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
642{
643 struct sigaction action = { .sa_handler = terminate };
644
645 if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
646 sigaddset(&action.sa_mask, SIGTERM) == -1 ||
647 sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
648 sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
649 ODPH_ERR("Error installing signal handler\n");
650 return PRS_NOK;
651 }
652
653 return parse_options(argc, argv, config);
654}
655
656static inline void save_alloc_stats(odp_time_t t1, odp_time_t t2, uint32_t num_alloc,
657 uint64_t round, uint8_t pattern, stats_t *stats)
658{
659 const uint64_t tm_diff = odp_time_diff_ns(t2, t1);
660
661 stats->alloc_tm += tm_diff;
662 stats->alloc_cnt += num_alloc;
663 ++stats->alloc_b_cnt;
664
665 if (tm_diff > stats->max_alloc_tm) {
666 stats->max_alloc_tm = tm_diff;
667 stats->max_alloc_rnd = round;
668 stats->max_alloc_pt = pattern;
669 }
670
671 if (tm_diff < stats->min_alloc_tm) {
672 stats->min_alloc_tm = tm_diff;
673 stats->min_alloc_rnd = round;
674 stats->min_alloc_pt = pattern;
675 }
676}
677
678static inline void write_to_uarea(uint8_t *data, uint32_t size)
679{
680 memset(data, UA_DATA, size);
681}
682
683static inline void save_uarea_stats(odp_time_t t1, odp_time_t t2, uint64_t round, uint8_t pattern,
684 stats_t *stats)
685{
686 const uint64_t tm_diff = odp_time_diff_ns(t2, t1);
687
688 stats->uarea_tm += tm_diff;
689
690 if (tm_diff > stats->max_uarea_tm) {
691 stats->max_uarea_tm = tm_diff;
692 stats->max_uarea_rnd = round;
693 stats->max_uarea_pt = pattern;
694 }
695
696 if (tm_diff < stats->min_uarea_tm) {
697 stats->min_uarea_tm = tm_diff;
698 stats->min_uarea_rnd = round;
699 stats->min_uarea_pt = pattern;
700 }
701}
702
703static inline void save_free_stats(odp_time_t t1, odp_time_t t2, uint64_t round, uint8_t pattern,
704 stats_t *stats)
705{
706 const uint64_t tm_diff = odp_time_diff_ns(t2, t1);
707
708 stats->free_tm += tm_diff;
709 ++stats->free_b_cnt;
710
711 if (tm_diff > stats->max_free_tm) {
712 stats->max_free_tm = tm_diff;
713 stats->max_free_rnd = round;
714 stats->max_free_pt = pattern;
715 }
716
717 if (tm_diff < stats->min_free_tm) {
718 stats->min_free_tm = tm_diff;
719 stats->min_free_rnd = round;
720 stats->min_free_pt = pattern;
721 }
722
723 stats->max_free_tm = ODPH_MAX(tm_diff, stats->max_free_tm);
724 stats->min_free_tm = ODPH_MIN(tm_diff, stats->min_free_tm);
725}
726
727static uint32_t allocate_buffers(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
728 uint64_t round, uint8_t pattern, odp_bool_t is_saved)
729{
730 odp_time_t t1, t2;
731 odp_pool_t pool = config->pool;
732 uint32_t retries = MAX_RETRIES;
733 odp_buffer_t *bufs = &((odp_buffer_t *)data)[idx];
734 uint32_t num_alloc, num_tot = 0U;
735 int ret;
736 stats_t *stats = &config->stats;
737
738 while (retries-- > 0U && num_tot < num) {
739 num_alloc = num - num_tot;
741 ret = odp_buffer_alloc_multi(pool, &bufs[num_tot], num_alloc);
743
744 if (odp_unlikely(ret < 0)) {
745 ++stats->alloc_errs;
746 break;
747 }
748
749 if (odp_unlikely((uint32_t)ret < num_alloc))
750 ++stats->reallocs;
751
752 num_tot += ret;
753
754 if (odp_likely(is_saved))
755 save_alloc_stats(t1, t2, ret, round, pattern, stats);
756 }
757
758 if (config->uarea_size > 0U) {
760
761 for (uint32_t i = 0U; i < num_tot; ++i)
762 write_to_uarea(odp_buffer_user_area(bufs[i]), config->uarea_size);
763
765
766 if (odp_likely(is_saved))
767 save_uarea_stats(t1, t2, round, pattern, stats);
768 }
769
770 return num_tot;
771}
772
773static void free_buffers(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
774 uint8_t pattern, odp_bool_t is_saved)
775{
776 odp_time_t t1, t2;
777 odp_buffer_t *bufs = &((odp_buffer_t *)data)[idx];
778
780 odp_buffer_free_multi(bufs, num);
782
783 if (odp_likely(is_saved))
784 save_free_stats(t1, t2, round, pattern, stats);
785}
786
787static uint32_t allocate_packets(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
788 uint64_t round, uint8_t pattern, odp_bool_t is_saved)
789{
790 odp_time_t t1, t2;
791 odp_pool_t pool = config->pool;
792 uint32_t retries = MAX_RETRIES, data_size = config->data_size;
793 odp_packet_t *pkts = &((odp_packet_t *)data)[idx];
794 uint32_t num_alloc, num_tot = 0U;
795 int ret;
796 stats_t *stats = &config->stats;
797
798 while (retries-- > 0U && num_tot < num) {
799 num_alloc = num - num_tot;
801 ret = odp_packet_alloc_multi(pool, data_size, &pkts[num_tot], num_alloc);
803
804 if (odp_unlikely(ret < 0)) {
805 ++stats->alloc_errs;
806 break;
807 }
808
809 if (odp_unlikely((uint32_t)ret < num_alloc))
810 ++stats->reallocs;
811
812 num_tot += ret;
813
814 if (odp_likely(is_saved))
815 save_alloc_stats(t1, t2, ret, round, pattern, stats);
816 }
817
818 if (config->uarea_size > 0U) {
820
821 for (uint32_t i = 0U; i < num_tot; ++i)
822 write_to_uarea(odp_packet_user_area(pkts[i]), config->uarea_size);
823
825
826 if (odp_likely(is_saved))
827 save_uarea_stats(t1, t2, round, pattern, stats);
828 }
829
830 return num_tot;
831}
832
833static void free_packets(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
834 uint8_t pattern, odp_bool_t is_saved)
835{
836 odp_time_t t1, t2;
837 odp_packet_t *pkts = &((odp_packet_t *)data)[idx];
838
840 odp_packet_free_multi(pkts, num);
842
843 if (odp_likely(is_saved))
844 save_free_stats(t1, t2, round, pattern, stats);
845}
846
847static uint32_t allocate_timeouts(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
848 uint64_t round, uint8_t pattern, odp_bool_t is_saved)
849{
850 odp_time_t t1, t2;
851 odp_pool_t pool = config->pool;
852 uint32_t retries = MAX_RETRIES;
853 odp_timeout_t *tmos = &((odp_timeout_t *)data)[idx];
854 uint32_t num_alloc, num_tot = 0U;
855 int ret;
856 stats_t *stats = &config->stats;
857
858 while (retries-- > 0U && num_tot < num) {
859 num_alloc = num - num_tot;
861 ret = odp_timeout_alloc_multi(pool, &tmos[num_tot], num_alloc);
863
864 if (odp_unlikely(ret < 0)) {
865 ++stats->alloc_errs;
866 break;
867 }
868
869 if (odp_unlikely((uint32_t)ret < num_alloc))
870 ++stats->reallocs;
871
872 num_tot += ret;
873
874 if (odp_likely(is_saved))
875 save_alloc_stats(t1, t2, ret, round, pattern, stats);
876 }
877
878 if (config->uarea_size > 0U) {
880
881 for (uint32_t i = 0U; i < num_tot; ++i)
882 write_to_uarea(odp_timeout_user_area(tmos[i]), config->uarea_size);
883
885
886 if (odp_likely(is_saved))
887 save_uarea_stats(t1, t2, round, pattern, stats);
888 }
889
890 return num_tot;
891}
892
893static void free_timeouts(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
894 uint8_t pattern, odp_bool_t is_saved)
895{
896 odp_time_t t1, t2;
897 odp_timeout_t *tmos = &((odp_timeout_t *)data)[idx];
898
900 odp_timeout_free_multi(tmos, num);
902
903 if (odp_likely(is_saved))
904 save_free_stats(t1, t2, round, pattern, stats);
905}
906
907static uint32_t allocate_vectors(worker_config_t *config, void *data, uint32_t idx, uint32_t num,
908 uint64_t round, uint8_t pattern, odp_bool_t is_saved)
909{
910 odp_time_t t1, t2;
911 odp_pool_t pool = config->pool;
912 uint32_t num_tot = 0U;
913 odp_packet_vector_t *vecs = &((odp_packet_vector_t *)data)[idx], vec;
914 stats_t *stats = &config->stats;
915
917
918 for (uint32_t i = 0U; i < num; ++i) {
919 vec = odp_packet_vector_alloc(pool);
920
922 break;
923
924 vecs[num_tot++] = vec;
925 }
926
928
929 if (odp_unlikely(num_tot == 0))
930 ++stats->alloc_errs;
931 else if (odp_likely(is_saved))
932 save_alloc_stats(t1, t2, num_tot, round, pattern, stats);
933
934 if (config->uarea_size > 0U) {
936
937 for (uint32_t i = 0U; i < num_tot; ++i)
938 write_to_uarea(odp_packet_vector_user_area(vecs[i]), config->uarea_size);
939
941
942 if (odp_likely(is_saved))
943 save_uarea_stats(t1, t2, round, pattern, stats);
944 }
945
946 return num_tot;
947}
948
949static void free_vectors(void *data, uint32_t idx, uint32_t num, stats_t *stats, uint64_t round,
950 uint8_t pattern, odp_bool_t is_saved)
951{
952 odp_time_t t1, t2;
953 odp_packet_vector_t *vecs = &((odp_packet_vector_t *)data)[idx];
954
956
957 for (uint32_t i = 0U; i < num; ++i)
958 odp_packet_vector_free(vecs[i]);
959
961
962 if (odp_likely(is_saved))
963 save_free_stats(t1, t2, round, pattern, stats);
964}
965
966static odp_pool_t create_pool(const char *name, const odp_pool_param_t *params, uint8_t policy)
967{
968 static odp_pool_t pool = ODP_POOL_INVALID;
969
970 if (policy == SINGLE && pool != ODP_POOL_INVALID)
971 return pool;
972
973 pool = odp_pool_create(name, params);
974
975 return pool;
976}
977
978static odp_bool_t setup_worker_config(prog_config_t *config)
979{
980 odp_pool_param_t param;
981 odp_pool_t pool;
982 worker_config_t *worker;
983 odp_shm_t shm;
984 void *data;
985
986 odp_pool_param_init(&param);
987
988 if (config->type == BUFFER) {
989 param.type = ODP_POOL_BUFFER;
990 param.buf.num = config->num_evs;
991 param.buf.size = config->data_size;
992 param.buf.uarea_size = config->uarea_size;
993 param.buf.cache_size = config->cache_size;
994 config->alloc_fn = allocate_buffers;
995 config->free_fn = free_buffers;
996 } else if (config->type == PACKET) {
997 param.type = ODP_POOL_PACKET;
998 param.pkt.num = config->num_evs;
999 param.pkt.len = config->data_size;
1000 param.pkt.seg_len = config->seg_len;
1001 param.pkt.uarea_size = config->uarea_size;
1002 param.pkt.cache_size = config->cache_size;
1003 config->alloc_fn = allocate_packets;
1004 config->free_fn = free_packets;
1005 } else if (config->type == TMO) {
1006 param.type = ODP_POOL_TIMEOUT;
1007 param.tmo.num = config->num_evs;
1008 param.tmo.uarea_size = config->uarea_size;
1009 param.tmo.cache_size = config->cache_size;
1010 config->alloc_fn = allocate_timeouts;
1011 config->free_fn = free_timeouts;
1012 } else {
1013 param.type = ODP_POOL_VECTOR;
1014 param.vector.num = config->num_evs;
1015 param.vector.max_size = config->data_size;
1016 param.vector.uarea_size = config->uarea_size;
1017 param.vector.cache_size = config->cache_size;
1018 config->alloc_fn = allocate_vectors;
1019 config->free_fn = free_vectors;
1020 }
1021
1022 for (uint32_t i = 0U; i < config->num_workers; ++i) {
1023 pool = create_pool(PROG_NAME "_pool", &param, config->policy);
1024
1025 if (pool == ODP_POOL_INVALID) {
1026 ODPH_ERR("Error creating worker pool\n");
1027 return false;
1028 }
1029
1030 shm = odp_shm_reserve(PROG_NAME "_shm",
1031 config->handle_size * config->num_data_elems,
1032 ODP_CACHE_LINE_SIZE, 0U);
1033
1034 if (shm == ODP_SHM_INVALID) {
1035 ODPH_ERR("Error creating worker SHM\n");
1036 return false;
1037 }
1038
1039 data = odp_shm_addr(shm);
1040
1041 if (data == NULL) {
1042 ODPH_ERR("Error resolving worker SHM\n");
1043 return false;
1044 }
1045
1046 worker = &config->worker_config[i];
1047 worker->pool = pool;
1048 worker->data = data;
1049 worker->prog_config = config;
1050 worker->shm = shm;
1051 worker->data_size = config->data_size;
1052 worker->uarea_size = config->uarea_size;
1053 }
1054
1055 return true;
1056}
1057
1058static int run_test(void *args)
1059{
1060 worker_config_t *config = args;
1061 odp_time_t t1, t2;
1062 uint64_t i, num_ignore = config->prog_config->num_ignore;
1063 const uint64_t num_rnds = config->prog_config->num_rounds;
1064 odp_atomic_u32_t *is_running = &config->prog_config->is_running;
1065 uint32_t head_idx, cur_idx, val, num_alloc, idx;
1066 odp_bool_t is_saved;
1067 const uint8_t num_elems = config->prog_config->num_elems;
1068 const alloc_elem_t *elems = config->prog_config->alloc_elems, *elem;
1069 uint8_t op;
1070 void *data = config->data;
1071 const alloc_fn_t alloc_fn = config->prog_config->alloc_fn;
1072 stats_t *stats = &config->stats;
1073 const free_fn_t free_fn = config->prog_config->free_fn;
1074
1075 odp_barrier_wait(&config->prog_config->init_barrier);
1076 t1 = odp_time_local_strict();
1077
1078 for (i = 0U; (i < num_rnds || num_rnds == 0U) && odp_atomic_load_u32(is_running); ++i) {
1079 head_idx = 0U;
1080 cur_idx = head_idx;
1081 is_saved = (num_ignore > 0U ? num_ignore-- : num_ignore) == 0U;
1082
1083 for (uint8_t j = 0U; j < num_elems; ++j) {
1084 elem = &elems[j];
1085 val = elem->val;
1086 op = elem->op;
1087
1088 if (op == ALLOC) {
1089 num_alloc = alloc_fn(config, data, cur_idx, val, i, j, is_saved);
1090
1091 if (odp_unlikely(num_alloc < val))
1092 ++stats->pattern_errs;
1093
1094 cur_idx += num_alloc;
1095 } else if (op == FREE) {
1096 /* Due to potential pattern errors, there might not be expected
1097 * amount of freeable events. */
1098 val = ODPH_MIN(val, cur_idx - head_idx);
1099
1100 if (elem->opt == TOP) {
1101 idx = head_idx;
1102 head_idx += val;
1103 } else {
1104 cur_idx -= val;
1105 idx = cur_idx;
1106 }
1107
1108 free_fn(data, idx, val, stats, i, j, is_saved);
1109 } else {
1110 odp_time_wait_ns(val);
1111 }
1112 }
1113 }
1114
1115 t2 = odp_time_local_strict();
1116 stats->tot_tm = odp_time_diff_ns(t2, t1);
1117 stats->act_num_rounds = i;
1118 odp_barrier_wait(&config->prog_config->term_barrier);
1119
1120 return 0;
1121}
1122
1123static odp_bool_t setup_workers(prog_config_t *config)
1124{
1125 odph_thread_common_param_t thr_common;
1126 odph_thread_param_t thr_params[config->num_workers], *thr_param;
1127
1128 odp_barrier_init(&config->init_barrier, config->num_workers + 1);
1129 odp_barrier_init(&config->term_barrier, config->num_workers + 1);
1130 odph_thread_common_param_init(&thr_common);
1131 thr_common.instance = config->odp_instance;
1132 thr_common.cpumask = &config->worker_mask;
1133
1134 for (uint32_t i = 0; i < config->num_workers; ++i) {
1135 thr_param = &thr_params[i];
1136 odph_thread_param_init(thr_param);
1137 thr_param->start = run_test;
1138 thr_param->thr_type = ODP_THREAD_WORKER;
1139 thr_param->arg = &config->worker_config[i];
1140 }
1141
1142 if ((uint32_t)odph_thread_create(config->thread_tbl, &thr_common, thr_params,
1143 config->num_workers) != config->num_workers) {
1144 ODPH_ERR("Error configuring worker threads\n");
1145 return false;
1146 }
1147
1148 odp_barrier_wait(&config->init_barrier);
1149
1150 return true;
1151}
1152
1153static odp_bool_t setup_test(prog_config_t *config)
1154{
1155 return setup_worker_config(config) && setup_workers(config);
1156}
1157
1158static void stop_test(prog_config_t *config)
1159{
1160 odp_barrier_wait(&config->term_barrier);
1161 (void)odph_thread_join(config->thread_tbl, config->num_workers);
1162}
1163
1164static void print_stats(const prog_config_t *config)
1165{
1166 const alloc_elem_t *elem;
1167 const stats_t *stats;
1168 uint64_t ev_rate, ave_b_alloc_tm, b_alloc_min, b_alloc_max, ave_b_free_tm, b_free_min,
1169 b_free_max, ave_alloc_tm, ave_free_tm, ave_ua_b_tm, b_ua_min, b_ua_max, ave_ua_tm,
1170 tot_b_alloc_tm = 0U, tot_b_free_tm = 0U, tot_alloc_tm = 0U, tot_free_tm = 0U,
1171 tot_alloc_min = 0U, tot_alloc_max = 0U, tot_free_min = 0U, tot_free_max = 0U,
1172 tot_b_ua_tm = 0U, tot_ua_tm = 0U, tot_ua_min = 0U, tot_ua_max = 0U;
1173
1174 printf("\n==================\n\n"
1175 "Pool latency test done\n\n"
1176 " type: %s\n"
1177 " event count: %u\n", config->type == BUFFER ? "buffer" :
1178 config->type == PACKET ? "packet" : config->type == TMO ? "timeout" : "vector",
1179 config->num_evs);
1180
1181 if (config->type != TMO)
1182 printf(" %s %u\n",
1183 config->type != VECTOR ? "data size: " : "vector size: ",
1184 config->data_size);
1185
1186 printf(" pool policy: %s\n"
1187 " target round count: %" PRIu64 "\n"
1188 " ignore count: %" PRIu64 "\n"
1189 " cache size: %" PRIi64 "\n"
1190 " user area: %u (B)\n"
1191 " burst pattern:\n", config->policy == SINGLE ? "shared" : "per-worker",
1192 config->num_rounds, config->num_ignore, config->cache_size, config->uarea_size);
1193
1194 for (uint8_t i = 0U; i < config->num_elems; ++i) {
1195 elem = &config->alloc_elems[i];
1196 printf(" %s %u%s\n", elem->op == ALLOC ? "allocate:" :
1197 elem->op == FREE && elem->opt == TOP ? "free (t):" :
1198 elem->op == FREE && elem->opt == BOTTOM ? "free (b):" :
1199 "delay: ", elem->val, elem->op == DELAY ? " (ns)" : "");
1200 }
1201
1202 printf("\n");
1203
1204 for (uint32_t i = 0U; i < config->num_workers; ++i) {
1205 stats = &config->worker_config[i].stats;
1206 ev_rate = stats->tot_tm > 0U ?
1207 (double)stats->alloc_cnt / stats->tot_tm * ODP_TIME_SEC_IN_NS : 0U;
1208 ave_b_alloc_tm = stats->alloc_b_cnt > 0U ?
1209 stats->alloc_tm / stats->alloc_b_cnt : 0U;
1210 b_alloc_min = ave_b_alloc_tm > 0U ? stats->min_alloc_tm : 0U;
1211 b_alloc_max = ave_b_alloc_tm > 0U ? stats->max_alloc_tm : 0U;
1212 ave_b_free_tm = stats->free_b_cnt > 0U ?
1213 stats->free_tm / stats->free_b_cnt : 0U;
1214 b_free_min = ave_b_free_tm > 0U ? stats->min_free_tm : 0U;
1215 b_free_max = ave_b_free_tm > 0U ? stats->max_free_tm : 0U;
1216 ave_alloc_tm = stats->alloc_cnt > 0U ? stats->alloc_tm / stats->alloc_cnt : 0U;
1217 ave_free_tm = stats->alloc_cnt > 0U ? stats->free_tm / stats->alloc_cnt : 0U;
1218
1219 printf(" worker %d:\n"
1220 " actual round count: %" PRIu64 "\n"
1221 " significant events allocated/freed: %" PRIu64 "\n"
1222 " allocation retries: %" PRIu64 "\n"
1223 " allocation errors: %" PRIu64 "\n"
1224 " pattern errors: %" PRIu64 "\n"
1225 " run time: %" PRIu64 " (ns)\n"
1226 " event rate %" PRIu64 " (evs/s)\n"
1227 " average latency breakdown (ns):\n"
1228 " per allocation burst: %" PRIu64 " (min: %" PRIu64 " (round: %"
1229 PRIu64 ", pattern: %u), max: %" PRIu64 " (round: %" PRIu64 ", pattern: %u))"
1230 "\n"
1231 " per allocation: %" PRIu64 "\n"
1232 " per free burst: %" PRIu64 " (min: %" PRIu64 " (round: %"
1233 PRIu64 ", pattern: %u), max: %" PRIu64 " (round: %" PRIu64 ", pattern: %u))"
1234 "\n"
1235 " per free: %" PRIu64 "\n", i, stats->act_num_rounds,
1236 stats->alloc_cnt, stats->reallocs, stats->alloc_errs, stats->pattern_errs,
1237 stats->tot_tm, ev_rate, ave_b_alloc_tm, b_alloc_min, stats->min_alloc_rnd,
1238 stats->min_alloc_pt, b_alloc_max, stats->max_alloc_rnd, stats->max_alloc_pt,
1239 ave_alloc_tm, ave_b_free_tm, b_free_min, stats->min_free_rnd,
1240 stats->min_free_pt, b_free_max, stats->max_free_rnd, stats->max_free_pt,
1241 ave_free_tm);
1242 tot_b_alloc_tm += ave_b_alloc_tm;
1243 tot_b_free_tm += ave_b_free_tm;
1244 tot_alloc_tm += ave_alloc_tm;
1245 tot_free_tm += ave_free_tm;
1246 tot_alloc_min += b_alloc_min;
1247 tot_alloc_max += b_alloc_max;
1248 tot_free_min += b_free_min;
1249 tot_free_max += b_free_max;
1250
1251 if (config->uarea_size > 0U) {
1252 ave_ua_b_tm = stats->alloc_b_cnt > 0U ?
1253 stats->uarea_tm / stats->alloc_b_cnt : 0U;
1254 ave_ua_tm = stats->alloc_cnt > 0U ?
1255 stats->uarea_tm / stats->alloc_cnt : 0U;
1256 b_ua_min = ave_ua_b_tm > 0U ? stats->min_uarea_tm : 0U;
1257 b_ua_max = ave_ua_b_tm > 0U ? stats->max_uarea_tm : 0U;
1258 printf(" per ua write burst: %" PRIu64 " (min: %" PRIu64 " ("
1259 "round: %" PRIu64 ", pattern: %u), max: %" PRIu64 " (round: %"
1260 PRIu64 ", pattern: %u))\n"
1261 " per ua write: %" PRIu64 "\n", ave_ua_b_tm,
1262 b_ua_min, stats->min_uarea_rnd, stats->min_uarea_pt, b_ua_max,
1263 stats->max_uarea_rnd, stats->max_uarea_pt, ave_ua_tm);
1264 tot_b_ua_tm += ave_ua_b_tm;
1265 tot_ua_tm += ave_ua_tm;
1266 tot_ua_min += b_ua_min;
1267 tot_ua_max += b_ua_max;
1268 }
1269
1270 printf("\n");
1271 }
1272
1273 printf(" total (ns):\n"
1274 " per allocation burst: %" PRIu64 " (min: %" PRIu64 ", max: %" PRIu64 ")\n"
1275 " per allocation: %" PRIu64 "\n"
1276 " per free burst: %" PRIu64 " (min: %" PRIu64 ", max: %" PRIu64 ")\n"
1277 " per free: %" PRIu64 "\n",
1278 tot_b_alloc_tm / config->num_workers, tot_alloc_min / config->num_workers,
1279 tot_alloc_max / config->num_workers, tot_alloc_tm / config->num_workers,
1280 tot_b_free_tm / config->num_workers, tot_free_min / config->num_workers,
1281 tot_free_max / config->num_workers, tot_free_tm / config->num_workers);
1282
1283 if (config->uarea_size > 0U) {
1284 printf(" per ua write burst: %" PRIu64 " (min: %" PRIu64 ", max: %"
1285 PRIu64 ")\n"
1286 " per ua write: %" PRIu64 "\n",
1287 tot_b_ua_tm / config->num_workers, tot_ua_min / config->num_workers,
1288 tot_ua_max / config->num_workers, tot_ua_tm / config->num_workers);
1289 }
1290
1291 printf("\n==================\n");
1292}
1293
1294static void destroy_pool(odp_pool_t pool, uint8_t policy)
1295{
1296 static odp_bool_t is_destroyed;
1297
1298 if (policy == SINGLE && is_destroyed)
1299 return;
1300
1301 (void)odp_pool_destroy(pool);
1302 is_destroyed = true;
1303}
1304
1305static void teardown(const prog_config_t *config)
1306{
1307 const worker_config_t *worker;
1308
1309 for (uint32_t i = 0U; i < config->num_workers; ++i) {
1310 worker = &config->worker_config[i];
1311
1312 if (worker->pool != ODP_POOL_INVALID)
1313 destroy_pool(worker->pool, config->policy);
1314
1315 if (worker->shm != ODP_SHM_INVALID)
1316 (void)odp_shm_free(worker->shm);
1317 }
1318}
1319
1320int main(int argc, char **argv)
1321{
1322 odph_helper_options_t odph_opts;
1323 odp_init_t init_param;
1325 odp_shm_t shm_cfg = ODP_SHM_INVALID;
1326 int ret = EXIT_SUCCESS;
1327 parse_result_t parse_res;
1328
1329 argc = odph_parse_options(argc, argv);
1330
1331 if (odph_options(&odph_opts) == -1) {
1332 ODPH_ERR("Error while reading ODP helper options, exiting\n");
1333 exit(EXIT_FAILURE);
1334 }
1335
1336 odp_init_param_init(&init_param);
1337 init_param.mem_model = odph_opts.mem_model;
1338
1339 if (odp_init_global(&odp_instance, &init_param, NULL)) {
1340 ODPH_ERR("ODP global init failed, exiting\n");
1341 exit(EXIT_FAILURE);
1342 }
1343
1345 ODPH_ERR("ODP local init failed, exiting\n");
1346 exit(EXIT_FAILURE);
1347 }
1348
1349 shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
1350 0U);
1351
1352 if (shm_cfg == ODP_SHM_INVALID) {
1353 ODPH_ERR("Error reserving shared memory\n");
1354 ret = EXIT_FAILURE;
1355 goto out;
1356 }
1357
1358 prog_conf = odp_shm_addr(shm_cfg);
1359
1360 if (prog_conf == NULL) {
1361 ODPH_ERR("Error resolving shared memory address\n");
1362 ret = EXIT_FAILURE;
1363 goto out;
1364 }
1365
1366 parse_res = setup_program(argc, argv, prog_conf);
1367
1368 if (parse_res == PRS_NOK) {
1369 ret = EXIT_FAILURE;
1370 goto out;
1371 }
1372
1373 if (parse_res == PRS_TERM) {
1374 ret = EXIT_SUCCESS;
1375 goto out;
1376 }
1377
1378 prog_conf->odp_instance = odp_instance;
1379 odp_atomic_init_u32(&prog_conf->is_running, 1U);
1380
1381 if (!setup_test(prog_conf)) {
1382 ret = EXIT_FAILURE;
1383 goto out_test;
1384 }
1385
1386 stop_test(prog_conf);
1387 print_stats(prog_conf);
1388
1389out_test:
1390 teardown(prog_conf);
1391
1392out:
1393 if (shm_cfg != ODP_SHM_INVALID)
1394 (void)odp_shm_free(shm_cfg);
1395
1396 if (odp_term_local()) {
1397 ODPH_ERR("ODP local terminate failed, exiting\n");
1398 exit(EXIT_FAILURE);
1399 }
1400
1402 ODPH_ERR("ODP global terminate failed, exiting\n");
1403 exit(EXIT_FAILURE);
1404 }
1405
1406 return ret;
1407}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
void * odp_buffer_user_area(odp_buffer_t buf)
Buffer user area.
int odp_buffer_alloc_multi(odp_pool_t pool, odp_buffer_t buf[], int num)
Allocate multiple buffers.
_odp_abi_buffer_t * odp_buffer_t
ODP buffer.
void odp_buffer_free_multi(const odp_buffer_t buf[], int num)
Free multiple buffers.
#define ODP_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition spec/hints.h:54
#define odp_likely(x)
Branch likely taken.
Definition spec/hints.h:59
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
int odp_instance(odp_instance_t *instance)
Get instance handle.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
#define ODP_STATIC_ASSERT(cond, msg)
Compile time assertion macro.
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_packet_vector_user_area(odp_packet_vector_t pktv)
Packet vector user area.
_odp_abi_packet_vector_t * odp_packet_vector_t
ODP packet vector.
odp_packet_vector_t odp_packet_vector_alloc(odp_pool_t pool)
Allocate a packet vector from a packet vector pool.
void odp_packet_vector_free(odp_packet_vector_t pktv)
Free packet vector.
void * odp_packet_user_area(odp_packet_t pkt)
User area address.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
int odp_packet_alloc_multi(odp_pool_t pool, uint32_t len, odp_packet_t pkt[], int num)
Allocate multiple packets from a packet pool.
_odp_abi_packet_t * odp_packet_t
ODP packet.
#define ODP_PACKET_VECTOR_INVALID
Invalid packet vector.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_TIMEOUT
Timeout pool.
@ ODP_POOL_VECTOR
Vector event pool.
@ ODP_POOL_BUFFER
Buffer pool.
@ ODP_POOL_PACKET
Packet pool.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
int odp_shm_capability(odp_shm_capability_t *capa)
Query shared memory capabilities.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#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_local_strict(void)
Current local time (strict)
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
void * odp_timeout_user_area(odp_timeout_t tmo)
Timeout user area.
int odp_timeout_alloc_multi(odp_pool_t pool, odp_timeout_t tmo[], int num)
Allocate multiple timeouts.
void odp_timeout_free_multi(odp_timeout_t tmo[], int num)
Free multiple timeouts.
_odp_abi_timeout_t * odp_timeout_t
ODP timeout handle.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
uint32_t max_num
Maximum number of buffers of any size.
uint32_t min_cache_size
Minimum size of thread local cache.
struct odp_pool_capability_t::@133 buf
Buffer pool capabilities
struct odp_pool_capability_t::@135 tmo
Timeout pool capabilities
uint32_t max_uarea_size
Maximum user area size in bytes.
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
uint32_t max_size
Maximum buffer data size in bytes.
struct odp_pool_capability_t::@136 vector
Vector pool capabilities.
uint32_t max_cache_size
Maximum size of thread local cache.
uint32_t max_pools
Maximum number of pools of any type (odp_pool_type_t)
uint32_t max_seg_len
Maximum packet segment data length in bytes.
uint32_t max_len
Maximum packet data length in bytes.
uint32_t uarea_size
Minimum user area size in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
uint32_t size
Minimum buffer size in bytes.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t max_size
Maximum number of handles (such as odp_packet_t) in a vector.
struct odp_pool_param_t::@138 buf
Parameters for buffer pools.
struct odp_pool_param_t::@140 tmo
Parameters for timeout pools.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
struct odp_pool_param_t::@141 vector
Parameters for vector pools.
Shared memory capabilities.
uint32_t max_blocks
Maximum number of shared memory blocks.
uint64_t max_size
Maximum memory block size in bytes.