Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2023 Intel Corporation
3 : : */
4 : :
5 : : #include <inttypes.h>
6 : : #include <stdio.h>
7 : : #include <stdlib.h>
8 : : #include <unistd.h>
9 : :
10 : : #include <rte_time.h>
11 : : #include <rte_mbuf.h>
12 : : #include <rte_dmadev.h>
13 : : #include <rte_malloc.h>
14 : : #include <rte_lcore.h>
15 : :
16 : : #include "main.h"
17 : :
18 : : #define MAX_DMA_CPL_NB 255
19 : :
20 : : #define TEST_WAIT_U_SECOND 10000
21 : : #define POLL_MAX 1000
22 : :
23 : : #define CSV_LINE_DMA_FMT "Scenario %u,%u,%s,%u,%u,%u,%u,%.2lf,%" PRIu64 ",%.3lf,%.3lf\n"
24 : : #define CSV_LINE_CPU_FMT "Scenario %u,%u,NA,NA,NA,%u,%u,%.2lf,%" PRIu64 ",%.3lf,%.3lf\n"
25 : :
26 : : #define CSV_TOTAL_LINE_FMT "Scenario %u Summary, , , , , ,%u,%.2lf,%u,%.3lf,%.3lf\n"
27 : :
28 : : struct worker_info {
29 : : bool ready_flag;
30 : : bool start_flag;
31 : : bool stop_flag;
32 : : uint32_t total_cpl;
33 : : uint32_t test_cpl;
34 : : };
35 : :
36 : : struct lcore_params {
37 : : uint8_t scenario_id;
38 : : unsigned int lcore_id;
39 : : char *dma_name;
40 : : uint16_t worker_id;
41 : : uint16_t dev_id;
42 : : uint32_t nr_buf;
43 : : uint16_t kick_batch;
44 : : uint32_t buf_size;
45 : : uint16_t test_secs;
46 : : struct rte_mbuf **srcs;
47 : : struct rte_mbuf **dsts;
48 : : volatile struct worker_info worker_info;
49 : : };
50 : :
51 : : static struct rte_mempool *src_pool;
52 : : static struct rte_mempool *dst_pool;
53 : :
54 : : static struct lcore_params *lcores[MAX_WORKER_NB];
55 : :
56 : : #define PRINT_ERR(...) print_err(__func__, __LINE__, __VA_ARGS__)
57 : :
58 : : static inline int
59 : : __rte_format_printf(3, 4)
60 : 0 : print_err(const char *func, int lineno, const char *format, ...)
61 : : {
62 : : va_list ap;
63 : : int ret;
64 : :
65 : 0 : ret = fprintf(stderr, "In %s:%d - ", func, lineno);
66 : 0 : va_start(ap, format);
67 : 0 : ret += vfprintf(stderr, format, ap);
68 : 0 : va_end(ap);
69 : :
70 : 0 : return ret;
71 : : }
72 : :
73 : : static inline void
74 : 0 : calc_result(uint32_t buf_size, uint32_t nr_buf, uint16_t nb_workers, uint16_t test_secs,
75 : : uint32_t total_cnt, float *memory, uint32_t *ave_cycle,
76 : : float *bandwidth, float *mops)
77 : : {
78 : : float ops;
79 : :
80 : 0 : *memory = (float)(buf_size * (nr_buf / nb_workers) * 2) / (1024 * 1024);
81 : 0 : *ave_cycle = test_secs * rte_get_timer_hz() / total_cnt;
82 : 0 : ops = (float)total_cnt / test_secs;
83 : 0 : *mops = ops / (1000 * 1000);
84 : 0 : *bandwidth = (ops * buf_size * 8) / (1000 * 1000 * 1000);
85 : 0 : }
86 : :
87 : : static void
88 : 0 : output_result(uint8_t scenario_id, uint32_t lcore_id, char *dma_name, uint16_t ring_size,
89 : : uint16_t kick_batch, uint64_t ave_cycle, uint32_t buf_size, uint32_t nr_buf,
90 : : float memory, float bandwidth, float mops, bool is_dma)
91 : : {
92 : 0 : if (is_dma)
93 : 0 : printf("lcore %u, DMA %s, DMA Ring Size: %u, Kick Batch Size: %u.\n",
94 : : lcore_id, dma_name, ring_size, kick_batch);
95 : : else
96 : : printf("lcore %u\n", lcore_id);
97 : :
98 : 0 : printf("Average Cycles/op: %" PRIu64 ", Buffer Size: %u B, Buffer Number: %u, Memory: %.2lf MB, Frequency: %.3lf Ghz.\n",
99 : : ave_cycle, buf_size, nr_buf, memory, rte_get_timer_hz()/1000000000.0);
100 : 0 : printf("Average Bandwidth: %.3lf Gbps, MOps: %.3lf\n", bandwidth, mops);
101 : :
102 : 0 : if (is_dma)
103 : 0 : snprintf(output_str[lcore_id], MAX_OUTPUT_STR_LEN, CSV_LINE_DMA_FMT,
104 : : scenario_id, lcore_id, dma_name, ring_size, kick_batch, buf_size,
105 : : nr_buf, memory, ave_cycle, bandwidth, mops);
106 : : else
107 : 0 : snprintf(output_str[lcore_id], MAX_OUTPUT_STR_LEN, CSV_LINE_CPU_FMT,
108 : : scenario_id, lcore_id, buf_size,
109 : : nr_buf, memory, ave_cycle, bandwidth, mops);
110 : 0 : }
111 : :
112 : : static inline void
113 : : cache_flush_buf(__rte_unused struct rte_mbuf **array,
114 : : __rte_unused uint32_t buf_size,
115 : : __rte_unused uint32_t nr_buf)
116 : : {
117 : : #ifdef RTE_ARCH_X86_64
118 : : char *data;
119 : : struct rte_mbuf **srcs = array;
120 : : uint32_t i, offset;
121 : :
122 : 0 : for (i = 0; i < nr_buf; i++) {
123 : 0 : data = rte_pktmbuf_mtod(srcs[i], char *);
124 : 0 : for (offset = 0; offset < buf_size; offset += 64)
125 : 0 : __builtin_ia32_clflush(data + offset);
126 : : }
127 : : #endif
128 : : }
129 : :
130 : : /* Configuration of device. */
131 : : static void
132 : 0 : configure_dmadev_queue(uint32_t dev_id, uint32_t ring_size)
133 : : {
134 : : uint16_t vchan = 0;
135 : : struct rte_dma_info info;
136 : 0 : struct rte_dma_conf dev_config = { .nb_vchans = 1 };
137 : 0 : struct rte_dma_vchan_conf qconf = {
138 : : .direction = RTE_DMA_DIR_MEM_TO_MEM,
139 : : .nb_desc = ring_size
140 : : };
141 : :
142 : 0 : if (rte_dma_configure(dev_id, &dev_config) != 0)
143 : 0 : rte_exit(EXIT_FAILURE, "Error with dma configure.\n");
144 : :
145 : 0 : if (rte_dma_vchan_setup(dev_id, vchan, &qconf) != 0)
146 : 0 : rte_exit(EXIT_FAILURE, "Error with queue configuration.\n");
147 : :
148 : 0 : if (rte_dma_info_get(dev_id, &info) != 0)
149 : 0 : rte_exit(EXIT_FAILURE, "Error with getting device info.\n");
150 : :
151 : 0 : if (info.nb_vchans != 1)
152 : 0 : rte_exit(EXIT_FAILURE, "Error, no configured queues reported on device id. %u\n",
153 : : dev_id);
154 : :
155 : 0 : if (rte_dma_start(dev_id) != 0)
156 : 0 : rte_exit(EXIT_FAILURE, "Error with dma start.\n");
157 : 0 : }
158 : :
159 : : static int
160 : 0 : config_dmadevs(struct test_configure *cfg)
161 : : {
162 : 0 : uint32_t ring_size = cfg->ring_size.cur;
163 : : struct lcore_dma_map_t *ldm = &cfg->lcore_dma_map;
164 : 0 : uint32_t nb_workers = ldm->cnt;
165 : : uint32_t i;
166 : : int dev_id;
167 : : uint16_t nb_dmadevs = 0;
168 : : char *dma_name;
169 : :
170 : 0 : for (i = 0; i < ldm->cnt; i++) {
171 : 0 : dma_name = ldm->dma_names[i];
172 : 0 : dev_id = rte_dma_get_dev_id_by_name(dma_name);
173 : 0 : if (dev_id < 0) {
174 : 0 : fprintf(stderr, "Error: Fail to find DMA %s.\n", dma_name);
175 : 0 : goto end;
176 : : }
177 : :
178 : 0 : ldm->dma_ids[i] = dev_id;
179 : 0 : configure_dmadev_queue(dev_id, ring_size);
180 : 0 : ++nb_dmadevs;
181 : : }
182 : :
183 : 0 : end:
184 : 0 : if (nb_dmadevs < nb_workers) {
185 : 0 : printf("Not enough dmadevs (%u) for all workers (%u).\n", nb_dmadevs, nb_workers);
186 : 0 : return -1;
187 : : }
188 : :
189 : 0 : printf("Number of used dmadevs: %u.\n", nb_dmadevs);
190 : :
191 : 0 : return 0;
192 : : }
193 : :
194 : : static void
195 : 0 : error_exit(int dev_id)
196 : : {
197 : 0 : rte_dma_stop(dev_id);
198 : 0 : rte_dma_close(dev_id);
199 : 0 : rte_exit(EXIT_FAILURE, "DMA error\n");
200 : : }
201 : :
202 : : static inline void
203 : 0 : do_dma_submit_and_poll(uint16_t dev_id, uint64_t *async_cnt,
204 : : volatile struct worker_info *worker_info)
205 : : {
206 : : int ret;
207 : : uint16_t nr_cpl;
208 : :
209 : 0 : ret = rte_dma_submit(dev_id, 0);
210 : 0 : if (ret < 0)
211 : 0 : error_exit(dev_id);
212 : :
213 : : nr_cpl = rte_dma_completed(dev_id, 0, MAX_DMA_CPL_NB, NULL, NULL);
214 : 0 : *async_cnt -= nr_cpl;
215 : 0 : worker_info->total_cpl += nr_cpl;
216 : 0 : }
217 : :
218 : : static inline int
219 : 0 : do_dma_mem_copy(void *p)
220 : : {
221 : : struct lcore_params *para = (struct lcore_params *)p;
222 : 0 : volatile struct worker_info *worker_info = &(para->worker_info);
223 : 0 : const uint16_t dev_id = para->dev_id;
224 : 0 : const uint32_t nr_buf = para->nr_buf;
225 : 0 : const uint16_t kick_batch = para->kick_batch;
226 : 0 : const uint32_t buf_size = para->buf_size;
227 : 0 : struct rte_mbuf **srcs = para->srcs;
228 : 0 : struct rte_mbuf **dsts = para->dsts;
229 : : uint16_t nr_cpl;
230 : 0 : uint64_t async_cnt = 0;
231 : : uint32_t i;
232 : : uint32_t poll_cnt = 0;
233 : : int ret;
234 : :
235 : 0 : worker_info->stop_flag = false;
236 : 0 : worker_info->ready_flag = true;
237 : :
238 : 0 : while (!worker_info->start_flag)
239 : : ;
240 : :
241 : : while (1) {
242 : 0 : for (i = 0; i < nr_buf; i++) {
243 : 0 : dma_copy:
244 : 0 : ret = rte_dma_copy(dev_id, 0, rte_mbuf_data_iova(srcs[i]),
245 : 0 : rte_mbuf_data_iova(dsts[i]), buf_size, 0);
246 : 0 : if (unlikely(ret < 0)) {
247 : 0 : if (ret == -ENOSPC) {
248 : 0 : do_dma_submit_and_poll(dev_id, &async_cnt, worker_info);
249 : 0 : goto dma_copy;
250 : : } else
251 : 0 : error_exit(dev_id);
252 : : }
253 : 0 : async_cnt++;
254 : :
255 : 0 : if ((async_cnt % kick_batch) == 0)
256 : 0 : do_dma_submit_and_poll(dev_id, &async_cnt, worker_info);
257 : : }
258 : :
259 : 0 : if (worker_info->stop_flag)
260 : : break;
261 : : }
262 : :
263 : 0 : rte_dma_submit(dev_id, 0);
264 : 0 : while ((async_cnt > 0) && (poll_cnt++ < POLL_MAX)) {
265 : : nr_cpl = rte_dma_completed(dev_id, 0, MAX_DMA_CPL_NB, NULL, NULL);
266 : 0 : async_cnt -= nr_cpl;
267 : : }
268 : :
269 : 0 : return 0;
270 : : }
271 : :
272 : : static inline int
273 : 0 : do_cpu_mem_copy(void *p)
274 : : {
275 : : struct lcore_params *para = (struct lcore_params *)p;
276 : : volatile struct worker_info *worker_info = &(para->worker_info);
277 : 0 : const uint32_t nr_buf = para->nr_buf;
278 : 0 : const uint32_t buf_size = para->buf_size;
279 : 0 : struct rte_mbuf **srcs = para->srcs;
280 : 0 : struct rte_mbuf **dsts = para->dsts;
281 : : uint32_t i;
282 : :
283 : 0 : worker_info->stop_flag = false;
284 : 0 : worker_info->ready_flag = true;
285 : :
286 : 0 : while (!worker_info->start_flag)
287 : : ;
288 : :
289 : : while (1) {
290 : 0 : for (i = 0; i < nr_buf; i++) {
291 : 0 : const void *src = rte_pktmbuf_mtod(dsts[i], void *);
292 : 0 : void *dst = rte_pktmbuf_mtod(srcs[i], void *);
293 : :
294 : : /* copy buffer form src to dst */
295 : 0 : rte_memcpy(dst, src, (size_t)buf_size);
296 : 0 : worker_info->total_cpl++;
297 : : }
298 : 0 : if (worker_info->stop_flag)
299 : : break;
300 : : }
301 : :
302 : 0 : return 0;
303 : : }
304 : :
305 : : static int
306 : 0 : setup_memory_env(struct test_configure *cfg, struct rte_mbuf ***srcs,
307 : : struct rte_mbuf ***dsts)
308 : : {
309 : 0 : unsigned int buf_size = cfg->buf_size.cur;
310 : : unsigned int nr_sockets;
311 : 0 : uint32_t nr_buf = cfg->nr_buf;
312 : :
313 : 0 : nr_sockets = rte_socket_count();
314 : 0 : if (cfg->src_numa_node >= nr_sockets ||
315 : 0 : cfg->dst_numa_node >= nr_sockets) {
316 : : printf("Error: Source or destination numa exceeds the acture numa nodes.\n");
317 : 0 : return -1;
318 : : }
319 : :
320 : 0 : src_pool = rte_pktmbuf_pool_create("Benchmark_DMA_SRC",
321 : : nr_buf,
322 : : 0,
323 : : 0,
324 : 0 : buf_size + RTE_PKTMBUF_HEADROOM,
325 : : cfg->src_numa_node);
326 : 0 : if (src_pool == NULL) {
327 : 0 : PRINT_ERR("Error with source mempool creation.\n");
328 : 0 : return -1;
329 : : }
330 : :
331 : 0 : dst_pool = rte_pktmbuf_pool_create("Benchmark_DMA_DST",
332 : : nr_buf,
333 : : 0,
334 : : 0,
335 : : buf_size + RTE_PKTMBUF_HEADROOM,
336 : 0 : cfg->dst_numa_node);
337 : 0 : if (dst_pool == NULL) {
338 : 0 : PRINT_ERR("Error with destination mempool creation.\n");
339 : 0 : return -1;
340 : : }
341 : :
342 : 0 : *srcs = rte_malloc(NULL, nr_buf * sizeof(struct rte_mbuf *), 0);
343 : 0 : if (*srcs == NULL) {
344 : : printf("Error: srcs malloc failed.\n");
345 : 0 : return -1;
346 : : }
347 : :
348 : 0 : *dsts = rte_malloc(NULL, nr_buf * sizeof(struct rte_mbuf *), 0);
349 : 0 : if (*dsts == NULL) {
350 : : printf("Error: dsts malloc failed.\n");
351 : 0 : return -1;
352 : : }
353 : :
354 : 0 : if (rte_pktmbuf_alloc_bulk(src_pool, *srcs, nr_buf) != 0) {
355 : : printf("alloc src mbufs failed.\n");
356 : 0 : return -1;
357 : : }
358 : :
359 : 0 : if (rte_pktmbuf_alloc_bulk(dst_pool, *dsts, nr_buf) != 0) {
360 : : printf("alloc dst mbufs failed.\n");
361 : 0 : return -1;
362 : : }
363 : :
364 : : return 0;
365 : : }
366 : :
367 : : void
368 : 0 : mem_copy_benchmark(struct test_configure *cfg, bool is_dma)
369 : : {
370 : : uint16_t i;
371 : : uint32_t offset;
372 : : unsigned int lcore_id = 0;
373 : 0 : struct rte_mbuf **srcs = NULL, **dsts = NULL;
374 : : struct lcore_dma_map_t *ldm = &cfg->lcore_dma_map;
375 : 0 : unsigned int buf_size = cfg->buf_size.cur;
376 : 0 : uint16_t kick_batch = cfg->kick_batch.cur;
377 : 0 : uint32_t nr_buf = cfg->nr_buf = (cfg->mem_size.cur * 1024 * 1024) / (cfg->buf_size.cur * 2);
378 : 0 : uint16_t nb_workers = ldm->cnt;
379 : 0 : uint16_t test_secs = cfg->test_secs;
380 : 0 : float memory = 0;
381 : 0 : uint32_t avg_cycles = 0;
382 : : uint32_t avg_cycles_total;
383 : : float mops, mops_total;
384 : : float bandwidth, bandwidth_total;
385 : :
386 : 0 : if (setup_memory_env(cfg, &srcs, &dsts) < 0)
387 : 0 : goto out;
388 : :
389 : 0 : if (is_dma)
390 : 0 : if (config_dmadevs(cfg) < 0)
391 : 0 : goto out;
392 : :
393 : 0 : if (cfg->cache_flush == 1) {
394 : 0 : cache_flush_buf(srcs, buf_size, nr_buf);
395 : 0 : cache_flush_buf(dsts, buf_size, nr_buf);
396 : : rte_mb();
397 : : }
398 : :
399 : : printf("Start testing....\n");
400 : :
401 : 0 : for (i = 0; i < nb_workers; i++) {
402 : 0 : lcore_id = ldm->lcores[i];
403 : 0 : offset = nr_buf / nb_workers * i;
404 : 0 : lcores[i] = rte_malloc(NULL, sizeof(struct lcore_params), 0);
405 : 0 : if (lcores[i] == NULL) {
406 : : printf("lcore parameters malloc failure for lcore %d\n", lcore_id);
407 : : break;
408 : : }
409 : 0 : if (is_dma) {
410 : 0 : lcores[i]->dma_name = ldm->dma_names[i];
411 : 0 : lcores[i]->dev_id = ldm->dma_ids[i];
412 : 0 : lcores[i]->kick_batch = kick_batch;
413 : : }
414 : 0 : lcores[i]->worker_id = i;
415 : 0 : lcores[i]->nr_buf = (uint32_t)(nr_buf / nb_workers);
416 : 0 : lcores[i]->buf_size = buf_size;
417 : 0 : lcores[i]->test_secs = test_secs;
418 : 0 : lcores[i]->srcs = srcs + offset;
419 : 0 : lcores[i]->dsts = dsts + offset;
420 : 0 : lcores[i]->scenario_id = cfg->scenario_id;
421 : 0 : lcores[i]->lcore_id = lcore_id;
422 : :
423 : 0 : if (is_dma)
424 : 0 : rte_eal_remote_launch(do_dma_mem_copy, (void *)(lcores[i]), lcore_id);
425 : : else
426 : 0 : rte_eal_remote_launch(do_cpu_mem_copy, (void *)(lcores[i]), lcore_id);
427 : : }
428 : :
429 : : while (1) {
430 : : bool ready = true;
431 : 0 : for (i = 0; i < nb_workers; i++) {
432 : 0 : if (lcores[i]->worker_info.ready_flag == false) {
433 : : ready = 0;
434 : : break;
435 : : }
436 : : }
437 : 0 : if (ready)
438 : : break;
439 : : }
440 : :
441 : 0 : for (i = 0; i < nb_workers; i++)
442 : 0 : lcores[i]->worker_info.start_flag = true;
443 : :
444 : 0 : usleep(TEST_WAIT_U_SECOND);
445 : 0 : for (i = 0; i < nb_workers; i++)
446 : 0 : lcores[i]->worker_info.test_cpl = lcores[i]->worker_info.total_cpl;
447 : :
448 : 0 : usleep(test_secs * 1000 * 1000);
449 : 0 : for (i = 0; i < nb_workers; i++)
450 : 0 : lcores[i]->worker_info.test_cpl = lcores[i]->worker_info.total_cpl -
451 : 0 : lcores[i]->worker_info.test_cpl;
452 : :
453 : 0 : for (i = 0; i < nb_workers; i++)
454 : 0 : lcores[i]->worker_info.stop_flag = true;
455 : :
456 : 0 : rte_eal_mp_wait_lcore();
457 : :
458 : : mops_total = 0;
459 : : bandwidth_total = 0;
460 : : avg_cycles_total = 0;
461 : 0 : for (i = 0; i < nb_workers; i++) {
462 : 0 : calc_result(buf_size, nr_buf, nb_workers, test_secs,
463 : 0 : lcores[i]->worker_info.test_cpl,
464 : : &memory, &avg_cycles, &bandwidth, &mops);
465 : 0 : output_result(cfg->scenario_id, lcores[i]->lcore_id,
466 : 0 : lcores[i]->dma_name, cfg->ring_size.cur, kick_batch,
467 : : avg_cycles, buf_size, nr_buf / nb_workers, memory,
468 : : bandwidth, mops, is_dma);
469 : 0 : mops_total += mops;
470 : 0 : bandwidth_total += bandwidth;
471 : 0 : avg_cycles_total += avg_cycles;
472 : : }
473 : 0 : printf("\nTotal Bandwidth: %.3lf Gbps, Total MOps: %.3lf\n", bandwidth_total, mops_total);
474 : 0 : snprintf(output_str[MAX_WORKER_NB], MAX_OUTPUT_STR_LEN, CSV_TOTAL_LINE_FMT,
475 : 0 : cfg->scenario_id, nr_buf, memory * nb_workers,
476 : : avg_cycles_total / nb_workers, bandwidth_total, mops_total);
477 : :
478 : 0 : out:
479 : : /* free mbufs used in the test */
480 : 0 : if (srcs != NULL)
481 : 0 : rte_pktmbuf_free_bulk(srcs, nr_buf);
482 : 0 : if (dsts != NULL)
483 : 0 : rte_pktmbuf_free_bulk(dsts, nr_buf);
484 : :
485 : : /* free the points for the mbufs */
486 : 0 : rte_free(srcs);
487 : 0 : srcs = NULL;
488 : 0 : rte_free(dsts);
489 : 0 : dsts = NULL;
490 : :
491 : 0 : rte_mempool_free(src_pool);
492 : 0 : src_pool = NULL;
493 : :
494 : 0 : rte_mempool_free(dst_pool);
495 : 0 : dst_pool = NULL;
496 : :
497 : : /* free the worker parameters */
498 : 0 : for (i = 0; i < nb_workers; i++) {
499 : 0 : rte_free(lcores[i]);
500 : 0 : lcores[i] = NULL;
501 : : }
502 : :
503 : 0 : if (is_dma) {
504 : 0 : for (i = 0; i < nb_workers; i++) {
505 : 0 : printf("Stopping dmadev %d\n", ldm->dma_ids[i]);
506 : 0 : rte_dma_stop(ldm->dma_ids[i]);
507 : : }
508 : : }
509 : 0 : }
|