/
phprus
/
github_dpdk
Обзор
Документация
Войти
/
phprus
/
github_dpdk
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
app/test-dma-perf/benchmark.c
1 148 строк
30 KB
Liangxing Wang
app/dma-perf: fix reversed CPU copy
17 мар 2026, 17:30
17 мар 2026, 17:30
e76312d
Код
Авторство
О чём код?
/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2023 Intel Corporation */ #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <rte_time.h> #include <rte_mbuf.h> #include <rte_dmadev.h> #include <rte_malloc.h> #include <rte_lcore.h> #include <rte_random.h> #include "main.h" #define MAX_DMA_CPL_NB 255 #define TEST_WAIT_U_SECOND 10000 #define CSV_LINE_DMA_FMT "Scenario %u,%u,%s,%u,%u,%u,%u,%.2lf,%" PRIu64 ",%.3lf,%.3lf\n" #define CSV_LINE_CPU_FMT "Scenario %u,%u,NA,NA,NA,%u,%u,%.2lf,%" PRIu64 ",%.3lf,%.3lf\n" #define CSV_TOTAL_LINE_FMT "Scenario %u Summary, , , , , ,%u,%.2lf,%.1lf,%.3lf,%.3lf\n" struct worker_info { bool ready_flag; bool start_flag; bool stop_flag; uint32_t total_cpl; uint32_t test_cpl; }; struct sge_info { struct rte_dma_sge *srcs; struct rte_dma_sge *dsts; uint8_t nb_srcs; uint8_t nb_dsts; }; struct lcore_params { uint8_t scenario_id; unsigned int lcore_id; char *dma_name; uint16_t worker_id; uint16_t dev_id; uint32_t nr_buf; uint16_t kick_batch; uint32_t buf_size; uint16_t test_secs; struct rte_mbuf **srcs; struct rte_mbuf **dsts; struct sge_info sge; struct rte_dma_op **dma_ops; volatile struct worker_info worker_info; }; static struct rte_mempool *src_pool; static struct rte_mempool *dst_pool; static struct lcore_params *lcores[MAX_WORKER_NB]; #define PRINT_ERR(...) print_err(__func__, __LINE__, __VA_ARGS__) static inline int __rte_format_printf(3, 4) print_err(const char *func, int lineno, const char *format, ...) { va_list ap; int ret; ret = fprintf(stderr, "In %s:%d - ", func, lineno); va_start(ap, format); ret += vfprintf(stderr, format, ap); va_end(ap); return ret; } static inline void calc_result(uint32_t buf_size, uint32_t nr_buf, uint16_t nb_workers, uint16_t test_secs, uint32_t total_cnt, float *memory, uint32_t *ave_cycle, float *bandwidth, float *mops) { float ops; *memory = (float)(buf_size * (nr_buf / nb_workers) * 2) / (1024 * 1024); *ave_cycle = test_secs * rte_get_timer_hz() / total_cnt; ops = (float)total_cnt / test_secs; *mops = ops / (1000 * 1000); *bandwidth = (ops * buf_size * 8) / (1000 * 1000 * 1000); } static void output_result(struct test_configure *cfg, struct lcore_params *para, uint16_t kick_batch, uint64_t ave_cycle, uint32_t buf_size, uint32_t nr_buf, float memory, float bandwidth, float mops) { uint16_t ring_size = cfg->ring_size.cur; uint8_t scenario_id = cfg->scenario_id; uint32_t lcore_id = para->lcore_id; char *dma_name = para->dma_name; if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) { printf("lcore %u, DMA %s, DMA Ring Size: %u, Kick Batch Size: %u", lcore_id, dma_name, ring_size, kick_batch); if (cfg->is_sg) printf(" DMA src sges: %u, dst sges: %u", para->sge.nb_srcs, para->sge.nb_dsts); printf(".\n"); } else { printf("lcore %u\n", lcore_id); } printf("Average Cycles/op: %" PRIu64 ", Buffer Size: %u B, Buffer Number: %u, Memory: %.2lf MB, Frequency: %.3lf Ghz.\n", ave_cycle, buf_size, nr_buf, memory, rte_get_timer_hz()/1000000000.0); printf("Average Bandwidth: %.3lf Gbps, MOps: %.3lf\n", bandwidth, mops); if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) output_csv(CSV_LINE_DMA_FMT, scenario_id, lcore_id, dma_name, ring_size, kick_batch, buf_size, nr_buf, memory, ave_cycle, bandwidth, mops); else output_csv(CSV_LINE_CPU_FMT, scenario_id, lcore_id, buf_size, nr_buf, memory, ave_cycle, bandwidth, mops); } static inline void cache_flush_buf(__rte_unused struct rte_mbuf **array, __rte_unused uint32_t buf_size, __rte_unused uint32_t nr_buf) { #ifdef RTE_ARCH_X86_64 char *data; struct rte_mbuf **srcs = array; uint32_t i, offset; for (i = 0; i < nr_buf; i++) { data = rte_pktmbuf_mtod(srcs[i], char *); for (offset = 0; offset < buf_size; offset += 64) __builtin_ia32_clflush(data + offset); } #endif } static int vchan_data_populate(uint32_t dev_id, struct rte_dma_vchan_conf *qconf, struct test_configure *cfg, uint16_t dev_num) { struct vchan_dev_config *vchan_dconfig; struct rte_dma_info info; vchan_dconfig = &cfg->dma_config[dev_num].vchan_dev; qconf->direction = vchan_dconfig->tdir; rte_dma_info_get(dev_id, &info); if (!(RTE_BIT64(qconf->direction) & info.dev_capa)) return -1; qconf->nb_desc = cfg->ring_size.cur; switch (qconf->direction) { case RTE_DMA_DIR_MEM_TO_DEV: qconf->dst_port.pcie.vfen = 1; qconf->dst_port.port_type = RTE_DMA_PORT_PCIE; qconf->dst_port.pcie.coreid = vchan_dconfig->port.pcie.coreid; qconf->dst_port.pcie.vfid = vchan_dconfig->port.pcie.vfid; qconf->dst_port.pcie.pfid = vchan_dconfig->port.pcie.pfid; break; case RTE_DMA_DIR_DEV_TO_MEM: qconf->src_port.pcie.vfen = 1; qconf->src_port.port_type = RTE_DMA_PORT_PCIE; qconf->src_port.pcie.coreid = vchan_dconfig->port.pcie.coreid; qconf->src_port.pcie.vfid = vchan_dconfig->port.pcie.vfid; qconf->src_port.pcie.pfid = vchan_dconfig->port.pcie.pfid; break; case RTE_DMA_DIR_MEM_TO_MEM: case RTE_DMA_DIR_DEV_TO_DEV: break; } return 0; } /* Configuration of device. */ static void configure_dmadev_queue(uint32_t dev_id, struct test_configure *cfg, uint8_t sges_max, uint16_t dev_num) { uint16_t vchan = 0; struct rte_dma_info info; struct rte_dma_conf dev_config = { .nb_vchans = 1 }; struct rte_dma_vchan_conf qconf = { 0 }; if (vchan_data_populate(dev_id, &qconf, cfg, dev_num) != 0) rte_exit(EXIT_FAILURE, "Error with vchan data populate.\n"); if (rte_dma_info_get(dev_id, &info) != 0) rte_exit(EXIT_FAILURE, "Error with getting device info.\n"); if (cfg->use_ops && !(info.dev_capa & RTE_DMA_CAPA_OPS_ENQ_DEQ)) rte_exit(EXIT_FAILURE, "Error with device %s not support enq_deq ops.\n", info.dev_name); if (cfg->use_ops) dev_config.flags = RTE_DMA_CFG_FLAG_ENQ_DEQ; if (rte_dma_configure(dev_id, &dev_config) != 0) rte_exit(EXIT_FAILURE, "Error with dma configure.\n"); if (rte_dma_vchan_setup(dev_id, vchan, &qconf) != 0) rte_exit(EXIT_FAILURE, "Error with queue configuration.\n"); if (rte_dma_info_get(dev_id, &info) != 0) rte_exit(EXIT_FAILURE, "Error with getting device info.\n"); if (info.nb_vchans != 1) rte_exit(EXIT_FAILURE, "Error, no configured queues reported on device id. %u\n", dev_id); if (info.max_sges < sges_max) rte_exit(EXIT_FAILURE, "Error with unsupported max_sges on device id %u.\n", dev_id); if (rte_dma_start(dev_id) != 0) rte_exit(EXIT_FAILURE, "Error with dma start.\n"); } static int config_dmadevs(struct test_configure *cfg) { uint32_t nb_workers = cfg->num_worker; struct lcore_dma_map_t *ldm; uint32_t i; int dev_id; uint16_t nb_dmadevs = 0; uint8_t nb_sges = 0; char *dma_name; if (cfg->test_type != TEST_TYPE_DMA_MEM_COPY) return 0; if (cfg->is_sg) nb_sges = RTE_MAX(cfg->nb_src_sges, cfg->nb_dst_sges); for (i = 0; i < nb_workers; i++) { ldm = &cfg->dma_config[i].lcore_dma_map; dma_name = ldm->dma_names; dev_id = rte_dma_get_dev_id_by_name(dma_name); if (dev_id < 0) { fprintf(stderr, "Error: Fail to find DMA %s.\n", dma_name); goto end; } ldm->dma_id = dev_id; configure_dmadev_queue(dev_id, cfg, nb_sges, nb_dmadevs); ++nb_dmadevs; } end: if (nb_dmadevs < nb_workers) { printf("Not enough dmadevs (%u) for all workers (%u).\n", nb_dmadevs, nb_workers); return -1; } printf("Number of used dmadevs: %u.\n", nb_dmadevs); return 0; } static void stop_dmadev(struct test_configure *cfg, bool *stopped) { struct lcore_dma_map_t *lcore_dma_map; uint32_t i; if (*stopped) return; if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) { for (i = 0; i < cfg->num_worker; i++) { lcore_dma_map = &cfg->dma_config[i].lcore_dma_map; printf("Stopping dmadev %d\n", lcore_dma_map->dma_id); rte_dma_stop(lcore_dma_map->dma_id); } } *stopped = true; } static void error_exit(int dev_id) { rte_dma_stop(dev_id); rte_dma_close(dev_id); rte_exit(EXIT_FAILURE, "DMA error\n"); } static inline void do_dma_submit_and_poll(uint16_t dev_id, uint64_t *async_cnt, volatile struct worker_info *worker_info) { int ret; uint16_t nr_cpl; ret = rte_dma_submit(dev_id, 0); if (ret < 0) error_exit(dev_id); nr_cpl = rte_dma_completed(dev_id, 0, MAX_DMA_CPL_NB, NULL, NULL); *async_cnt -= nr_cpl; worker_info->total_cpl += nr_cpl; } static int do_dma_submit_and_wait_cpl(uint16_t dev_id, uint64_t async_cnt, bool use_ops) { #define MAX_WAIT_MSEC 1000 #define MAX_POLL 1000 #define DEQ_SZ 64 struct rte_dma_op *op[DEQ_SZ]; enum rte_dma_vchan_status st; uint32_t poll_cnt = 0; uint32_t wait_ms = 0; uint16_t nr_cpl; if (!use_ops) rte_dma_submit(dev_id, 0); if (rte_dma_vchan_status(dev_id, 0, &st) < 0) { rte_delay_ms(MAX_WAIT_MSEC); goto wait_cpl; } while (st == RTE_DMA_VCHAN_ACTIVE && wait_ms++ < MAX_WAIT_MSEC) { rte_delay_ms(1); rte_dma_vchan_status(dev_id, 0, &st); } wait_cpl: while ((async_cnt > 0) && (poll_cnt++ < MAX_POLL)) { if (use_ops) nr_cpl = rte_dma_dequeue_ops(dev_id, 0, op, DEQ_SZ); else nr_cpl = rte_dma_completed(dev_id, 0, MAX_DMA_CPL_NB, NULL, NULL); async_cnt -= nr_cpl; } if (async_cnt > 0) PRINT_ERR("Error: wait DMA %u failed!\n", dev_id); return async_cnt == 0 ? 0 : -1; } static inline int do_dma_plain_mem_copy(void *p) { struct lcore_params *para = (struct lcore_params *)p; volatile struct worker_info *worker_info = &(para->worker_info); const uint16_t dev_id = para->dev_id; const uint32_t nr_buf = para->nr_buf; const uint16_t kick_batch = para->kick_batch; const uint32_t buf_size = para->buf_size; struct rte_mbuf **srcs = para->srcs; struct rte_mbuf **dsts = para->dsts; uint64_t async_cnt = 0; uint32_t i; int ret; worker_info->stop_flag = false; worker_info->ready_flag = true; while (!worker_info->start_flag) ; while (1) { for (i = 0; i < nr_buf; i++) { dma_copy: ret = rte_dma_copy(dev_id, 0, rte_mbuf_data_iova(srcs[i]), rte_mbuf_data_iova(dsts[i]), buf_size, 0); if (unlikely(ret < 0)) { if (ret == -ENOSPC) { do_dma_submit_and_poll(dev_id, &async_cnt, worker_info); goto dma_copy; } else error_exit(dev_id); } async_cnt++; if ((async_cnt % kick_batch) == 0) do_dma_submit_and_poll(dev_id, &async_cnt, worker_info); } if (worker_info->stop_flag) break; } return do_dma_submit_and_wait_cpl(dev_id, async_cnt, false); } static inline int do_dma_sg_mem_copy(void *p) { struct lcore_params *para = (struct lcore_params *)p; volatile struct worker_info *worker_info = &(para->worker_info); struct rte_dma_sge *src_sges = para->sge.srcs; struct rte_dma_sge *dst_sges = para->sge.dsts; const uint8_t nb_src_sges = para->sge.nb_srcs; const uint8_t nb_dst_sges = para->sge.nb_dsts; const uint16_t kick_batch = para->kick_batch; const uint16_t dev_id = para->dev_id; uint32_t nr_buf = para->nr_buf; uint64_t async_cnt = 0; uint32_t i, j; int ret; nr_buf /= RTE_MAX(nb_src_sges, nb_dst_sges); worker_info->stop_flag = false; worker_info->ready_flag = true; while (!worker_info->start_flag) ; while (1) { j = 0; for (i = 0; i < nr_buf; i++) { dma_copy: ret = rte_dma_copy_sg(dev_id, 0, &src_sges[i * nb_src_sges], &dst_sges[j * nb_dst_sges], nb_src_sges, nb_dst_sges, 0); if (unlikely(ret < 0)) { if (ret == -ENOSPC) { do_dma_submit_and_poll(dev_id, &async_cnt, worker_info); goto dma_copy; } else error_exit(dev_id); } async_cnt++; j++; if ((async_cnt % kick_batch) == 0) do_dma_submit_and_poll(dev_id, &async_cnt, worker_info); } if (worker_info->stop_flag) break; } return do_dma_submit_and_wait_cpl(dev_id, async_cnt, false); } static inline int do_dma_enq_deq_mem_copy(void *p) { #define DEQ_SZ 64 struct lcore_params *para = (struct lcore_params *)p; volatile struct worker_info *worker_info = &(para->worker_info); struct rte_dma_op **dma_ops = para->dma_ops; uint16_t kick_batch = para->kick_batch, sz; const uint16_t dev_id = para->dev_id; uint32_t nr_buf = para->nr_buf; struct rte_dma_op *op[DEQ_SZ]; uint64_t tenq, tdeq; uint16_t enq, deq; uint32_t i; worker_info->stop_flag = false; worker_info->ready_flag = true; while (!worker_info->start_flag) ; if (kick_batch > nr_buf) kick_batch = nr_buf; tenq = 0; tdeq = 0; while (1) { for (i = 0; i < nr_buf; i += kick_batch) { sz = RTE_MIN(nr_buf - i, kick_batch); enq = rte_dma_enqueue_ops(dev_id, 0, &dma_ops[i], sz); while (enq < sz) { do { deq = rte_dma_dequeue_ops(dev_id, 0, op, DEQ_SZ); tdeq += deq; } while (deq); enq += rte_dma_enqueue_ops(dev_id, 0, &dma_ops[i + enq], sz - enq); if (worker_info->stop_flag) break; } tenq += enq; worker_info->total_cpl += enq; } if (worker_info->stop_flag) break; } return do_dma_submit_and_wait_cpl(dev_id, tenq - tdeq, true); } static inline int do_cpu_mem_copy(void *p) { struct lcore_params *para = (struct lcore_params *)p; volatile struct worker_info *worker_info = &(para->worker_info); const uint32_t nr_buf = para->nr_buf; const uint32_t buf_size = para->buf_size; struct rte_mbuf **srcs = para->srcs; struct rte_mbuf **dsts = para->dsts; uint32_t i; worker_info->stop_flag = false; worker_info->ready_flag = true; while (!worker_info->start_flag) ; while (1) { for (i = 0; i < nr_buf; i++) { const void *src = rte_pktmbuf_mtod(srcs[i], void *); void *dst = rte_pktmbuf_mtod(dsts[i], void *); /* copy buffer from src to dst */ rte_memcpy(dst, src, (size_t)buf_size); worker_info->total_cpl++; } if (worker_info->stop_flag) break; } return 0; } static void dummy_free_ext_buf(void *addr, void *opaque) { RTE_SET_USED(addr); RTE_SET_USED(opaque); } static int setup_memory_env(struct test_configure *cfg, uint32_t nr_buf, struct rte_mbuf ***srcs, struct rte_mbuf ***dsts, struct rte_dma_sge **src_sges, struct rte_dma_sge **dst_sges, struct rte_dma_op ***dma_ops) { unsigned int cur_buf_size = cfg->buf_size.cur; unsigned int buf_size = cur_buf_size + RTE_PKTMBUF_HEADROOM; bool is_src_numa_incorrect, is_dst_numa_incorrect; unsigned int nr_sockets; uintptr_t ops; uint32_t i; nr_sockets = rte_socket_count(); is_src_numa_incorrect = (cfg->src_numa_node >= nr_sockets); is_dst_numa_incorrect = (cfg->dst_numa_node >= nr_sockets); if (is_src_numa_incorrect || is_dst_numa_incorrect) { PRINT_ERR("Error: Incorrect NUMA config for %s.\n", (is_src_numa_incorrect && is_dst_numa_incorrect) ? "source & destination" : (is_src_numa_incorrect) ? "source" : "destination"); return -1; } if (buf_size > UINT16_MAX) { PRINT_ERR("Error: Invalid buf size: %u\n", cur_buf_size); return -1; } src_pool = rte_pktmbuf_pool_create("Benchmark_DMA_SRC", nr_buf, 0, 0, buf_size, cfg->src_numa_node); if (src_pool == NULL) { PRINT_ERR("Error with source mempool creation.\n"); return -1; } dst_pool = rte_pktmbuf_pool_create("Benchmark_DMA_DST", nr_buf, 0, 0, buf_size, cfg->dst_numa_node); if (dst_pool == NULL) { PRINT_ERR("Error with destination mempool creation.\n"); return -1; } *srcs = rte_malloc(NULL, nr_buf * sizeof(struct rte_mbuf *), 0); if (*srcs == NULL) { printf("Error: srcs malloc failed.\n"); return -1; } *dsts = rte_malloc(NULL, nr_buf * sizeof(struct rte_mbuf *), 0); if (*dsts == NULL) { printf("Error: dsts malloc failed.\n"); return -1; } if (rte_pktmbuf_alloc_bulk(src_pool, *srcs, nr_buf) != 0) { printf("alloc src mbufs failed.\n"); return -1; } if (rte_pktmbuf_alloc_bulk(dst_pool, *dsts, nr_buf) != 0) { printf("alloc dst mbufs failed.\n"); return -1; } for (i = 0; i < nr_buf; i++) { memset(rte_pktmbuf_mtod((*srcs)[i], void *), rte_rand(), cur_buf_size); memset(rte_pktmbuf_mtod((*dsts)[i], void *), 0, cur_buf_size); } if (cfg->is_sg) { uint8_t nb_src_sges = cfg->nb_src_sges; uint8_t nb_dst_sges = cfg->nb_dst_sges; uint32_t sglen_src, sglen_dst; *src_sges = rte_zmalloc(NULL, nr_buf * sizeof(struct rte_dma_sge), RTE_CACHE_LINE_SIZE); if (*src_sges == NULL) { printf("Error: src_sges array malloc failed.\n"); return -1; } *dst_sges = rte_zmalloc(NULL, nr_buf * sizeof(struct rte_dma_sge), RTE_CACHE_LINE_SIZE); if (*dst_sges == NULL) { printf("Error: dst_sges array malloc failed.\n"); return -1; } sglen_src = cur_buf_size / nb_src_sges; sglen_dst = cur_buf_size / nb_dst_sges; for (i = 0; i < nr_buf; i++) { (*src_sges)[i].addr = rte_pktmbuf_iova((*srcs)[i]); (*src_sges)[i].length = sglen_src; if (!((i+1) % nb_src_sges)) (*src_sges)[i].length += (cur_buf_size % nb_src_sges); (*dst_sges)[i].addr = rte_pktmbuf_iova((*dsts)[i]); (*dst_sges)[i].length = sglen_dst; if (!((i+1) % nb_dst_sges)) (*dst_sges)[i].length += (cur_buf_size % nb_dst_sges); } if (cfg->use_ops) { nr_buf /= RTE_MAX(nb_src_sges, nb_dst_sges); *dma_ops = rte_zmalloc(NULL, nr_buf * (sizeof(struct rte_dma_op *)), RTE_CACHE_LINE_SIZE); if (*dma_ops == NULL) { printf("Error: dma_ops container malloc failed.\n"); return -1; } ops = (uintptr_t)rte_zmalloc( NULL, nr_buf * (sizeof(struct rte_dma_op) + ((nb_src_sges + nb_dst_sges) * sizeof(struct rte_dma_sge))), RTE_CACHE_LINE_SIZE); if (ops == 0) { printf("Error: dma_ops malloc failed.\n"); return -1; } for (i = 0; i < nr_buf; i++) (*dma_ops)[i] = (struct rte_dma_op *)(ops + (i * (sizeof(struct rte_dma_op) + ((nb_src_sges + nb_dst_sges) * sizeof(struct rte_dma_sge))))); } } return 0; } static void teardown_memory_env(uint32_t nr_buf, struct rte_mbuf **srcs, struct rte_mbuf **dsts, struct rte_dma_sge *src_sges, struct rte_dma_sge *dst_sges, struct rte_dma_op **dma_ops) { /* free mbufs used in the test */ if (srcs != NULL) rte_pktmbuf_free_bulk(srcs, nr_buf); if (dsts != NULL) rte_pktmbuf_free_bulk(dsts, nr_buf); /* free the points for the mbufs */ rte_free(srcs); srcs = NULL; rte_free(dsts); dsts = NULL; rte_mempool_free(src_pool); src_pool = NULL; rte_mempool_free(dst_pool); dst_pool = NULL; /* free sges for mbufs */ rte_free(src_sges); src_sges = NULL; rte_free(dst_sges); dst_sges = NULL; rte_free(dma_ops); } static uint32_t align_buffer_count(struct test_configure *cfg, uint32_t *nr_sgsrc, uint32_t *nr_sgdst) { uint16_t nb_workers = cfg->num_worker; uint32_t nr_buf; nr_buf = (cfg->mem_size.cur * 1024 * 1024) / (cfg->buf_size.cur * 2); nr_buf -= (nr_buf % nb_workers); if (nr_sgsrc == NULL || nr_sgdst == NULL) return nr_buf; if (cfg->is_sg) { nr_buf /= nb_workers; nr_buf -= nr_buf % (cfg->nb_src_sges * cfg->nb_dst_sges); nr_buf *= nb_workers; if (cfg->nb_dst_sges > cfg->nb_src_sges) { *nr_sgsrc = (nr_buf / cfg->nb_dst_sges * cfg->nb_src_sges); *nr_sgdst = nr_buf; } else { *nr_sgsrc = nr_buf; *nr_sgdst = (nr_buf / cfg->nb_src_sges * cfg->nb_dst_sges); } } return nr_buf; } static lcore_function_t * get_work_function(struct test_configure *cfg) { lcore_function_t *fn; if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) { if (!cfg->is_sg) fn = do_dma_plain_mem_copy; else { if (cfg->use_ops) fn = do_dma_enq_deq_mem_copy; else fn = do_dma_sg_mem_copy; } } else { fn = do_cpu_mem_copy; } return fn; } static int attach_ext_buffer(struct vchan_dev_config *vchan_dev, struct lcore_params *lcore, bool is_sg, uint32_t nr_sgsrc, uint32_t nr_sgdst) { static struct rte_mbuf_ext_shared_info *ext_buf_info; struct rte_dma_sge **src_sges, **dst_sges; struct rte_mbuf **srcs, **dsts; unsigned int cur_buf_size; unsigned int buf_size; uint32_t nr_buf; uint32_t i; cur_buf_size = lcore->buf_size; buf_size = cur_buf_size + RTE_PKTMBUF_HEADROOM; nr_buf = lcore->nr_buf; srcs = lcore->srcs; dsts = lcore->dsts; ext_buf_info = rte_malloc(NULL, sizeof(struct rte_mbuf_ext_shared_info), 0); if (ext_buf_info == NULL) { printf("Error: ext_buf_info malloc failed.\n"); return -1; } ext_buf_info->free_cb = dummy_free_ext_buf; ext_buf_info->fcb_opaque = NULL; if (vchan_dev->tdir == RTE_DMA_DIR_DEV_TO_MEM) { for (i = 0; i < nr_buf; i++) { /* Using mbuf structure to hold remote iova address. */ rte_pktmbuf_attach_extbuf(srcs[i], (void *)(vchan_dev->raddr + (i * buf_size)), (rte_iova_t)(vchan_dev->raddr + (i * buf_size)), 0, ext_buf_info); rte_mbuf_ext_refcnt_update(ext_buf_info, 1); } } if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_DEV) { for (i = 0; i < nr_buf; i++) { /* Using mbuf structure to hold remote iova address. */ rte_pktmbuf_attach_extbuf(dsts[i], (void *)(vchan_dev->raddr + (i * buf_size)), (rte_iova_t)(vchan_dev->raddr + (i * buf_size)), 0, ext_buf_info); rte_mbuf_ext_refcnt_update(ext_buf_info, 1); } } if (is_sg) { uint8_t nb_src_sges = lcore->sge.nb_srcs; uint8_t nb_dst_sges = lcore->sge.nb_dsts; uint32_t sglen_src, sglen_dst; src_sges = &lcore->sge.srcs; dst_sges = &lcore->sge.dsts; sglen_src = cur_buf_size / nb_src_sges; sglen_dst = cur_buf_size / nb_dst_sges; if (vchan_dev->tdir == RTE_DMA_DIR_DEV_TO_MEM) { for (i = 0; i < nr_sgsrc; i++) { (*src_sges)[i].addr = rte_pktmbuf_iova(srcs[i]); (*src_sges)[i].length = sglen_src; if (!((i+1) % nb_src_sges)) (*src_sges)[i].length += (cur_buf_size % nb_src_sges); } } if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_DEV) { for (i = 0; i < nr_sgdst; i++) { (*dst_sges)[i].addr = rte_pktmbuf_iova(dsts[i]); (*dst_sges)[i].length = sglen_dst; if (!((i+1) % nb_dst_sges)) (*dst_sges)[i].length += (cur_buf_size % nb_dst_sges); } } } return 0; } static int verify_data(struct test_configure *cfg, struct rte_mbuf **srcs, struct rte_mbuf **dsts, uint32_t nr_buf) { struct rte_mbuf **src_buf = NULL, **dst_buf = NULL; uint32_t nr_buf_pt = nr_buf / cfg->num_worker; struct vchan_dev_config *vchan_dev = NULL; unsigned int buf_size = cfg->buf_size.cur; uint32_t offset, work_idx, i, j; for (work_idx = 0; work_idx < cfg->num_worker; work_idx++) { vchan_dev = &cfg->dma_config[work_idx].vchan_dev; offset = nr_buf / cfg->num_worker * work_idx; src_buf = srcs + offset; dst_buf = dsts + offset; if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_MEM && !cfg->is_sg) { for (i = 0; i < nr_buf_pt; i++) { if (memcmp(rte_pktmbuf_mtod(src_buf[i], void *), rte_pktmbuf_mtod(dst_buf[i], void *), cfg->buf_size.cur) != 0) { printf("Copy validation fails for buffer number %d\n", i); return -1; } } continue; } if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_MEM && cfg->is_sg) { size_t src_remsz = buf_size % cfg->nb_src_sges; size_t dst_remsz = buf_size % cfg->nb_dst_sges; size_t src_sz = buf_size / cfg->nb_src_sges; size_t dst_sz = buf_size / cfg->nb_dst_sges; uint8_t src[buf_size], dst[buf_size]; uint8_t *sbuf, *dbuf, *ptr; for (i = 0; i < (nr_buf_pt / RTE_MAX(cfg->nb_src_sges, cfg->nb_dst_sges)); i++) { sbuf = src; dbuf = dst; ptr = NULL; for (j = 0; j < cfg->nb_src_sges; j++) { ptr = rte_pktmbuf_mtod(src_buf[i * cfg->nb_src_sges + j], uint8_t *); memcpy(sbuf, ptr, src_sz); sbuf += src_sz; } if (src_remsz) memcpy(sbuf, ptr + src_sz, src_remsz); for (j = 0; j < cfg->nb_dst_sges; j++) { ptr = rte_pktmbuf_mtod(dst_buf[i * cfg->nb_dst_sges + j], uint8_t *); memcpy(dbuf, ptr, dst_sz); dbuf += dst_sz; } if (dst_remsz) memcpy(dbuf, ptr + dst_sz, dst_remsz); if (memcmp(src, dst, buf_size) != 0) { printf("SG Copy validation fails for buffer number %d\n", i * cfg->nb_src_sges); return -1; } } continue; } } return 0; } static int setup_worker(struct test_configure *cfg, uint32_t nr_buf, struct rte_mbuf **srcs, struct rte_mbuf **dsts, struct rte_dma_sge *src_sges, struct rte_dma_sge *dst_sges, struct rte_dma_op **dma_ops, uint32_t nr_sgsrc, uint32_t nr_sgdst) { struct lcore_dma_map_t *lcore_dma_map = NULL; struct vchan_dev_config *vchan_dev = NULL; unsigned int buf_size = cfg->buf_size.cur; uint16_t kick_batch = cfg->kick_batch.cur; uint16_t test_secs = global_cfg.test_secs; uint16_t nb_workers = cfg->num_worker; unsigned int lcore_id = 0; uint32_t i, j, k; uint32_t nr_ops; uint32_t offset; for (i = 0; i < nb_workers; i++) { lcore_dma_map = &cfg->dma_config[i].lcore_dma_map; vchan_dev = &cfg->dma_config[i].vchan_dev; lcore_id = lcore_dma_map->lcore; offset = nr_buf / nb_workers * i; lcores[i] = rte_malloc(NULL, sizeof(struct lcore_params), 0); if (lcores[i] == NULL) { printf("lcore parameters malloc failure for lcore %d\n", lcore_id); return -1; } if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) { lcores[i]->dma_name = lcore_dma_map->dma_names; lcores[i]->dev_id = lcore_dma_map->dma_id; lcores[i]->kick_batch = kick_batch; } lcores[i]->worker_id = i; lcores[i]->nr_buf = (uint32_t)(nr_buf / nb_workers); lcores[i]->buf_size = buf_size; lcores[i]->test_secs = test_secs; lcores[i]->srcs = srcs + offset; lcores[i]->dsts = dsts + offset; lcores[i]->scenario_id = cfg->scenario_id; lcores[i]->lcore_id = lcore_id; if (cfg->is_sg) { lcores[i]->sge.nb_srcs = cfg->nb_src_sges; lcores[i]->sge.nb_dsts = cfg->nb_dst_sges; lcores[i]->sge.srcs = src_sges + (nr_sgsrc / nb_workers * i); lcores[i]->sge.dsts = dst_sges + (nr_sgdst / nb_workers * i); } if (vchan_dev->tdir == RTE_DMA_DIR_DEV_TO_MEM || vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_DEV) { if (attach_ext_buffer(vchan_dev, lcores[i], cfg->is_sg, (nr_sgsrc/nb_workers), (nr_sgdst/nb_workers)) < 0) return -1; } if (cfg->is_sg && cfg->use_ops) { nr_ops = nr_buf / RTE_MAX(cfg->nb_src_sges, cfg->nb_dst_sges); lcores[i]->nr_buf = nr_ops / nb_workers; lcores[i]->dma_ops = dma_ops + (nr_ops / nb_workers * i); for (j = 0; j < (nr_ops / nb_workers); j++) { for (k = 0; k < cfg->nb_src_sges; k++) lcores[i]->dma_ops[j]->src_dst_seg[k] = lcores[i]->sge.srcs[(j * cfg->nb_src_sges) + k]; for (k = 0; k < cfg->nb_dst_sges; k++) lcores[i]->dma_ops[j]->src_dst_seg[k + cfg->nb_src_sges] = lcores[i]->sge.dsts[(j * cfg->nb_dst_sges) + k]; lcores[i]->dma_ops[j]->nb_src = cfg->nb_src_sges; lcores[i]->dma_ops[j]->nb_dst = cfg->nb_dst_sges; lcores[i]->dma_ops[j]->vchan = 0; } } rte_eal_remote_launch(get_work_function(cfg), (void *)(lcores[i]), lcore_id); } return 0; } static void teardown_worker_res(struct test_configure *cfg, uint32_t nr_buf, struct rte_mbuf **srcs, struct rte_mbuf **dsts) { uint16_t nb_workers = cfg->num_worker; struct vchan_dev_config *vchan_dev; struct rte_mbuf **m; uint32_t offset; uint32_t i, j; for (i = 0; i < nb_workers; i++) { struct rte_mbuf **sbuf = NULL, **dbuf = NULL; vchan_dev = &cfg->dma_config[i].vchan_dev; offset = nr_buf / nb_workers * i; m = NULL; if (vchan_dev->tdir == RTE_DMA_DIR_DEV_TO_MEM) { sbuf = srcs + offset; m = sbuf; } else if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_DEV) { dbuf = dsts + offset; m = dbuf; } if (m) { for (j = 0; j < (nr_buf / nb_workers); j++) rte_pktmbuf_detach_extbuf(m[j]); if (m[0]->shinfo && rte_mbuf_ext_refcnt_read(m[0]->shinfo) == 0) rte_free(m[0]->shinfo); } rte_free(lcores[i]); lcores[i] = NULL; } } int mem_copy_benchmark(struct test_configure *cfg) { struct rte_mbuf **srcs = NULL, **dsts = NULL; struct rte_dma_sge *src_sges = NULL, *dst_sges = NULL; struct vchan_dev_config *vchan_dev = NULL; unsigned int buf_size = cfg->buf_size.cur; uint16_t kick_batch = cfg->kick_batch.cur; uint16_t test_secs = global_cfg.test_secs; uint16_t nb_workers = cfg->num_worker; uint32_t nr_sgsrc = 0, nr_sgdst = 0; struct rte_dma_op **dma_ops = NULL; float bandwidth, bandwidth_total; uint32_t avg_cycles_total; bool dev_stopped = false; uint32_t avg_cycles = 0; float mops, mops_total; float memory = 0; uint32_t nr_buf; int ret = -1; uint32_t i; nr_buf = align_buffer_count(cfg, &nr_sgsrc, &nr_sgdst); if (setup_memory_env(cfg, nr_buf, &srcs, &dsts, &src_sges, &dst_sges, &dma_ops) < 0) goto out; if (config_dmadevs(cfg) < 0) goto out; if (global_cfg.cache_flush > 0) { cache_flush_buf(srcs, buf_size, nr_buf); cache_flush_buf(dsts, buf_size, nr_buf); rte_mb(); } printf("Start testing....\n"); ret = setup_worker(cfg, nr_buf, srcs, dsts, src_sges, dst_sges, dma_ops, nr_sgsrc, nr_sgdst); if (ret != 0) goto stop_dmadev; while (1) { bool ready = true; for (i = 0; i < nb_workers; i++) { if (lcores[i]->worker_info.ready_flag == false) { ready = 0; break; } } if (ready) break; } for (i = 0; i < nb_workers; i++) lcores[i]->worker_info.start_flag = true; usleep(TEST_WAIT_U_SECOND); for (i = 0; i < nb_workers; i++) lcores[i]->worker_info.test_cpl = lcores[i]->worker_info.total_cpl; usleep(test_secs * 1000 * 1000); for (i = 0; i < nb_workers; i++) lcores[i]->worker_info.test_cpl = lcores[i]->worker_info.total_cpl - lcores[i]->worker_info.test_cpl; for (i = 0; i < nb_workers; i++) lcores[i]->worker_info.stop_flag = true; rte_eal_mp_wait_lcore(); stop_dmadev(cfg, &dev_stopped); ret = verify_data(cfg, srcs, dsts, nr_buf); if (ret != 0) goto out; mops_total = 0; bandwidth_total = 0; avg_cycles_total = 0; for (i = 0; i < nb_workers; i++) { vchan_dev = &cfg->dma_config[i].vchan_dev; calc_result(buf_size, nr_buf, nb_workers, test_secs, lcores[i]->worker_info.test_cpl, &memory, &avg_cycles, &bandwidth, &mops); printf("Direction: %s\n", vchan_dev->tdir == 0 ? "mem2mem" : vchan_dev->tdir == 1 ? "mem2dev" : "dev2mem"); output_result(cfg, lcores[i], kick_batch, avg_cycles, buf_size, nr_buf / nb_workers, memory, bandwidth, mops); mops_total += mops; bandwidth_total += bandwidth; avg_cycles_total += avg_cycles; } printf("\nAverage Cycles/op per worker: %.1lf, Total Bandwidth: %.3lf Gbps, Total MOps: %.3lf\n", (avg_cycles_total * (float) 1.0) / nb_workers, bandwidth_total, mops_total); output_csv(CSV_TOTAL_LINE_FMT, cfg->scenario_id, nr_buf, memory * nb_workers, (avg_cycles_total * (float) 1.0) / nb_workers, bandwidth_total, mops_total); stop_dmadev: stop_dmadev(cfg, &dev_stopped); out: teardown_worker_res(cfg, nr_buf, srcs, dsts); teardown_memory_env(nr_buf, srcs, dsts, src_sges, dst_sges, dma_ops); return ret; }