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