API Reference Manual 1.51.0
Loading...
Searching...
No Matches
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
34enum {
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
53ODP_STATIC_ASSERT(MAX_IFS < UINT8_MAX, "Too large maximum interface count");
54ODP_STATIC_ASSERT(MAX_OUT_QS < UINT8_MAX, "Too large maximum output queue count");
55
56typedef 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
63typedef enum {
64 PRS_OK,
65 PRS_NOK,
66 PRS_TERM,
67 PRS_NOT_SUP
68} parse_result_t;
69
70typedef struct prog_config_s prog_config_t;
71
72typedef 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
90typedef 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
102typedef 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
109typedef 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 */
117typedef 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 */
121typedef 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 */
124typedef 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 */
127typedef void (*drain_fn_t)(thread_config_t *config);
128
129typedef 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
167typedef struct {
168 odp_packet_t pkts[MAX_BURST * 2U];
169 pktio_t *pktio;
170 int num;
171} pkt_vec_t;
172
173static prog_config_t *prog_conf;
174
175static void terminate(int signal ODP_UNUSED)
176{
177 odp_atomic_store_u32(&prog_conf->is_running, 0U);
178}
179
180static 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
228static 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
266static 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
287static 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
302static 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
469static 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
538static 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
553static 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
565static 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
595static 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;
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
641static 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;
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
684static 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
698static 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
716static 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
771static void drain_events(thread_config_t *config ODP_UNUSED)
772{
773 odp_event_t ev;
774 odp_event_type_t type;
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
803static void drain_polled(thread_config_t *config)
804{
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
836static 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
946static 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
1034static 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
1101static 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
1136static 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
1144static 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
1152static 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
1160static 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
1239static 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
1269static 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
1288static 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
1298static 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
1335static 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
1398int 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
1490out_test:
1491 teardown(prog_conf);
1492
1493out:
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.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
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.
uint16_t seg_free
Allow freeing all the source segments.
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.
uint32_t max_num
Maximum number of buffers of any size.
uint32_t min_cache_size
Minimum size of thread local cache.
struct odp_pool_capability_t::@133 buf
Buffer pool capabilities
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
uint32_t max_cache_size
Maximum size of thread local cache.
uint32_t max_len
Maximum packet data length in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
uint32_t cache_size
Maximum number of buffers cached locally per thread.
uint32_t size
Minimum buffer size in bytes.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
struct odp_pool_param_t::@138 buf
Parameters for buffer pools.
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.
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.
struct odp_stash_capability_t::@171 max_num
Maximum number of object handles per stash for each object size.
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::@107 proto
Protocol header fields for hashing.
uint32_t ipv4
IPv4 addresses.