API Reference Manual  1.46.0
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 
35 enum {
36  BUFFER = 0U,
37  PACKET,
38  TMO,
39  VECTOR
40 };
41 
42 enum {
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 
66 ODP_STATIC_ASSERT(MAX_PATTERN_LEN < UINT8_MAX, "Too long pattern length");
67 
68 typedef 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 
82 typedef enum {
83  PRS_OK,
84  PRS_NOK,
85  PRS_TERM
86 } parse_result_t;
87 
88 typedef 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 
120 typedef struct {
121  uint32_t val;
122  uint8_t op;
123  uint8_t opt;
124 } alloc_elem_t;
125 
126 typedef struct prog_config_s prog_config_t;
127 
128 typedef 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 
138 typedef 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);
140 typedef 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 
143 typedef 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 
170 static prog_config_t *prog_conf;
171 
172 static void terminate(int signal ODP_UNUSED)
173 {
174  odp_atomic_store_u32(&prog_conf->is_running, 0U);
175 }
176 
177 static 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 
227 static 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 
263 static 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 
330 static 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 
568 static 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 
641 static 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 
656 static 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 
678 static inline void write_to_uarea(uint8_t *data, uint32_t size)
679 {
680  memset(data, UA_DATA, size);
681 }
682 
683 static 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 
703 static 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 
727 static 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;
740  t1 = odp_time_local_strict();
741  ret = odp_buffer_alloc_multi(pool, &bufs[num_tot], num_alloc);
742  t2 = odp_time_local_strict();
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) {
759  t1 = odp_time_local_strict();
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 
764  t2 = odp_time_local_strict();
765 
766  if (odp_likely(is_saved))
767  save_uarea_stats(t1, t2, round, pattern, stats);
768  }
769 
770  return num_tot;
771 }
772 
773 static 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 
779  t1 = odp_time_local_strict();
780  odp_buffer_free_multi(bufs, num);
781  t2 = odp_time_local_strict();
782 
783  if (odp_likely(is_saved))
784  save_free_stats(t1, t2, round, pattern, stats);
785 }
786 
787 static 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;
800  t1 = odp_time_local_strict();
801  ret = odp_packet_alloc_multi(pool, data_size, &pkts[num_tot], num_alloc);
802  t2 = odp_time_local_strict();
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) {
819  t1 = odp_time_local_strict();
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 
824  t2 = odp_time_local_strict();
825 
826  if (odp_likely(is_saved))
827  save_uarea_stats(t1, t2, round, pattern, stats);
828  }
829 
830  return num_tot;
831 }
832 
833 static 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 
839  t1 = odp_time_local_strict();
840  odp_packet_free_multi(pkts, num);
841  t2 = odp_time_local_strict();
842 
843  if (odp_likely(is_saved))
844  save_free_stats(t1, t2, round, pattern, stats);
845 }
846 
847 static 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;
860  t1 = odp_time_local_strict();
861  ret = odp_timeout_alloc_multi(pool, &tmos[num_tot], num_alloc);
862  t2 = odp_time_local_strict();
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) {
879  t1 = odp_time_local_strict();
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 
884  t2 = odp_time_local_strict();
885 
886  if (odp_likely(is_saved))
887  save_uarea_stats(t1, t2, round, pattern, stats);
888  }
889 
890  return num_tot;
891 }
892 
893 static 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 
899  t1 = odp_time_local_strict();
900  odp_timeout_free_multi(tmos, num);
901  t2 = odp_time_local_strict();
902 
903  if (odp_likely(is_saved))
904  save_free_stats(t1, t2, round, pattern, stats);
905 }
906 
907 static 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 
916  t1 = odp_time_local_strict();
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 
927  t2 = odp_time_local_strict();
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) {
935  t1 = odp_time_local_strict();
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 
940  t2 = odp_time_local_strict();
941 
942  if (odp_likely(is_saved))
943  save_uarea_stats(t1, t2, round, pattern, stats);
944  }
945 
946  return num_tot;
947 }
948 
949 static 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 
955  t1 = odp_time_local_strict();
956 
957  for (uint32_t i = 0U; i < num; ++i)
958  odp_packet_vector_free(vecs[i]);
959 
960  t2 = odp_time_local_strict();
961 
962  if (odp_likely(is_saved))
963  save_free_stats(t1, t2, round, pattern, stats);
964 }
965 
966 static 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 
978 static 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 
1058 static 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 
1123 static 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 
1153 static odp_bool_t setup_test(prog_config_t *config)
1154 {
1155  return setup_worker_config(config) && setup_workers(config);
1156 }
1157 
1158 static 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 
1164 static 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 
1294 static 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 
1305 static 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 
1320 int 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 
1389 out_test:
1390  teardown(prog_conf);
1391 
1392 out:
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.
_odp_abi_packet_vector_t * odp_packet_vector_t
ODP packet vector.
void * odp_packet_vector_user_area(odp_packet_vector_t pktv)
Packet vector user area.
void * odp_packet_user_area(odp_packet_t pkt)
User area address.
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_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.
#define ODP_SHM_INVALID
Invalid shared memory block.
int odp_shm_capability(odp_shm_capability_t *capa)
Query shared memory capabilities.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
@ 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.
struct odp_pool_capability_t::@121 buf
Buffer pool capabilities
struct odp_pool_capability_t::@122 pkt
Packet pool capabilities
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@123 tmo
Timeout pool capabilities
uint32_t min_cache_size
Minimum size of thread local cache.
uint32_t max_uarea_size
Maximum user area size in bytes.
struct odp_pool_capability_t::@124 vector
Vector pool capabilities.
uint32_t max_size
Maximum buffer data size in bytes.
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.
Pool parameters.
uint32_t uarea_size
Minimum user area size in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@127 tmo
Parameters for timeout pools.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
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.
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::@126 pkt
Parameters for packet pools.
struct odp_pool_param_t::@128 vector
Parameters for vector pools.
struct odp_pool_param_t::@125 buf
Parameters for buffer pools.
Shared memory capabilities.
uint32_t max_blocks
Maximum number of shared memory blocks.
uint64_t max_size
Maximum memory block size in bytes.