API Reference Manual  1.48.0
odp_dmafwd.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2023-2025 Nokia
3  */
4 
16 #ifndef _GNU_SOURCE
17 #define _GNU_SOURCE
18 #endif
19 
20 #include <inttypes.h>
21 #include <stdlib.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <time.h>
26 
27 #include <odp_api.h>
28 #include <odp/helper/odph_api.h>
29 
30 #define EXIT_NOT_SUP 2
31 #define PROG_NAME "odp_dmafwd"
32 #define DELIMITER ","
33 
34 enum {
35  SW_COPY = 0U,
36  DMA_COPY_EV,
37  DMA_COPY_POLL
38 };
39 
40 #define DEF_CPY_TYPE SW_COPY
41 #define DEF_CNT 32768U
42 #define DEF_LEN 1024U
43 #define DEF_WORKERS 1U
44 #define DEF_TIME 0U
45 
46 #define MAX_IFS 2U
47 #define MAX_OUT_QS 32U
48 #define MAX_BURST 32U
49 #define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
50 
51 #define DIV_IF(a, b) ((b) > 0U ? ((a) / (b)) : 0U)
52 
53 ODP_STATIC_ASSERT(MAX_IFS < UINT8_MAX, "Too large maximum interface count");
54 ODP_STATIC_ASSERT(MAX_OUT_QS < UINT8_MAX, "Too large maximum output queue count");
55 
56 typedef struct {
57  uint32_t burst_size;
58  uint32_t num_pkts;
59  uint32_t pkt_len;
60  uint32_t cache_size;
61 } dynamic_defs_t;
62 
63 typedef enum {
64  PRS_OK,
65  PRS_NOK,
66  PRS_TERM,
67  PRS_NOT_SUP
68 } parse_result_t;
69 
70 typedef struct prog_config_s prog_config_t;
71 
72 typedef struct {
73  uint64_t copy_errs;
74  uint64_t trs;
75  uint64_t start_errs;
76  uint64_t trs_errs;
77  uint64_t buf_alloc_errs;
78  uint64_t compl_alloc_errs;
79  uint64_t pkt_alloc_errs;
80  uint64_t trs_poll_errs;
81  uint64_t trs_polled;
82  uint64_t fwd_pkts;
83  uint64_t discards;
84  uint64_t sched_cc;
85  uint64_t tot_cc;
86  uint64_t sched_rounds;
87  uint64_t pkt_cleanup;
88 } stats_t;
89 
90 typedef struct ODP_ALIGNED_CACHE {
91  prog_config_t *prog_config;
92  odp_dma_t dma_handle;
93  odp_pool_t compl_pool;
94  odp_pool_t copy_pool;
95  odp_pool_t trs_pool;
96  odp_queue_t compl_q;
97  odp_stash_t inflight_stash;
98  stats_t stats;
99  int thr_idx;
100 } thread_config_t;
101 
102 typedef struct pktio_s {
103  odp_pktout_queue_t out_qs[MAX_OUT_QS];
104  char *name;
105  odp_pktio_t handle;
106  uint8_t num_out_qs;
107 } pktio_t;
108 
109 typedef struct {
110  odp_packet_t src_pkts[MAX_BURST];
111  odp_packet_t dst_pkts[MAX_BURST];
112  pktio_t *pktio;
113  int num;
114 } transfer_t;
115 
116 /* Function for initializing transfer structures */
117 typedef transfer_t *(*init_fn_t)(odp_dma_transfer_param_t *trs_param,
118  odp_dma_compl_param_t *compl_param, odp_dma_seg_t *src_segs,
119  odp_dma_seg_t *dst_segs, pktio_t *pktio, thread_config_t *config);
120 /* Function for starting transfers */
121 typedef odp_bool_t (*start_fn_t)(odp_dma_transfer_param_t *trs_param,
122  odp_dma_compl_param_t *compl_param, thread_config_t *config);
123 /* Function for setting up packets for copy */
124 typedef void (*pkt_fn_t)(odp_packet_t pkts[], int num, pktio_t *pktio, init_fn_t init_fn,
125  start_fn_t start_fn, thread_config_t *config);
126 /* Function for draining and tearing down inflight operations */
127 typedef void (*drain_fn_t)(thread_config_t *config);
128 
129 typedef struct prog_config_s {
130  uint8_t pktio_idx_map[ODP_PKTIO_MAX_INDEX + 1];
131  odph_thread_t thread_tbl[MAX_WORKERS];
132  thread_config_t thread_config[MAX_WORKERS];
133  pktio_t pktios[MAX_IFS];
134  dynamic_defs_t dyn_defs;
136  odp_barrier_t init_barrier;
137  odp_barrier_t term_barrier;
138  odp_atomic_u32_t is_running;
139  odp_pool_t pktio_pool;
140  odp_pool_t copy_pool;
141  odp_pool_t trs_pool;
142 
143  struct {
144  init_fn_t init_fn;
145  start_fn_t start_fn;
146  pkt_fn_t pkt_fn;
147  drain_fn_t drain_fn;
148  };
149 
150  uint64_t inflight_obj_size;
151  uint32_t burst_size;
152  uint32_t num_pkts;
153  uint32_t pkt_len;
154  uint32_t cache_size;
155  uint32_t num_inflight;
156  uint32_t trs_cache_size;
157  uint32_t compl_cache_size;
158  uint32_t stash_cache_size;
159  double time_sec;
160  odp_bool_t seg_free;
161  odp_stash_type_t stash_type;
162  int num_thrs;
163  uint8_t num_ifs;
164  uint8_t copy_type;
165 } prog_config_t;
166 
167 typedef struct {
168  odp_packet_t pkts[MAX_BURST * 2U];
169  pktio_t *pktio;
170  int num;
171 } pkt_vec_t;
172 
173 static prog_config_t *prog_conf;
174 
175 static void terminate(int signal ODP_UNUSED)
176 {
177  odp_atomic_store_u32(&prog_conf->is_running, 0U);
178 }
179 
180 static void init_config(prog_config_t *config)
181 {
182  odp_dma_capability_t dma_capa;
183  uint32_t burst_size;
184  odp_pool_capability_t pool_capa;
185  odp_pool_param_t pool_param;
186  thread_config_t *thr;
187 
188  memset(config, 0, sizeof(*config));
189 
190  if (odp_dma_capability(&dma_capa) == 0) {
191  burst_size = ODPH_MIN(dma_capa.max_src_segs, dma_capa.max_dst_segs);
192  burst_size = ODPH_MIN(burst_size, MAX_BURST);
193  config->dyn_defs.burst_size = burst_size;
194  }
195 
196  if (odp_pool_capability(&pool_capa) == 0) {
197  config->dyn_defs.num_pkts = pool_capa.pkt.max_num > 0U ?
198  ODPH_MIN(pool_capa.pkt.max_num, DEF_CNT) : DEF_CNT;
199  config->dyn_defs.pkt_len = pool_capa.pkt.max_len > 0U ?
200  ODPH_MIN(pool_capa.pkt.max_len, DEF_LEN) : DEF_LEN;
201  odp_pool_param_init(&pool_param);
202  config->dyn_defs.cache_size = pool_param.pkt.cache_size;
203  }
204 
205  config->pktio_pool = ODP_POOL_INVALID;
206  config->copy_pool = ODP_POOL_INVALID;
207  config->trs_pool = ODP_POOL_INVALID;
208  config->burst_size = config->dyn_defs.burst_size;
209  config->num_pkts = config->dyn_defs.num_pkts;
210  config->pkt_len = config->dyn_defs.pkt_len;
211  config->cache_size = config->dyn_defs.cache_size;
212  config->time_sec = DEF_TIME;
213  config->num_thrs = DEF_WORKERS;
214  config->copy_type = DEF_CPY_TYPE;
215 
216  for (int i = 0; i < MAX_WORKERS; ++i) {
217  thr = &config->thread_config[i];
218  thr->dma_handle = ODP_DMA_INVALID;
219  thr->compl_pool = ODP_POOL_INVALID;
220  thr->compl_q = ODP_QUEUE_INVALID;
221  thr->inflight_stash = ODP_STASH_INVALID;
222  }
223 
224  for (uint32_t i = 0U; i < MAX_IFS; ++i)
225  config->pktios[i].handle = ODP_PKTIO_INVALID;
226 }
227 
228 static void print_usage(dynamic_defs_t *dyn_defs)
229 {
230  printf("\n"
231  "DMA performance tester with packet I/O. Receive and forward packets after\n"
232  "software copy or DMA offload copy.\n"
233  "\n"
234  "Usage: " PROG_NAME " OPTIONS\n"
235  "\n"
236  " E.g. " PROG_NAME " -i eth0\n"
237  " " PROG_NAME " -i eth0 -t 0\n"
238  " " PROG_NAME " -i eth0 -t 1 -b 15 -l 4096 -c 5\n"
239  "\n"
240  "Mandatory OPTIONS:\n"
241  "\n"
242  " -i, --interfaces Ethernet interfaces for packet I/O, comma-separated, no\n"
243  " spaces.\n"
244  "\n"
245  "Optional OPTIONS:\n"
246  "\n"
247  " -t, --copy_type Type of copy. %u by default.\n"
248  " 0: SW\n"
249  " 1: DMA with event completion\n"
250  " 2: DMA with poll completion\n"
251  " -b, --burst_size Copy burst size. This many packets are accumulated before\n"
252  " copy. %u by default.\n"
253  " -n, --num_pkts Number of packet buffers allocated for packet I/O pool.\n"
254  " %u by default.\n"
255  " -l, --pkt_len Maximum size of packet buffers in packet I/O pool. %u by\n"
256  " default.\n"
257  " -c, --worker_count Amount of workers. %u by default.\n"
258  " -C, --cache_size Maximum cache size for pools. %u by default.\n"
259  " -T, --time_sec Time in seconds to run. 0 means infinite. %u by default.\n"
260  " -s, --seg_free Use DMA source segment free offload. Disabled by default.\n"
261  " -h, --help This help.\n"
262  "\n", DEF_CPY_TYPE, dyn_defs->burst_size, dyn_defs->num_pkts, dyn_defs->pkt_len,
263  DEF_WORKERS, dyn_defs->cache_size, DEF_TIME);
264 }
265 
266 static void parse_interfaces(prog_config_t *config, const char *optarg)
267 {
268  char *tmp_str = strdup(optarg), *tmp;
269 
270  if (tmp_str == NULL)
271  return;
272 
273  tmp = strtok(tmp_str, DELIMITER);
274 
275  while (tmp && config->num_ifs < MAX_IFS) {
276  config->pktios[config->num_ifs].name = strdup(tmp);
277 
278  if (config->pktios[config->num_ifs].name != NULL)
279  ++config->num_ifs;
280 
281  tmp = strtok(NULL, DELIMITER);
282  }
283 
284  free(tmp_str);
285 }
286 
287 static odp_bool_t get_stash_capa(odp_stash_capability_t *stash_capa, odp_stash_type_t *stash_type)
288 {
289  if (odp_stash_capability(stash_capa, ODP_STASH_TYPE_FIFO) == 0) {
290  *stash_type = ODP_STASH_TYPE_FIFO;
291  return true;
292  }
293 
294  if (odp_stash_capability(stash_capa, ODP_STASH_TYPE_DEFAULT) == 0) {
295  *stash_type = ODP_STASH_TYPE_DEFAULT;
296  return true;
297  }
298 
299  return false;
300 }
301 
302 static parse_result_t check_options(prog_config_t *config)
303 {
304  odp_dma_capability_t dma_capa;
305  uint32_t burst_size;
306  odp_stash_capability_t stash_capa;
307  const uint64_t obj_size = sizeof(odp_dma_transfer_id_t);
308  uint64_t max_num;
309  odp_pool_capability_t pool_capa;
310 
311  if (config->num_ifs == 0U) {
312  ODPH_ERR("Invalid number of interfaces: %u (min: 1, max: %u)\n", config->num_ifs,
313  MAX_IFS);
314  return PRS_NOK;
315  }
316 
317  if (config->copy_type != SW_COPY && config->copy_type != DMA_COPY_EV &&
318  config->copy_type != DMA_COPY_POLL) {
319  ODPH_ERR("Invalid copy type: %u\n", config->copy_type);
320  return PRS_NOK;
321  }
322 
323  if (config->num_thrs <= 0 || config->num_thrs > MAX_WORKERS) {
324  ODPH_ERR("Invalid worker count: %d (min: 1, max: %d)\n", config->num_thrs,
325  MAX_WORKERS);
326  return PRS_NOK;
327  }
328 
329  if (odp_dma_capability(&dma_capa) < 0) {
330  ODPH_ERR("Error querying DMA capabilities\n");
331  return PRS_NOK;
332  }
333 
334  if (config->seg_free && dma_capa.src_seg_free == 0) {
335  ODPH_ERR("Unsupported source segment free by DMA\n");
336  return PRS_NOT_SUP;
337  }
338 
339  if ((uint32_t)config->num_thrs > dma_capa.max_sessions) {
340  ODPH_ERR("Unsupported DMA session count: %d (max: %u)\n", config->num_thrs,
341  dma_capa.max_sessions);
342  return PRS_NOT_SUP;
343  }
344 
345  burst_size = ODPH_MIN(dma_capa.max_src_segs, dma_capa.max_dst_segs);
346  burst_size = ODPH_MIN(burst_size, MAX_BURST);
347 
348  if (config->burst_size == 0U || config->burst_size > burst_size) {
349  ODPH_ERR("Invalid segment count for DMA: %u (min: 1, max: %u)\n",
350  config->burst_size, burst_size);
351  return PRS_NOK;
352  }
353 
354  if (config->pkt_len > dma_capa.max_seg_len) {
355  ODPH_ERR("Invalid packet length for DMA: %u (max: %u)\n", config->pkt_len,
356  dma_capa.max_seg_len);
357  return PRS_NOK;
358  }
359 
360  config->num_inflight = dma_capa.max_transfers;
361 
362  if (odp_pool_capability(&pool_capa) < 0) {
363  ODPH_ERR("Error querying pool capabilities\n");
364  return PRS_NOK;
365  }
366 
367  if (config->cache_size < pool_capa.pkt.min_cache_size ||
368  config->cache_size > pool_capa.pkt.max_cache_size) {
369  ODPH_ERR("Invalid pool cache size: %u (min: %u, max: %u)\n", config->cache_size,
370  pool_capa.pkt.min_cache_size, pool_capa.pkt.max_cache_size);
371  return PRS_NOK;
372  }
373 
374  if (config->copy_type != SW_COPY)
375  config->trs_cache_size = ODPH_MIN(ODPH_MAX(config->cache_size,
376  pool_capa.buf.min_cache_size),
377  pool_capa.buf.max_cache_size);
378 
379  if (config->copy_type == DMA_COPY_EV) {
380  if ((dma_capa.compl_mode_mask & ODP_DMA_COMPL_EVENT) == 0U ||
381  !dma_capa.queue_type_sched) {
382  ODPH_ERR("Unsupported DMA completion mode: event (mode support: %x, "
383  "scheduled queue support: %u)\n", dma_capa.compl_mode_mask,
384  dma_capa.queue_type_sched);
385  return PRS_NOT_SUP;
386  }
387 
388  if ((uint32_t)config->num_thrs > dma_capa.pool.max_pools) {
389  ODPH_ERR("Invalid amount of DMA completion pools: %d (max: %u)\n",
390  config->num_thrs, dma_capa.pool.max_pools);
391  return PRS_NOK;
392  }
393 
394  if (config->num_inflight > dma_capa.pool.max_num) {
395  ODPH_ERR("Invalid amount of DMA completion events: %u (max: %u)\n",
396  config->num_inflight, dma_capa.pool.max_num);
397  return PRS_NOK;
398  }
399 
400  config->compl_cache_size = ODPH_MIN(ODPH_MAX(config->cache_size,
401  dma_capa.pool.min_cache_size),
402  dma_capa.pool.max_cache_size);
403  } else if (config->copy_type == DMA_COPY_POLL) {
404  if ((dma_capa.compl_mode_mask & ODP_DMA_COMPL_POLL) == 0U) {
405  ODPH_ERR("Unsupported DMA completion mode: poll (mode support: %x)\n",
406  dma_capa.compl_mode_mask);
407  return PRS_NOT_SUP;
408  }
409 
410  if (!get_stash_capa(&stash_capa, &config->stash_type)) {
411  ODPH_ERR("Error querying stash capabilities\n");
412  return PRS_NOK;
413  }
414 
415  if ((uint32_t)config->num_thrs > stash_capa.max_stashes) {
416  ODPH_ERR("Invalid amount of stashes: %d (max: %u)\n", config->num_thrs,
417  stash_capa.max_stashes);
418  return PRS_NOK;
419  }
420 
421  if (obj_size == sizeof(uint8_t)) {
422  max_num = stash_capa.max_num.u8;
423  } else if (obj_size == sizeof(uint16_t)) {
424  max_num = stash_capa.max_num.u16;
425  } else if (obj_size <= sizeof(uint32_t)) {
426  max_num = stash_capa.max_num.u32;
427  } else if (obj_size <= sizeof(uint64_t)) {
428  max_num = stash_capa.max_num.u64;
429  } else if (obj_size <= sizeof(odp_u128_t)) {
430  max_num = stash_capa.max_num.u128;
431  } else {
432  ODPH_ERR("Invalid stash object size: %" PRIu64 "\n", obj_size);
433  return PRS_NOK;
434  }
435 
436  if (config->num_inflight > max_num) {
437  ODPH_ERR("Invalid stash size: %u (max: %" PRIu64 ")\n",
438  config->num_inflight, max_num);
439  return PRS_NOK;
440  }
441 
442  config->inflight_obj_size = obj_size;
443  config->stash_cache_size = ODPH_MIN(config->cache_size, stash_capa.max_cache_size);
444  }
445 
446  if (config->num_pkts == 0U ||
447  (pool_capa.pkt.max_num > 0U && config->num_pkts > pool_capa.pkt.max_num)) {
448  ODPH_ERR("Invalid pool packet count: %u (min: 1, max: %u)\n", config->num_pkts,
449  pool_capa.pkt.max_num);
450  return PRS_NOK;
451  }
452 
453  if (config->pkt_len == 0U ||
454  (pool_capa.pkt.max_len > 0U && config->pkt_len > pool_capa.pkt.max_len)) {
455  ODPH_ERR("Invalid pool packet length: %u (min: 1, max: %u)\n", config->pkt_len,
456  pool_capa.pkt.max_len);
457  return PRS_NOK;
458  }
459 
460  if (config->num_inflight > pool_capa.buf.max_num) {
461  ODPH_ERR("Invalid pool buffer count: %u (max: %u)\n", config->num_inflight,
462  pool_capa.buf.max_num);
463  return PRS_NOK;
464  }
465 
466  return PRS_OK;
467 }
468 
469 static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
470 {
471  int opt;
472 
473  static const struct option longopts[] = {
474  { "interfaces", required_argument, NULL, 'i' },
475  { "copy_type", required_argument, NULL, 't' },
476  { "burst_size", required_argument, NULL, 'b' },
477  { "num_pkts", required_argument, NULL, 'n' },
478  { "pkt_len", required_argument, NULL, 'l' },
479  { "worker_count", required_argument, NULL, 'c' },
480  { "cache_size", required_argument, NULL, 'C' },
481  { "time_sec", required_argument, NULL, 'T' },
482  { "seg_free", no_argument, NULL, 's' },
483  { "help", no_argument, NULL, 'h' },
484  { NULL, 0, NULL, 0 }
485  };
486 
487  static const char *shortopts = "i:t:b:n:l:c:C:T:sh";
488 
489  init_config(config);
490 
491  while (1) {
492  opt = getopt_long(argc, argv, shortopts, longopts, NULL);
493 
494  if (opt == -1)
495  break;
496 
497  switch (opt) {
498  case 'i':
499  parse_interfaces(config, optarg);
500  break;
501  case 't':
502  config->copy_type = atoi(optarg);
503  break;
504  case 'b':
505  config->burst_size = atoi(optarg);
506  break;
507  case 'n':
508  config->num_pkts = atoi(optarg);
509  break;
510  case 'l':
511  config->pkt_len = atoi(optarg);
512  break;
513  case 'c':
514  config->num_thrs = atoi(optarg);
515  break;
516  case 'C':
517  config->cache_size = atoi(optarg);
518  break;
519  case 'T':
520  config->time_sec = atof(optarg);
521  break;
522  case 's':
523  config->seg_free = 1;
524  break;
525  case 'h':
526  print_usage(&config->dyn_defs);
527  return PRS_TERM;
528  case '?':
529  default:
530  print_usage(&config->dyn_defs);
531  return PRS_NOK;
532  }
533  }
534 
535  return check_options(config);
536 }
537 
538 static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
539 {
540  struct sigaction action = { .sa_handler = terminate };
541 
542  if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
543  sigaddset(&action.sa_mask, SIGTERM) == -1 ||
544  sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
545  sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
546  ODPH_ERR("Error installing signal handler\n");
547  return PRS_NOK;
548  }
549 
550  return parse_options(argc, argv, config);
551 }
552 
553 static inline int send_packets(odp_pktout_queue_t queue, odp_packet_t pkts[], int num)
554 {
555  int ret = odp_pktout_send(queue, pkts, num);
556 
557  if (odp_unlikely(ret < num)) {
558  ret = ret < 0 ? 0 : ret;
559  odp_packet_free_multi(&pkts[ret], num - ret);
560  }
561 
562  return ret;
563 }
564 
565 static void sw_copy_and_send_packets(odp_packet_t pkts[], int num, pktio_t *pktio,
566  init_fn_t init_fn ODP_UNUSED, start_fn_t start_fn ODP_UNUSED,
567  thread_config_t *config)
568 {
569  odp_packet_t old_pkt, new_pkt;
570  odp_pool_t copy_pool = config->copy_pool;
571  odp_packet_t out_pkts[num];
572  int num_out_pkts = 0, num_sent;
573  stats_t *stats = &config->stats;
574 
575  for (int i = 0; i < num; ++i) {
576  old_pkt = pkts[i];
577  new_pkt = odp_packet_copy(old_pkt, copy_pool);
578 
579  if (new_pkt != ODP_PACKET_INVALID)
580  out_pkts[num_out_pkts++] = new_pkt;
581  else
582  ++stats->copy_errs;
583 
584  odp_packet_free(old_pkt);
585  }
586 
587  if (num_out_pkts > 0) {
588  num_sent = send_packets(pktio->out_qs[config->thr_idx % pktio->num_out_qs],
589  out_pkts, num_out_pkts);
590  stats->fwd_pkts += num_sent;
591  stats->discards += num_out_pkts - num_sent;
592  }
593 }
594 
595 static transfer_t *init_dma_ev_trs(odp_dma_transfer_param_t *trs_param,
596  odp_dma_compl_param_t *compl_param, odp_dma_seg_t *src_segs,
597  odp_dma_seg_t *dst_segs, pktio_t *pktio,
598  thread_config_t *config)
599 {
600  odp_buffer_t buf;
601  stats_t *stats = &config->stats;
602  transfer_t *trs;
603  odp_dma_compl_t c_ev;
604 
605  buf = odp_buffer_alloc(config->trs_pool);
606 
607  if (odp_unlikely(buf == ODP_BUFFER_INVALID)) {
608  ++stats->buf_alloc_errs;
609  return NULL;
610  }
611 
612  trs = (transfer_t *)odp_buffer_addr(buf);
613  trs->num = 0;
614  trs->pktio = pktio;
615  trs_param->src_format = ODP_DMA_FORMAT_PACKET;
616  trs_param->dst_format = ODP_DMA_FORMAT_PACKET;
617  trs_param->num_src = 0U;
618  trs_param->num_dst = 0U;
619  trs_param->src_seg = src_segs;
620  trs_param->dst_seg = dst_segs;
621  if (config->prog_config->seg_free)
622  trs_param->opts.seg_free = 1;
623  compl_param->compl_mode = ODP_DMA_COMPL_EVENT;
624  c_ev = odp_dma_compl_alloc(config->compl_pool);
625 
626  if (odp_unlikely(c_ev == ODP_DMA_COMPL_INVALID)) {
627  odp_buffer_free(buf);
628  ++stats->compl_alloc_errs;
629  return NULL;
630  }
631 
632  compl_param->event = odp_dma_compl_to_event(c_ev);
633  compl_param->queue = config->compl_q;
634  compl_param->user_ptr = buf;
635  memset(src_segs, 0, sizeof(*src_segs) * MAX_BURST);
636  memset(dst_segs, 0, sizeof(*dst_segs) * MAX_BURST);
637 
638  return trs;
639 }
640 
641 static transfer_t *init_dma_poll_trs(odp_dma_transfer_param_t *trs_param,
642  odp_dma_compl_param_t *compl_param, odp_dma_seg_t *src_segs,
643  odp_dma_seg_t *dst_segs, pktio_t *pktio,
644  thread_config_t *config)
645 {
646  odp_buffer_t buf;
647  stats_t *stats = &config->stats;
648  transfer_t *trs;
649 
650  buf = odp_buffer_alloc(config->trs_pool);
651 
652  if (odp_unlikely(buf == ODP_BUFFER_INVALID)) {
653  ++stats->buf_alloc_errs;
654  return NULL;
655  }
656 
657  trs = (transfer_t *)odp_buffer_addr(buf);
658  trs->num = 0;
659  trs->pktio = pktio;
660  trs_param->src_format = ODP_DMA_FORMAT_PACKET;
661  trs_param->dst_format = ODP_DMA_FORMAT_PACKET;
662  trs_param->num_src = 0U;
663  trs_param->num_dst = 0U;
664  trs_param->src_seg = src_segs;
665  trs_param->dst_seg = dst_segs;
666  if (config->prog_config->seg_free)
667  trs_param->opts.seg_free = 1;
668  compl_param->compl_mode = ODP_DMA_COMPL_POLL;
669  compl_param->transfer_id = odp_dma_transfer_id_alloc(config->dma_handle);
670 
672  odp_buffer_free(buf);
673  ++stats->compl_alloc_errs;
674  return NULL;
675  }
676 
677  compl_param->user_ptr = buf;
678  memset(src_segs, 0, sizeof(*src_segs) * MAX_BURST);
679  memset(dst_segs, 0, sizeof(*dst_segs) * MAX_BURST);
680 
681  return trs;
682 }
683 
684 static odp_bool_t start_dma_ev_trs(odp_dma_transfer_param_t *trs_param,
685  odp_dma_compl_param_t *compl_param, thread_config_t *config)
686 {
687  const int ret = odp_dma_transfer_start(config->dma_handle, trs_param, compl_param);
688 
689  if (odp_unlikely(ret <= 0)) {
690  odp_buffer_free(compl_param->user_ptr);
691  odp_event_free(compl_param->event);
692  return false;
693  }
694 
695  return true;
696 }
697 
698 static odp_bool_t start_dma_poll_trs(odp_dma_transfer_param_t *trs_param,
699  odp_dma_compl_param_t *compl_param, thread_config_t *config)
700 {
701  const int ret = odp_dma_transfer_start(config->dma_handle, trs_param, compl_param);
702 
703  if (odp_unlikely(ret <= 0)) {
704  odp_buffer_free(compl_param->user_ptr);
705  odp_dma_transfer_id_free(config->dma_handle, compl_param->transfer_id);
706  return false;
707  }
708 
709  if (odp_unlikely(odp_stash_put(config->inflight_stash, &compl_param->transfer_id, 1) != 1))
710  /* Should not happen, but make it visible if it somehow does */
711  ODPH_ABORT("DMA inflight transfer stash overflow, aborting");
712 
713  return true;
714 }
715 
716 static void dma_copy(odp_packet_t pkts[], int num, pktio_t *pktio, init_fn_t init_fn,
717  start_fn_t start_fn, thread_config_t *config)
718 {
719  odp_dma_transfer_param_t trs_param;
720  odp_dma_compl_param_t compl_param;
721  odp_packet_t pkt;
722  transfer_t *trs = NULL;
723  odp_dma_seg_t src_segs[MAX_BURST], dst_segs[MAX_BURST];
724  uint32_t num_segs = 0U, pkt_len;
725  odp_pool_t copy_pool = config->copy_pool;
726  stats_t *stats = &config->stats;
727 
728  odp_dma_transfer_param_init(&trs_param);
729  odp_dma_compl_param_init(&compl_param);
730 
731  for (int i = 0; i < num; ++i) {
732  pkt = pkts[i];
733 
734  if (odp_unlikely(trs == NULL)) {
735  trs = init_fn(&trs_param, &compl_param, src_segs, dst_segs, pktio, config);
736 
737  if (trs == NULL) {
738  odp_packet_free(pkt);
739  continue;
740  }
741  }
742 
743  pkt_len = odp_packet_len(pkt);
744  src_segs[num_segs].packet = pkt;
745  src_segs[num_segs].len = pkt_len;
746  dst_segs[num_segs].packet = odp_packet_alloc(copy_pool, pkt_len);
747 
748  if (odp_unlikely(dst_segs[num_segs].packet == ODP_PACKET_INVALID)) {
749  odp_packet_free(pkt);
750  ++stats->pkt_alloc_errs;
751  continue;
752  }
753 
754  dst_segs[num_segs].len = pkt_len;
755  trs->src_pkts[num_segs] = src_segs[num_segs].packet;
756  trs->dst_pkts[num_segs] = dst_segs[num_segs].packet;
757  ++trs->num;
758  ++trs_param.num_src;
759  ++trs_param.num_dst;
760  ++num_segs;
761  }
762 
763  if (num_segs > 0U)
764  if (odp_unlikely(!start_fn(&trs_param, &compl_param, config))) {
765  odp_packet_free_multi(trs->src_pkts, trs->num);
766  odp_packet_free_multi(trs->dst_pkts, trs->num);
767  ++stats->start_errs;
768  }
769 }
770 
771 static void drain_events(thread_config_t *config ODP_UNUSED)
772 {
773  odp_event_t ev;
774  odp_event_type_t type;
775  odp_dma_result_t res;
776  odp_buffer_t buf;
777  transfer_t *trs;
778  const odp_bool_t seg_free = config->prog_config->seg_free;
779 
780  while (true) {
782 
783  if (ev == ODP_EVENT_INVALID)
784  break;
785 
786  type = odp_event_type(ev);
787 
788  if (type == ODP_EVENT_DMA_COMPL) {
789  memset(&res, 0, sizeof(res));
791  buf = (odp_buffer_t)res.user_ptr;
792  trs = (transfer_t *)odp_buffer_addr(buf);
793  if (!seg_free)
794  odp_packet_free_multi(trs->src_pkts, trs->num);
795  odp_packet_free_multi(trs->dst_pkts, trs->num);
796  odp_buffer_free(buf);
797  }
798 
799  odp_event_free(ev);
800  }
801 }
802 
803 static void drain_polled(thread_config_t *config)
804 {
806  odp_dma_result_t res;
807  int ret;
808  odp_buffer_t buf;
809  transfer_t *trs;
810  const odp_bool_t seg_free = config->prog_config->seg_free;
811 
812  while (true) {
813  if (odp_stash_get(config->inflight_stash, &id, 1) != 1)
814  break;
815 
816  memset(&res, 0, sizeof(res));
817 
818  do {
819  ret = odp_dma_transfer_done(config->dma_handle, id, &res);
820  } while (ret == 0);
821 
822  odp_dma_transfer_id_free(config->dma_handle, id);
823 
824  if (ret < 0)
825  continue;
826 
827  buf = (odp_buffer_t)res.user_ptr;
828  trs = (transfer_t *)odp_buffer_addr(buf);
829  if (!seg_free)
830  odp_packet_free_multi(trs->src_pkts, trs->num);
831  odp_packet_free_multi(trs->dst_pkts, trs->num);
832  odp_buffer_free(buf);
833  }
834 }
835 
836 static odp_bool_t setup_copy(prog_config_t *config)
837 {
838  odp_pool_param_t pool_param;
839  thread_config_t *thr;
840  const odp_dma_param_t dma_param = {
842  .type = ODP_DMA_TYPE_COPY,
843  .compl_mode_mask = ODP_DMA_COMPL_EVENT | ODP_DMA_COMPL_POLL,
844  .mt_mode = ODP_DMA_MT_SERIAL,
845  .order = ODP_DMA_ORDER_NONE };
846  odp_dma_pool_param_t compl_pool_param;
847  odp_queue_param_t queue_param;
848  odp_stash_param_t stash_param;
849 
850  odp_pool_param_init(&pool_param);
851  pool_param.pkt.seg_len = config->pkt_len;
852  pool_param.pkt.len = config->pkt_len;
853  pool_param.pkt.num = config->num_pkts;
854  pool_param.pkt.cache_size = config->cache_size;
855  pool_param.type = ODP_POOL_PACKET;
856  config->copy_pool = odp_pool_create(PROG_NAME "_copy", &pool_param);
857 
858  if (config->copy_pool == ODP_POOL_INVALID) {
859  ODPH_ERR("Error creating packet copy pool\n");
860  return false;
861  }
862 
863  if (config->copy_type == SW_COPY) {
864  config->pkt_fn = sw_copy_and_send_packets;
865 
866  for (int i = 0; i < config->num_thrs; ++i)
867  config->thread_config[i].copy_pool = config->copy_pool;
868 
869  return true;
870  }
871 
872  pool_param.buf.num = config->num_inflight;
873  pool_param.buf.size = sizeof(transfer_t);
874  pool_param.buf.cache_size = config->trs_cache_size;
875  pool_param.type = ODP_POOL_BUFFER;
876  config->trs_pool = odp_pool_create(PROG_NAME "_dma_trs", &pool_param);
877 
878  if (config->trs_pool == ODP_POOL_INVALID) {
879  ODPH_ERR("Error creating DMA transfer tracking pool\n");
880  return false;
881  }
882 
883  for (int i = 0; i < config->num_thrs; ++i) {
884  thr = &config->thread_config[i];
885  thr->copy_pool = config->copy_pool;
886  thr->trs_pool = config->trs_pool;
887  thr->dma_handle = odp_dma_create(PROG_NAME "_dma", &dma_param);
888 
889  if (thr->dma_handle == ODP_DMA_INVALID) {
890  ODPH_ERR("Error creating DMA session\n");
891  return false;
892  }
893 
894  if (config->copy_type == DMA_COPY_EV) {
895  odp_dma_pool_param_init(&compl_pool_param);
896  compl_pool_param.num = config->num_inflight;
897  compl_pool_param.cache_size = config->compl_cache_size;
898  thr->compl_pool = odp_dma_pool_create(PROG_NAME "_dma_compl",
899  &compl_pool_param);
900 
901  if (thr->compl_pool == ODP_POOL_INVALID) {
902  ODPH_ERR("Error creating DMA event completion pool\n");
903  return false;
904  }
905 
906  odp_queue_param_init(&queue_param);
907  queue_param.type = ODP_QUEUE_TYPE_SCHED;
908  queue_param.sched.sync = ODP_SCHED_SYNC_PARALLEL;
909  queue_param.sched.prio = odp_schedule_max_prio();
910  thr->compl_q = odp_queue_create(PROG_NAME "_dma_compl", &queue_param);
911 
912  if (thr->compl_q == ODP_QUEUE_INVALID) {
913  ODPH_ERR("Error creating DMA completion queue\n");
914  return false;
915  }
916 
917  config->init_fn = init_dma_ev_trs;
918  config->start_fn = start_dma_ev_trs;
919  config->drain_fn = drain_events;
920  } else {
921  odp_stash_param_init(&stash_param);
922  stash_param.type = config->stash_type;
923  stash_param.put_mode = ODP_STASH_OP_LOCAL;
924  stash_param.get_mode = ODP_STASH_OP_LOCAL;
925  stash_param.num_obj = config->num_inflight;
926  stash_param.obj_size = config->inflight_obj_size;
927  stash_param.cache_size = config->stash_cache_size;
928  thr->inflight_stash = odp_stash_create("_dma_inflight", &stash_param);
929 
930  if (thr->inflight_stash == ODP_STASH_INVALID) {
931  ODPH_ERR("Error creating DMA inflight transfer stash\n");
932  return false;
933  }
934 
935  config->init_fn = init_dma_poll_trs;
936  config->start_fn = start_dma_poll_trs;
937  config->drain_fn = drain_polled;
938  }
939  }
940 
941  config->pkt_fn = dma_copy;
942 
943  return true;
944 }
945 
946 static odp_bool_t setup_pktios(prog_config_t *config)
947 {
948  odp_pool_param_t pool_param;
949  pktio_t *pktio;
950  odp_pktio_param_t pktio_param;
952  uint32_t num_input_qs, num_output_qs;
953  odp_pktin_queue_param_t pktin_param;
954  odp_pktout_queue_param_t pktout_param;
955 
956  odp_pool_param_init(&pool_param);
957  pool_param.pkt.seg_len = config->pkt_len;
958  pool_param.pkt.len = config->pkt_len;
959  pool_param.pkt.num = config->num_pkts;
960  pool_param.pkt.cache_size = config->cache_size;
961  pool_param.type = ODP_POOL_PACKET;
962  config->pktio_pool = odp_pool_create(PROG_NAME, &pool_param);
963 
964  if (config->pktio_pool == ODP_POOL_INVALID) {
965  ODPH_ERR("Error creating packet I/O pool\n");
966  return false;
967  }
968 
969  for (uint32_t i = 0U; i < config->num_ifs; ++i) {
970  pktio = &config->pktios[i];
971  odp_pktio_param_init(&pktio_param);
972  pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
973  pktio_param.out_mode = ODP_PKTOUT_MODE_DIRECT;
974  pktio->handle = odp_pktio_open(pktio->name, config->pktio_pool, &pktio_param);
975 
976  if (pktio->handle == ODP_PKTIO_INVALID) {
977  ODPH_ERR("Error opening packet I/O (%s)\n", pktio->name);
978  return false;
979  }
980 
981  config->pktio_idx_map[odp_pktio_index(pktio->handle)] = i;
982 
983  if (odp_pktio_capability(pktio->handle, &capa) < 0) {
984  ODPH_ERR("Error querying packet I/O capabilities (%s)\n", pktio->name);
985  return false;
986  }
987 
988  num_input_qs = ODPH_MIN((uint32_t)config->num_thrs, capa.max_input_queues);
989  num_output_qs = ODPH_MIN((uint32_t)config->num_thrs, capa.max_output_queues);
990  num_output_qs = ODPH_MIN(num_output_qs, MAX_OUT_QS);
991  odp_pktin_queue_param_init(&pktin_param);
992 
993  if (num_input_qs > 1) {
994  pktin_param.hash_enable = true;
995  pktin_param.hash_proto.proto.ipv4 = 1U;
996  }
997 
998  pktin_param.num_queues = num_input_qs;
1000 
1001  if (odp_pktin_queue_config(pktio->handle, &pktin_param) < 0) {
1002  ODPH_ERR("Error configuring packet I/O input queues (%s)\n", pktio->name);
1003  return false;
1004  }
1005 
1006  odp_pktout_queue_param_init(&pktout_param);
1007 
1008  if (num_output_qs == (uint32_t)config->num_thrs)
1009  pktout_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
1010 
1011  pktout_param.num_queues = num_output_qs;
1012  pktio->num_out_qs = num_output_qs;
1013 
1014  if (odp_pktout_queue_config(pktio->handle, &pktout_param) < 0) {
1015  ODPH_ERR("Error configuring packet I/O output queues (%s)\n", pktio->name);
1016  return false;
1017  }
1018 
1019  if (odp_pktout_queue(pktio->handle, pktio->out_qs, num_output_qs) !=
1020  (int)num_output_qs) {
1021  ODPH_ERR("Error querying packet I/O output queues (%s)\n", pktio->name);
1022  return false;
1023  }
1024 
1025  if (odp_pktio_start(pktio->handle) < 0) {
1026  ODPH_ERR("Error starting packet I/O (%s)\n", pktio->name);
1027  return false;
1028  }
1029  }
1030 
1031  return true;
1032 }
1033 
1034 static inline void send_dma_poll_trs_pkts(int burst_size, thread_config_t *config)
1035 {
1036  odp_stash_t stash_handle = config->inflight_stash;
1037  odp_dma_transfer_id_t ids[burst_size], id;
1038  int32_t num;
1039  odp_dma_t dma_handle = config->dma_handle;
1040  odp_dma_result_t res;
1041  int ret;
1042  odp_buffer_t buf;
1043  transfer_t *trs;
1044  pktio_t *pktio;
1045  int num_sent;
1046  stats_t *stats = &config->stats;
1047  const odp_bool_t seg_free = config->prog_config->seg_free;
1048 
1049  while (true) {
1050  num = odp_stash_get(stash_handle, &ids, burst_size);
1051 
1052  if (num <= 0)
1053  break;
1054 
1055  for (int32_t i = 0; i < num; ++i) {
1056  id = ids[i];
1057  ret = odp_dma_transfer_done(dma_handle, id, &res);
1058 
1059  if (ret == 0) {
1060  if (odp_unlikely(odp_stash_put(stash_handle, &id, 1) != 1))
1061  /* Should not happen, but make it visible if it somehow
1062  * does */
1063  ODPH_ABORT("DMA inflight transfer stash overflow,"
1064  " aborting");
1065 
1066  ++stats->trs_polled;
1067  continue;
1068  }
1069 
1070  odp_dma_transfer_id_free(dma_handle, id);
1071 
1072  if (ret < 0) {
1073  ++stats->trs_poll_errs;
1074  continue;
1075  }
1076 
1077  buf = (odp_buffer_t)res.user_ptr;
1078  trs = (transfer_t *)odp_buffer_addr(buf);
1079 
1080  if (res.success) {
1081  pktio = trs->pktio;
1082  num_sent = send_packets(pktio->out_qs[config->thr_idx %
1083  pktio->num_out_qs],
1084  trs->dst_pkts, trs->num);
1085  ++stats->trs;
1086  stats->fwd_pkts += num_sent;
1087  stats->discards += trs->num - num_sent;
1088  if (!seg_free)
1089  odp_packet_free_multi(trs->src_pkts, trs->num);
1090  } else {
1091  odp_packet_free_multi(trs->src_pkts, trs->num);
1092  odp_packet_free_multi(trs->dst_pkts, trs->num);
1093  ++stats->trs_errs;
1094  }
1095 
1096  odp_buffer_free(buf);
1097  }
1098  }
1099 }
1100 
1101 static inline void send_dma_ev_trs_pkts(odp_dma_compl_t compl_ev, thread_config_t *config)
1102 {
1103  odp_dma_result_t res;
1104  odp_buffer_t buf;
1105  transfer_t *trs;
1106  pktio_t *pktio;
1107  int num_sent;
1108  stats_t *stats = &config->stats;
1109  const odp_bool_t seg_free = config->prog_config->seg_free;
1110 
1111  memset(&res, 0, sizeof(res));
1112  odp_dma_compl_result(compl_ev, &res);
1113  buf = (odp_buffer_t)res.user_ptr;
1114  trs = (transfer_t *)odp_buffer_addr(buf);
1115 
1116  if (res.success) {
1117  pktio = trs->pktio;
1118  num_sent = send_packets(pktio->out_qs[config->thr_idx % pktio->num_out_qs],
1119  trs->dst_pkts, trs->num);
1120  ++stats->trs;
1121  stats->fwd_pkts += num_sent;
1122  stats->discards += trs->num - num_sent;
1123 
1124  if (!seg_free)
1125  odp_packet_free_multi(trs->src_pkts, trs->num);
1126  } else {
1127  odp_packet_free_multi(trs->src_pkts, trs->num);
1128  odp_packet_free_multi(trs->dst_pkts, trs->num);
1129  ++stats->trs_errs;
1130  }
1131 
1132  odp_buffer_free(buf);
1133  odp_dma_compl_free(compl_ev);
1134 }
1135 
1136 static inline void push_packet(odp_packet_t pkt, pkt_vec_t pkt_vecs[], uint8_t *pktio_idx_map)
1137 {
1138  uint8_t idx = pktio_idx_map[odp_packet_input_index(pkt)];
1139  pkt_vec_t *pkt_vec = &pkt_vecs[idx];
1140 
1141  pkt_vec->pkts[pkt_vec->num++] = pkt;
1142 }
1143 
1144 static inline void pop_packets(pkt_vec_t *pkt_vec, int num_procd)
1145 {
1146  pkt_vec->num -= num_procd;
1147 
1148  for (int i = 0, j = num_procd; i < pkt_vec->num; ++i, ++j)
1149  pkt_vec->pkts[i] = pkt_vec->pkts[j];
1150 }
1151 
1152 static void free_pending_packets(pkt_vec_t pkt_vecs[], uint32_t num_ifs, stats_t *stats)
1153 {
1154  for (uint32_t i = 0U; i < num_ifs; ++i) {
1155  stats->pkt_cleanup += pkt_vecs[i].num;
1156  odp_packet_free_multi(pkt_vecs[i].pkts, pkt_vecs[i].num);
1157  }
1158 }
1159 
1160 static int process_packets(void *args)
1161 {
1162  thread_config_t *config = args;
1163  const uint8_t num_ifs = config->prog_config->num_ifs;
1164  pkt_vec_t pkt_vecs[num_ifs], *pkt_vec;
1165  odp_atomic_u32_t *is_running = &config->prog_config->is_running;
1166  uint64_t c1, c2, c3, c4, cdiff = 0U, rounds = 0U;
1167  const uint8_t copy_type = config->prog_config->copy_type;
1168  const int burst_size = config->prog_config->burst_size;
1169  odp_event_t evs[burst_size];
1170  int num_evs;
1171  odp_event_t ev;
1172  odp_event_type_t type;
1173  uint8_t *pktio_map = config->prog_config->pktio_idx_map;
1174  stats_t *stats = &config->stats;
1175  init_fn_t init_fn = config->prog_config->init_fn;
1176  start_fn_t start_fn = config->prog_config->start_fn;
1177  pkt_fn_t pkt_fn = config->prog_config->pkt_fn;
1178 
1179  for (uint32_t i = 0U; i < num_ifs; ++i) {
1180  pkt_vecs[i].pktio = &config->prog_config->pktios[i];
1181  pkt_vecs[i].num = 0;
1182  }
1183 
1184  config->thr_idx = odp_thread_id();
1185  odp_barrier_wait(&config->prog_config->init_barrier);
1186  c1 = odp_cpu_cycles();
1187 
1188  while (odp_atomic_load_u32(is_running)) {
1189  c3 = odp_cpu_cycles();
1190  num_evs = odp_schedule_multi_no_wait(NULL, evs, burst_size);
1191  c4 = odp_cpu_cycles();
1192  cdiff += odp_cpu_cycles_diff(c4, c3);
1193  ++rounds;
1194 
1195  if (copy_type == DMA_COPY_POLL)
1196  send_dma_poll_trs_pkts(burst_size, config);
1197 
1198  if (num_evs == 0)
1199  continue;
1200 
1201  for (int i = 0; i < num_evs; ++i) {
1202  ev = evs[i];
1203  type = odp_event_type(ev);
1204 
1205  if (type == ODP_EVENT_DMA_COMPL) {
1206  send_dma_ev_trs_pkts(odp_dma_compl_from_event(ev), config);
1207  } else if (type == ODP_EVENT_PACKET) {
1208  push_packet(odp_packet_from_event(ev), pkt_vecs, pktio_map);
1209  } else {
1210  odp_event_free(ev);
1211  ++stats->discards;
1212  }
1213  }
1214 
1215  for (uint32_t i = 0U; i < num_ifs; ++i) {
1216  pkt_vec = &pkt_vecs[i];
1217 
1218  if (pkt_vec->num >= burst_size) {
1219  pkt_fn(pkt_vec->pkts, burst_size, pkt_vec->pktio, init_fn,
1220  start_fn, config);
1221  pop_packets(pkt_vec, burst_size);
1222  }
1223  }
1224  }
1225 
1226  c2 = odp_cpu_cycles();
1227  stats->sched_cc = cdiff;
1228  stats->tot_cc = odp_cpu_cycles_diff(c2, c1);
1229  stats->sched_rounds = rounds;
1230  free_pending_packets(pkt_vecs, num_ifs, stats);
1231  odp_barrier_wait(&config->prog_config->term_barrier);
1232 
1233  if (config->prog_config->drain_fn)
1234  config->prog_config->drain_fn(config);
1235 
1236  return 0;
1237 }
1238 
1239 static odp_bool_t setup_workers(prog_config_t *config)
1240 {
1241  odp_cpumask_t cpumask;
1242  int num_workers;
1243  odph_thread_common_param_t thr_common;
1244  odph_thread_param_t thr_param[config->num_thrs];
1245 
1246  num_workers = odp_cpumask_default_worker(&cpumask, config->num_thrs);
1247  odph_thread_common_param_init(&thr_common);
1248  thr_common.instance = config->odp_instance;
1249  thr_common.cpumask = &cpumask;
1250 
1251  for (int i = 0; i < config->num_thrs; ++i) {
1252  odph_thread_param_init(&thr_param[i]);
1253  thr_param[i].start = process_packets;
1254  thr_param[i].thr_type = ODP_THREAD_WORKER;
1255  config->thread_config[i].prog_config = config;
1256  thr_param[i].arg = &config->thread_config[i];
1257  }
1258 
1259  num_workers = odph_thread_create(config->thread_tbl, &thr_common, thr_param, num_workers);
1260 
1261  if (num_workers != config->num_thrs) {
1262  ODPH_ERR("Error configuring worker threads\n");
1263  return false;
1264  }
1265 
1266  return true;
1267 }
1268 
1269 static odp_bool_t setup_test(prog_config_t *config)
1270 {
1271  odp_barrier_init(&config->init_barrier, config->num_thrs + 1);
1272  odp_barrier_init(&config->term_barrier, config->num_thrs + 1);
1273 
1274  if (!setup_copy(config))
1275  return false;
1276 
1277  if (!setup_pktios(config))
1278  return false;
1279 
1280  if (!setup_workers(config))
1281  return false;
1282 
1283  odp_barrier_wait(&config->init_barrier);
1284 
1285  return true;
1286 }
1287 
1288 static void stop_test(prog_config_t *config)
1289 {
1290  for (uint32_t i = 0U; i < config->num_ifs; ++i)
1291  if (config->pktios[i].handle != ODP_PKTIO_INVALID)
1292  (void)odp_pktio_stop(config->pktios[i].handle);
1293 
1294  odp_barrier_wait(&config->term_barrier);
1295  (void)odph_thread_join(config->thread_tbl, config->num_thrs);
1296 }
1297 
1298 static void teardown(prog_config_t *config)
1299 {
1300  thread_config_t *thr;
1301 
1302  for (uint32_t i = 0U; i < config->num_ifs; ++i) {
1303  free(config->pktios[i].name);
1304 
1305  if (config->pktios[i].handle != ODP_PKTIO_INVALID)
1306  (void)odp_pktio_close(config->pktios[i].handle);
1307  }
1308 
1309  if (config->pktio_pool != ODP_POOL_INVALID)
1310  (void)odp_pool_destroy(config->pktio_pool);
1311 
1312  for (int i = 0; i < config->num_thrs; ++i) {
1313  thr = &config->thread_config[i];
1314 
1315  if (thr->inflight_stash != ODP_STASH_INVALID)
1316  (void)odp_stash_destroy(thr->inflight_stash);
1317 
1318  if (thr->compl_q != ODP_QUEUE_INVALID)
1319  (void)odp_queue_destroy(thr->compl_q);
1320 
1321  if (thr->compl_pool != ODP_POOL_INVALID)
1322  (void)odp_pool_destroy(thr->compl_pool);
1323 
1324  if (thr->dma_handle != ODP_DMA_INVALID)
1325  (void)odp_dma_destroy(thr->dma_handle);
1326  }
1327 
1328  if (config->copy_pool != ODP_POOL_INVALID)
1329  (void)odp_pool_destroy(config->copy_pool);
1330 
1331  if (config->trs_pool != ODP_POOL_INVALID)
1332  (void)odp_pool_destroy(config->trs_pool);
1333 }
1334 
1335 static void print_stats(const prog_config_t *config)
1336 {
1337  const stats_t *stats;
1338  const char *align1 = config->copy_type == DMA_COPY_EV ? " " : "";
1339  const char *align2 = config->copy_type == SW_COPY ? " " :
1340  config->copy_type == DMA_COPY_EV ? " " :
1341  " ";
1342 
1343  printf("\n==================\n\n"
1344  "DMA forwarder done\n\n"
1345  " copy mode: %s\n"
1346  " burst size: %u\n"
1347  " packet length: %u\n"
1348  " max cache size: %u\n"
1349  " use DMA source segment free: %s\n", config->copy_type == SW_COPY ? "SW" :
1350  config->copy_type == DMA_COPY_EV ? "DMA-event" : "DMA-poll",
1351  config->burst_size, config->pkt_len, config->cache_size,
1352  config->seg_free ? "yes" : "no");
1353 
1354  for (int i = 0; i < config->num_thrs; ++i) {
1355  stats = &config->thread_config[i].stats;
1356 
1357  printf("\n worker %d:\n", i);
1358 
1359  if (config->copy_type == SW_COPY) {
1360  printf(" packet copy errors: %" PRIu64 "\n",
1361  stats->copy_errs);
1362  } else {
1363  printf(" successful DMA transfers: %s%" PRIu64 "\n"
1364  " DMA transfer start errors: %s%" PRIu64 "\n"
1365  " DMA transfer errors: %s%" PRIu64 "\n"
1366  " transfer buffer allocation errors: %s%" PRIu64 "\n"
1367  " copy packet allocation errors: %s%" PRIu64 "\n",
1368  align1, stats->trs, align1, stats->start_errs, align1,
1369  stats->trs_errs, align1, stats->buf_alloc_errs, align1,
1370  stats->pkt_alloc_errs);
1371 
1372  if (config->copy_type == DMA_COPY_EV)
1373  printf(" completion event allocation errors: %" PRIu64 "\n",
1374  stats->compl_alloc_errs);
1375  else
1376  printf(" transfer ID allocation errors: %" PRIu64 "\n"
1377  " transfer poll errors: %" PRIu64 "\n"
1378  " transfers polled: %" PRIu64 "\n",
1379  stats->compl_alloc_errs, stats->trs_poll_errs,
1380  stats->trs_polled);
1381  }
1382 
1383  printf(" packets forwarded:%s%" PRIu64 "\n"
1384  " packets dropped: %s%" PRIu64 "\n"
1385  " packets cleanup: %s%" PRIu64 "\n"
1386  " call cycles per schedule round:\n"
1387  " total: %" PRIu64 "\n"
1388  " schedule: %" PRIu64 "\n"
1389  " rounds: %" PRIu64 "\n", align2, stats->fwd_pkts, align2,
1390  stats->discards, align2, stats->pkt_cleanup, DIV_IF(stats->tot_cc,
1391  stats->sched_rounds), DIV_IF(stats->sched_cc, stats->sched_rounds),
1392  stats->sched_rounds);
1393  }
1394 
1395  printf("\n==================\n");
1396 }
1397 
1398 int main(int argc, char **argv)
1399 {
1400  odph_helper_options_t odph_opts;
1401  odp_init_t init_param;
1403  odp_shm_t shm_cfg = ODP_SHM_INVALID;
1404  int ret = EXIT_SUCCESS;
1405  parse_result_t parse_res;
1406 
1407  argc = odph_parse_options(argc, argv);
1408 
1409  if (odph_options(&odph_opts) == -1) {
1410  ODPH_ERR("Error while reading ODP helper options, exiting\n");
1411  exit(EXIT_FAILURE);
1412  }
1413 
1414  odp_init_param_init(&init_param);
1415  init_param.mem_model = odph_opts.mem_model;
1416 
1417  if (odp_init_global(&odp_instance, &init_param, NULL)) {
1418  ODPH_ERR("ODP global init failed, exiting\n");
1419  exit(EXIT_FAILURE);
1420  }
1421 
1423  ODPH_ERR("ODP local init failed, exiting\n");
1424  exit(EXIT_FAILURE);
1425  }
1426 
1427  shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
1428  0U);
1429 
1430  if (shm_cfg == ODP_SHM_INVALID) {
1431  ODPH_ERR("Error reserving shared memory\n");
1432  ret = EXIT_FAILURE;
1433  goto out;
1434  }
1435 
1436  prog_conf = odp_shm_addr(shm_cfg);
1437 
1438  if (prog_conf == NULL) {
1439  ODPH_ERR("Error resolving shared memory address\n");
1440  ret = EXIT_FAILURE;
1441  goto out;
1442  }
1443 
1444  parse_res = setup_program(argc, argv, prog_conf);
1445 
1446  if (parse_res == PRS_NOK) {
1447  ret = EXIT_FAILURE;
1448  goto out_test;
1449  }
1450 
1451  if (parse_res == PRS_TERM) {
1452  ret = EXIT_SUCCESS;
1453  goto out_test;
1454  }
1455 
1456  if (parse_res == PRS_NOT_SUP) {
1457  ret = EXIT_NOT_SUP;
1458  goto out_test;
1459  }
1460 
1461  if (odp_schedule_config(NULL) < 0) {
1462  ODPH_ERR("Error configuring scheduler\n");
1463  ret = EXIT_FAILURE;
1464  goto out_test;
1465  }
1466 
1467  prog_conf->odp_instance = odp_instance;
1468  odp_atomic_init_u32(&prog_conf->is_running, 1U);
1469 
1470  if (!setup_test(prog_conf)) {
1471  ret = EXIT_FAILURE;
1472  goto out_test;
1473  }
1474 
1475  if (prog_conf->time_sec > 0.001) {
1476  struct timespec ts;
1477 
1478  ts.tv_sec = prog_conf->time_sec;
1479  ts.tv_nsec = (prog_conf->time_sec - ts.tv_sec) * ODP_TIME_SEC_IN_NS;
1480  nanosleep(&ts, NULL);
1481  odp_atomic_store_u32(&prog_conf->is_running, 0U);
1482  } else {
1483  while (odp_atomic_load_u32(&prog_conf->is_running))
1484  sleep(1U);
1485  }
1486 
1487  stop_test(prog_conf);
1488  print_stats(prog_conf);
1489 
1490 out_test:
1491  teardown(prog_conf);
1492 
1493 out:
1494  if (shm_cfg != ODP_SHM_INVALID)
1495  (void)odp_shm_free(shm_cfg);
1496 
1497  if (odp_term_local()) {
1498  ODPH_ERR("ODP local terminate failed, exiting\n");
1499  exit(EXIT_FAILURE);
1500  }
1501 
1503  ODPH_ERR("ODP global terminate failed, exiting\n");
1504  exit(EXIT_FAILURE);
1505  }
1506 
1507  return ret;
1508 }
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.
odp_buffer_t odp_buffer_alloc(odp_pool_t pool)
Buffer alloc.
void odp_buffer_free(odp_buffer_t buf)
Buffer free.
void * odp_buffer_addr(odp_buffer_t buf)
Buffer start address.
_odp_abi_buffer_t * odp_buffer_t
ODP buffer.
#define ODP_BUFFER_INVALID
Invalid buffer.
#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
uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
CPU cycle count difference.
uint64_t odp_cpu_cycles(void)
Current CPU cycle count.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
odp_dma_t odp_dma_create(const char *name, const odp_dma_param_t *param)
Create DMA session.
odp_pool_t odp_dma_pool_create(const char *name, const odp_dma_pool_param_t *pool_param)
Create DMA completion event pool.
int odp_dma_transfer_done(odp_dma_t dma, odp_dma_transfer_id_t transfer_id, odp_dma_result_t *result)
Check if DMA transfer has completed.
#define ODP_DMA_TYPE_COPY
Copy data.
void odp_dma_transfer_id_free(odp_dma_t dma, odp_dma_transfer_id_t transfer_id)
Free DMA transfer identifier.
#define ODP_DMA_COMPL_EVENT
Asynchronous transfer with completion event.
void odp_dma_transfer_param_init(odp_dma_transfer_param_t *trs_param)
Initialize DMA transfer parameters.
int odp_dma_destroy(odp_dma_t dma)
Destroy DMA session.
int odp_dma_compl_result(odp_dma_compl_t dma_compl, odp_dma_result_t *result)
Check DMA completion event.
void odp_dma_compl_param_init(odp_dma_compl_param_t *compl_param)
Initialize DMA transfer completion parameters.
#define ODP_DMA_TRANSFER_ID_INVALID
Invalid DMA transfer identifier.
#define ODP_DMA_COMPL_INVALID
Invalid DMA completion event.
odp_event_t odp_dma_compl_to_event(odp_dma_compl_t dma_compl)
Convert DMA completion event to event.
void odp_dma_pool_param_init(odp_dma_pool_param_t *pool_param)
Initialize DMA completion event pool parameters.
int odp_dma_transfer_start(odp_dma_t dma, const odp_dma_transfer_param_t *trs_param, const odp_dma_compl_param_t *compl_param)
Start DMA transfer.
uint64_t odp_dma_transfer_id_t
DMA transfer identifier.
odp_dma_compl_t odp_dma_compl_from_event(odp_event_t ev)
Convert event to DMA completion event.
#define ODP_DMA_MAIN_TO_MAIN
DMA transfer within the main memory.
int odp_dma_capability(odp_dma_capability_t *capa)
Query DMA capabilities.
odp_dma_transfer_id_t odp_dma_transfer_id_alloc(odp_dma_t dma)
Allocate DMA transfer identifier.
void odp_dma_compl_free(odp_dma_compl_t dma_compl)
Free DMA completion event.
#define ODP_DMA_INVALID
Invalid DMA session.
#define ODP_DMA_COMPL_POLL
Asynchronous transfer with completion polling.
odp_dma_compl_t odp_dma_compl_alloc(odp_pool_t pool)
Allocate DMA completion event.
@ ODP_DMA_MT_SERIAL
Application serializes operations.
@ ODP_DMA_FORMAT_PACKET
Data format is odp_packet_t.
@ ODP_DMA_ORDER_NONE
No specific ordering between transfers.
void odp_event_free(odp_event_t event)
Free event.
odp_event_type_t odp_event_type(odp_event_t event)
Event type of an event.
odp_event_type_t
Event type.
#define ODP_EVENT_INVALID
Invalid event.
int odp_instance(odp_instance_t *instance)
Get instance handle.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
#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_pktin_queue_param_init(odp_pktin_queue_param_t *param)
Initialize packet input queue parameters.
void odp_pktio_param_init(odp_pktio_param_t *param)
Initialize pktio params.
int odp_pktio_close(odp_pktio_t pktio)
Close a packet IO interface.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
void odp_pktout_queue_param_init(odp_pktout_queue_param_t *param)
Initialize packet output queue parameters.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktio_index(odp_pktio_t pktio)
Get pktio interface index.
int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa)
Query packet IO interface capabilities.
#define ODP_PKTIO_MAX_INDEX
Maximum packet IO interface index.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
int odp_pktin_queue_config(odp_pktio_t pktio, const odp_pktin_queue_param_t *param)
Configure packet input queues.
int odp_pktout_queue_config(odp_pktio_t pktio, const odp_pktout_queue_param_t *param)
Configure packet output queues.
@ ODP_PKTOUT_MODE_DIRECT
Direct packet output on the interface.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIN_MODE_SCHED
Packet input through scheduler and scheduled event queues.
int odp_packet_input_index(odp_packet_t pkt)
Packet input interface index.
odp_packet_t odp_packet_copy(odp_packet_t pkt, odp_pool_t pool)
Full copy of a packet.
uint32_t odp_packet_len(odp_packet_t pkt)
Packet data length.
odp_packet_t odp_packet_alloc(odp_pool_t pool, uint32_t len)
Allocate a packet from a packet pool.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
void odp_packet_free(odp_packet_t pkt)
Free packet.
#define ODP_PACKET_INVALID
Invalid packet.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
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_BUFFER
Buffer pool.
@ ODP_POOL_PACKET
Packet pool.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
int odp_schedule_multi_no_wait(odp_queue_t *from, odp_event_t events[], int num)
Schedule, do not wait for events.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
int odp_schedule_default_prio(void)
Default scheduling priority level.
int odp_schedule_max_prio(void)
Maximum scheduling priority level.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
#define ODP_SHM_INVALID
Invalid shared memory block.
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.
odp_stash_type_t
Stash types.
#define ODP_STASH_INVALID
Invalid stash handle.
int32_t odp_stash_put(odp_stash_t stash, const void *obj, int32_t num)
Put object handles into a stash.
void odp_stash_param_init(odp_stash_param_t *param)
Initialize stash params.
int32_t odp_stash_get(odp_stash_t stash, void *obj, int32_t num)
Get object handles from a stash.
int odp_stash_destroy(odp_stash_t stash)
Destroy a stash.
int odp_stash_capability(odp_stash_capability_t *capa, odp_stash_type_t type)
Query stash capabilities.
odp_stash_t odp_stash_create(const char *name, const odp_stash_param_t *param)
Create a stash.
@ ODP_STASH_TYPE_DEFAULT
The default stash type.
@ ODP_STASH_TYPE_FIFO
Stash type FIFO.
@ ODP_STASH_OP_LOCAL
Thread local operation.
bool odp_bool_t
Boolean type.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
The OpenDataPlane API.
uint32_t max_sessions
Maximum number of DMA sessions.
uint32_t max_transfers
Maximum number of transfers per DMA session.
odp_dma_pool_capability_t pool
DMA completion event pool capabilities.
uint32_t max_dst_segs
Maximum number of destination segments in a single transfer.
uint32_t max_src_segs
Maximum number of source segments in a single transfer.
odp_bool_t queue_type_sched
Scheduled queue support.
odp_dma_compl_mode_t compl_mode_mask
Supported completion modes.
uint32_t max_seg_len
Maximum segment length in bytes.
odp_bool_t src_seg_free
Source segment free support for data format ODP_DMA_FORMAT_PACKET.
DMA transfer completion parameters.
odp_dma_transfer_id_t transfer_id
Transfer identifier.
void * user_ptr
User context pointer.
odp_event_t event
Completion event.
odp_dma_compl_mode_t compl_mode
Completion mode.
odp_queue_t queue
Completion queue.
DMA session parameters.
odp_dma_direction_t direction
Transfer direction.
uint32_t min_cache_size
Minimum size of thread local cache.
uint32_t max_cache_size
Maximum size of thread local cache.
uint32_t max_pools
Maximum number of DMA completion event pools.
uint32_t max_num
Maximum number of DMA completion events in a pool.
DMA completion event pool parameters.
uint32_t cache_size
Maximum number of events cached locally per thread.
uint32_t num
Number of DMA completion events in the pool.
DMA transfer results.
void * user_ptr
User context pointer.
odp_bool_t success
DMA transfer success.
odp_packet_t packet
Packet handle.
uint32_t len
Segment length in bytes.
DMA transfer parameters.
struct odp_dma_transfer_param_t::@38::@40 opts
Option bit fields.
odp_dma_seg_t * dst_seg
Table of destination segments.
odp_dma_data_format_t dst_format
Destination data format.
uint32_t num_dst
Number of destination segments.
uint32_t num_src
Number of source segments.
odp_dma_seg_t * src_seg
Table of source segments.
odp_dma_data_format_t src_format
Source data format.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Packet input queue parameters.
uint32_t num_queues
Number of input queues to be created.
odp_queue_param_t queue_param
Queue parameters.
odp_pktin_hash_proto_t hash_proto
Protocol field selection for hashing.
odp_bool_t hash_enable
Enable flow hashing.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_output_queues
Maximum number of output queues.
Packet IO parameters.
odp_pktin_mode_t in_mode
Packet input mode.
odp_pktout_mode_t out_mode
Packet output mode.
Packet output queue parameters.
odp_pktio_op_mode_t op_mode
Operation mode.
uint32_t num_queues
Number of output queues to be created.
struct odp_pool_capability_t::@133 pkt
Packet pool capabilities
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::@132 buf
Buffer pool capabilities
uint32_t max_cache_size
Maximum size of thread local cache.
uint32_t max_len
Maximum packet data length in bytes.
Pool parameters.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@137 buf
Parameters for buffer pools.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
struct odp_pool_param_t::@138 pkt
Parameters for packet pools.
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 seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
Stash capabilities (per stash type)
uint64_t u16
Maximum number of 2 byte object handles.
struct odp_stash_capability_t::@169 max_num
Maximum number of object handles per stash for each object size.
uint64_t u128
Maximum number of 16 byte object handles.
uint32_t max_stashes
Maximum number of stashes of this type.
uint64_t u64
Maximum number of 8 byte object handles.
uint32_t max_cache_size
Maximum size of thread local cache.
uint64_t u32
Maximum number of 4 byte object handles.
uint64_t u8
Maximum number of 1 byte object handles.
Stash parameters.
uint32_t cache_size
Maximum number of object handles cached locally per thread.
odp_stash_op_mode_t get_mode
Get operation mode.
uint32_t obj_size
Object handle size in bytes.
odp_stash_type_t type
Stash type.
odp_stash_op_mode_t put_mode
Put operation mode.
uint64_t num_obj
Number of object handles.
128-bit unsigned integer structure
struct odp_pktin_hash_proto_t::@106 proto
Protocol header fields for hashing.
uint32_t ipv4
IPv4 addresses.