Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2021, 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 : : */
4 : :
5 : : #include "spdk/stdinc.h"
6 : :
7 : : #include "spdk/dma.h"
8 : : #include "spdk/bdev.h"
9 : : #include "spdk/env.h"
10 : : #include "spdk/event.h"
11 : : #include "spdk/likely.h"
12 : : #include "spdk/string.h"
13 : : #include "spdk/util.h"
14 : :
15 : : #include <infiniband/verbs.h>
16 : :
17 : : struct dma_test_task;
18 : :
19 : : struct dma_test_req {
20 : : struct iovec iov;
21 : : struct spdk_bdev_ext_io_opts io_opts;
22 : : uint64_t submit_tsc;
23 : : struct ibv_mr *mr;
24 : : struct dma_test_task *task;
25 : : };
26 : :
27 : : struct dma_test_task_stats {
28 : : uint64_t io_completed;
29 : : uint64_t total_tsc;
30 : : uint64_t min_tsc;
31 : : uint64_t max_tsc;
32 : : };
33 : :
34 : : struct dma_test_task {
35 : : struct spdk_bdev_desc *desc;
36 : : struct spdk_io_channel *channel;
37 : : uint64_t cur_io_offset;
38 : : uint64_t max_offset_in_ios;
39 : : uint64_t num_blocks_per_io;
40 : : int rw_percentage;
41 : : uint32_t seed;
42 : : uint32_t io_inflight;
43 : : struct dma_test_task_stats stats;
44 : : struct dma_test_task_stats last_stats;
45 : : bool is_draining;
46 : : bool is_random;
47 : : struct dma_test_req *reqs;
48 : : struct spdk_thread *thread;
49 : : const char *bdev_name;
50 : : uint64_t num_translations;
51 : : uint64_t num_pull_push;
52 : : uint64_t num_mem_zero;
53 : : uint32_t lcore;
54 : :
55 : : TAILQ_ENTRY(dma_test_task) link;
56 : : };
57 : :
58 : : struct dma_test_data_cpl_ctx {
59 : : spdk_memory_domain_data_cpl_cb data_cpl;
60 : : void *data_cpl_arg;
61 : : };
62 : :
63 : : enum dma_test_domain_ops {
64 : : DMA_TEST_DOMAIN_OP_TRANSLATE = 1u << 0,
65 : : DMA_TEST_DOMAIN_OP_PULL_PUSH = 1u << 1,
66 : : DMA_TEST_DOMAIN_OP_MEMZERO = 1u << 2,
67 : : };
68 : :
69 : : TAILQ_HEAD(, dma_test_task) g_tasks = TAILQ_HEAD_INITIALIZER(g_tasks);
70 : :
71 : : /* User's input */
72 : : static char *g_bdev_name;
73 : : static const char *g_rw_mode_str;
74 : : static int g_rw_percentage = -1;
75 : : static uint32_t g_queue_depth;
76 : : static uint32_t g_io_size;
77 : : static uint32_t g_run_time_sec;
78 : : static uint32_t g_run_count;
79 : : static uint32_t g_test_ops;
80 : : static bool g_is_random;
81 : : static bool g_force_memory_domains_support;
82 : :
83 : : static struct spdk_thread *g_main_thread;
84 : : static struct spdk_poller *g_runtime_poller;
85 : : static struct spdk_memory_domain *g_domain;
86 : : static uint64_t g_num_blocks_per_io;
87 : : static uint32_t g_num_construct_tasks;
88 : : static uint32_t g_num_complete_tasks;
89 : : static uint64_t g_start_tsc;
90 : : static int g_run_rc;
91 : :
92 : : static void destroy_tasks(void);
93 : : static int dma_test_submit_io(struct dma_test_req *req);
94 : :
95 : : static void
96 : 4 : print_total_stats(void)
97 : : {
98 : : struct dma_test_task *task;
99 : 4 : uint64_t tsc_rate = spdk_get_ticks_hz();
100 [ - + ]: 4 : uint64_t test_time_usec = (spdk_get_ticks() - g_start_tsc) * SPDK_SEC_TO_USEC / tsc_rate;
101 : 4 : uint64_t total_tsc = 0, total_io_completed = 0;
102 : : double task_iops, task_bw, task_min_lat, task_avg_lat, task_max_lat;
103 : 4 : double total_iops = 0, total_bw = 0, total_min_lat = (double)UINT64_MAX, total_max_lat = 0,
104 : : total_avg_lat;
105 : :
106 [ - + ]: 4 : printf("==========================================================================\n");
107 [ - + ]: 4 : printf("%*s\n", 55, "Latency [us]");
108 [ - + ]: 4 : printf("%*s %10s %10s %10s %10s\n", 19, "IOPS", "MiB/s", "Average", "min", "max");
109 : :
110 [ + + ]: 12 : TAILQ_FOREACH(task, &g_tasks, link) {
111 [ - + ]: 8 : if (!task->stats.io_completed) {
112 : 0 : continue;
113 : : }
114 : 8 : task_iops = (double)task->stats.io_completed * SPDK_SEC_TO_USEC / test_time_usec;
115 : 8 : task_bw = task_iops * g_io_size / (1024 * 1024);
116 : 8 : task_avg_lat = (double)task->stats.total_tsc / task->stats.io_completed * SPDK_SEC_TO_USEC /
117 : : tsc_rate;
118 : 8 : task_min_lat = (double)task->stats.min_tsc * SPDK_SEC_TO_USEC / tsc_rate;
119 : 8 : task_max_lat = (double)task->stats.max_tsc * SPDK_SEC_TO_USEC / tsc_rate;
120 : :
121 : 8 : total_iops += task_iops;
122 : 8 : total_bw += task_bw;
123 : 8 : total_io_completed += task->stats.io_completed;
124 : 8 : total_tsc += task->stats.total_tsc;
125 [ + + ]: 8 : if (task_min_lat < total_min_lat) {
126 : 7 : total_min_lat = task_min_lat;
127 : : }
128 [ + + ]: 8 : if (task_max_lat > total_max_lat) {
129 : 5 : total_max_lat = task_max_lat;
130 : : }
131 [ - + ]: 8 : printf("Core %2u: %10.2f %10.2f %10.2f %10.2f %10.2f\n",
132 : : task->lcore, task_iops, task_bw, task_avg_lat, task_min_lat, task_max_lat);
133 : : }
134 : :
135 [ + - ]: 4 : if (total_io_completed) {
136 : 4 : total_avg_lat = (double)total_tsc / total_io_completed * SPDK_SEC_TO_USEC / tsc_rate;
137 [ - + ]: 4 : printf("==========================================================================\n");
138 [ - + ]: 4 : printf("%-*s %10.2f %10.2f %10.2f %10.2f %10.2f\n",
139 : : 8, "Total :", total_iops, total_bw, total_avg_lat, total_min_lat, total_max_lat);
140 : 4 : printf("\n");
141 : : }
142 : 4 : }
143 : :
144 : : static void
145 : 0 : print_periodic_stats(void)
146 : : {
147 : : struct dma_test_task *task;
148 : 0 : uint64_t io_last_sec = 0, tsc_last_sec = 0;
149 : : double lat_last_sec, bw_last_sec;
150 : :
151 [ # # ]: 0 : TAILQ_FOREACH(task, &g_tasks, link) {
152 : 0 : io_last_sec += task->stats.io_completed - task->last_stats.io_completed;
153 : 0 : tsc_last_sec += task->stats.total_tsc - task->last_stats.total_tsc;
154 [ # # # # ]: 0 : memcpy(&task->last_stats, &task->stats, sizeof(task->stats));
155 : : }
156 : :
157 [ # # ]: 0 : printf("Running %3u/%-3u sec", g_run_count, g_run_time_sec);
158 [ # # ]: 0 : if (io_last_sec) {
159 : 0 : lat_last_sec = (double)tsc_last_sec / io_last_sec * SPDK_SEC_TO_USEC / spdk_get_ticks_hz();
160 : 0 : bw_last_sec = (double)io_last_sec * g_io_size / (1024 * 1024);
161 [ # # ]: 0 : printf(" IOPS: %-8"PRIu64" BW: %-6.2f [MiB/s] avg.lat %-5.2f [us]",
162 : : io_last_sec, bw_last_sec, lat_last_sec);
163 : : }
164 : :
165 : 0 : printf("\r");
166 : 0 : fflush(stdout);
167 : 0 : }
168 : :
169 : : static void
170 : 8 : dma_test_task_complete(void *ctx)
171 : : {
172 [ - + ]: 8 : assert(g_num_complete_tasks > 0);
173 : :
174 [ + + ]: 8 : if (--g_num_complete_tasks == 0) {
175 : 4 : spdk_poller_unregister(&g_runtime_poller);
176 : 4 : print_total_stats();
177 : 4 : spdk_app_stop(g_run_rc);
178 : : }
179 : 8 : }
180 : :
181 : : static inline void
182 : 128 : dma_test_check_and_signal_task_done(struct dma_test_task *task)
183 : : {
184 [ + + ]: 128 : if (task->io_inflight == 0) {
185 : 8 : spdk_put_io_channel(task->channel);
186 : 8 : spdk_bdev_close(task->desc);
187 : 8 : spdk_thread_send_msg(g_main_thread, dma_test_task_complete, task);
188 : 8 : spdk_thread_exit(spdk_get_thread());
189 : : }
190 : 128 : }
191 : :
192 : : static inline void
193 : 1370697 : dma_test_task_update_stats(struct dma_test_task *task, uint64_t submit_tsc)
194 : : {
195 : 1370697 : uint64_t tsc_diff = spdk_get_ticks() - submit_tsc;
196 : :
197 : 1370697 : task->stats.io_completed++;
198 : 1370697 : task->stats.total_tsc += tsc_diff;
199 [ + + ]: 1370697 : if (spdk_unlikely(tsc_diff < task->stats.min_tsc)) {
200 : 209 : task->stats.min_tsc = tsc_diff;
201 : : }
202 [ + + ]: 1370697 : if (spdk_unlikely(tsc_diff > task->stats.max_tsc)) {
203 : 95 : task->stats.max_tsc = tsc_diff;
204 : : }
205 : 1370697 : }
206 : :
207 : : static void
208 : 1370697 : dma_test_bdev_io_completion_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
209 : : {
210 : 1370697 : struct dma_test_req *req = cb_arg;
211 : 1370697 : struct dma_test_task *task = req->task;
212 : :
213 [ - + ]: 1370697 : assert(task->io_inflight > 0);
214 : 1370697 : --task->io_inflight;
215 : 1370697 : dma_test_task_update_stats(task, req->submit_tsc);
216 : :
217 [ - + ]: 1370697 : if (!success) {
218 [ # # ]: 0 : if (!g_run_rc) {
219 [ # # ]: 0 : fprintf(stderr, "IO completed with error\n");
220 : 0 : g_run_rc = -1;
221 : : }
222 : 0 : task->is_draining = true;
223 : : }
224 : :
225 : 1370697 : spdk_bdev_free_io(bdev_io);
226 : :
227 [ - + + + ]: 1370697 : if (spdk_unlikely(task->is_draining)) {
228 : 128 : dma_test_check_and_signal_task_done(task);
229 : 128 : return;
230 : : }
231 : :
232 : 1370569 : dma_test_submit_io(req);
233 : : }
234 : :
235 : : static inline uint64_t
236 : 1370697 : dma_test_get_offset_in_ios(struct dma_test_task *task)
237 : : {
238 : : uint64_t offset;
239 : :
240 [ - + + - ]: 1370697 : if (task->is_random) {
241 [ - + ]: 1370697 : offset = rand_r(&task->seed) % task->max_offset_in_ios;
242 : : } else {
243 : 0 : offset = task->cur_io_offset++;
244 [ # # ]: 0 : if (spdk_unlikely(task->cur_io_offset == task->max_offset_in_ios)) {
245 : 0 : task->cur_io_offset = 0;
246 : : }
247 : : }
248 : :
249 : 1370697 : return offset;
250 : : }
251 : :
252 : : static inline bool
253 : 1370697 : dma_test_task_is_read(struct dma_test_task *task)
254 : : {
255 [ + + ]: 1370697 : if (task->rw_percentage == 100) {
256 : 814168 : return true;
257 : : }
258 [ + - + + ]: 556529 : if (task->rw_percentage != 0 && (rand_r(&task->seed) % 100) < task->rw_percentage) {
259 : 389535 : return true;
260 : : }
261 : 166994 : return false;
262 : : }
263 : :
264 : : static void
265 : 1389171 : dma_test_data_cpl(void *ctx)
266 : : {
267 : 1389171 : struct dma_test_data_cpl_ctx *cpl_ctx = ctx;
268 : :
269 : 1389171 : cpl_ctx->data_cpl(cpl_ctx->data_cpl_arg, 0);
270 : 1389171 : free(cpl_ctx);
271 : 1389171 : }
272 : :
273 : : static int
274 : 574900 : dma_test_copy_memory(struct dma_test_req *req, struct iovec *dst_iov, uint32_t dst_iovcnt,
275 : : struct iovec *src_iov, uint32_t src_iovcnt, spdk_memory_domain_data_cpl_cb cpl_cb, void *cpl_cb_arg)
276 : : {
277 : : struct dma_test_data_cpl_ctx *cpl_ctx;
278 : :
279 : 574900 : cpl_ctx = calloc(1, sizeof(*cpl_ctx));
280 [ - + ]: 574900 : if (!cpl_ctx) {
281 : 0 : return -ENOMEM;
282 : : }
283 : :
284 : 574900 : cpl_ctx->data_cpl = cpl_cb;
285 : 574900 : cpl_ctx->data_cpl_arg = cpl_cb_arg;
286 : :
287 : 574900 : spdk_iovcpy(src_iov, src_iovcnt, dst_iov, dst_iovcnt);
288 : 574900 : req->task->num_pull_push++;
289 : 574900 : spdk_thread_send_msg(req->task->thread, dma_test_data_cpl, cpl_ctx);
290 : :
291 : 574900 : return 0;
292 : : }
293 : :
294 : : static int
295 : 402636 : dma_test_push_memory_cb(struct spdk_memory_domain *dst_domain,
296 : : void *dst_domain_ctx,
297 : : struct iovec *dst_iov, uint32_t dst_iovcnt, struct iovec *src_iov, uint32_t src_iovcnt,
298 : : spdk_memory_domain_data_cpl_cb cpl_cb, void *cpl_cb_arg)
299 : : {
300 : 402636 : struct dma_test_req *req = dst_domain_ctx;
301 : :
302 : 402636 : return dma_test_copy_memory(req, dst_iov, dst_iovcnt, src_iov, src_iovcnt, cpl_cb, cpl_cb_arg);
303 : : }
304 : :
305 : : static int
306 : 172264 : dma_test_pull_memory_cb(struct spdk_memory_domain *src_domain,
307 : : void *src_domain_ctx,
308 : : struct iovec *src_iov, uint32_t src_iovcnt, struct iovec *dst_iov, uint32_t dst_iovcnt,
309 : : spdk_memory_domain_data_cpl_cb cpl_cb, void *cpl_cb_arg)
310 : : {
311 : 172264 : struct dma_test_req *req = src_domain_ctx;
312 : :
313 : 172264 : return dma_test_copy_memory(req, dst_iov, dst_iovcnt, src_iov, src_iovcnt, cpl_cb, cpl_cb_arg);
314 : : }
315 : :
316 : : static int
317 : 814271 : dma_test_memzero_cb(struct spdk_memory_domain *src_domain, void *src_domain_ctx,
318 : : struct iovec *iov, uint32_t iovcnt,
319 : : spdk_memory_domain_data_cpl_cb cpl_cb, void *cpl_cb_arg)
320 : : {
321 : 814271 : struct dma_test_req *req = src_domain_ctx;
322 : : struct dma_test_data_cpl_ctx *cpl_ctx;
323 : : uint32_t i;
324 : :
325 : 814271 : cpl_ctx = calloc(1, sizeof(*cpl_ctx));
326 [ - + ]: 814271 : if (!cpl_ctx) {
327 : 0 : return -ENOMEM;
328 : : }
329 : :
330 : 814271 : cpl_ctx->data_cpl = cpl_cb;
331 : 814271 : cpl_ctx->data_cpl_arg = cpl_cb_arg;
332 : :
333 [ + + ]: 1628542 : for (i = 0; i < iovcnt; i++) {
334 [ - + ]: 814271 : memset(iov[i].iov_base, 0, iov[i].iov_len);
335 : : }
336 : 814271 : req->task->num_mem_zero++;
337 : :
338 : 814271 : spdk_thread_send_msg(req->task->thread, dma_test_data_cpl, cpl_ctx);
339 : :
340 : 814271 : return 0;
341 : : }
342 : :
343 : :
344 : : static int
345 : 412701 : dma_test_translate_memory_cb(struct spdk_memory_domain *src_domain, void *src_domain_ctx,
346 : : struct spdk_memory_domain *dst_domain, struct spdk_memory_domain_translation_ctx *dst_domain_ctx,
347 : : void *addr, size_t len, struct spdk_memory_domain_translation_result *result)
348 : : {
349 : 412701 : struct dma_test_req *req = src_domain_ctx;
350 : 412701 : struct ibv_qp *dst_domain_qp = (struct ibv_qp *)dst_domain_ctx->rdma.ibv_qp;
351 : :
352 [ + - - + ]: 412701 : if (spdk_unlikely(addr < req->iov.iov_base ||
353 : : (uint8_t *)addr + len > (uint8_t *)req->iov.iov_base + req->iov.iov_len)) {
354 [ # # ]: 0 : fprintf(stderr, "incorrect data %p, len %zu\n", addr, len);
355 : 0 : return -1;
356 : : }
357 : :
358 [ + + ]: 412701 : if (spdk_unlikely(!req->mr)) {
359 [ + - ]: 128 : req->mr = ibv_reg_mr(dst_domain_qp->pd, req->iov.iov_base, req->iov.iov_len,
360 : : IBV_ACCESS_LOCAL_WRITE |
361 : : IBV_ACCESS_REMOTE_READ |
362 : : IBV_ACCESS_REMOTE_WRITE);
363 [ - + ]: 64 : if (!req->mr) {
364 [ # # ]: 0 : fprintf(stderr, "Failed to register memory region, errno %d\n", errno);
365 : 0 : return -1;
366 : : }
367 : : }
368 : :
369 : 412701 : result->iov.iov_base = addr;
370 : 412701 : result->iov.iov_len = len;
371 : 412701 : result->iov_count = 1;
372 : 412701 : result->rdma.lkey = req->mr->lkey;
373 : 412701 : result->rdma.rkey = req->mr->rkey;
374 : 412701 : result->dst_domain = dst_domain;
375 : :
376 : 412701 : req->task->num_translations++;
377 : :
378 : 412701 : return 0;
379 : : }
380 : :
381 : : static int
382 : 1370697 : dma_test_submit_io(struct dma_test_req *req)
383 : : {
384 : 1370697 : struct dma_test_task *task = req->task;
385 : : uint64_t offset_in_ios;
386 : : int rc;
387 : : bool is_read;
388 : :
389 : 1370697 : offset_in_ios = dma_test_get_offset_in_ios(task);
390 : 1370697 : is_read = dma_test_task_is_read(task);
391 : 1370697 : req->submit_tsc = spdk_get_ticks();
392 [ + + ]: 1370697 : if (is_read) {
393 : 2407406 : rc = spdk_bdev_readv_blocks_ext(task->desc, task->channel, &req->iov, 1,
394 : 1203703 : offset_in_ios * task->num_blocks_per_io, task->num_blocks_per_io,
395 : : dma_test_bdev_io_completion_cb, req, &req->io_opts);
396 : : } else {
397 : 333988 : rc = spdk_bdev_writev_blocks_ext(task->desc, task->channel, &req->iov, 1,
398 : 166994 : offset_in_ios * task->num_blocks_per_io, task->num_blocks_per_io,
399 : : dma_test_bdev_io_completion_cb, req, &req->io_opts);
400 : : }
401 : :
402 [ - + ]: 1370697 : if (spdk_unlikely(rc)) {
403 [ # # ]: 0 : if (!g_run_rc) {
404 : : /* log an error only once */
405 [ # # # # ]: 0 : fprintf(stderr, "Failed to submit %s IO, rc %d, stop sending IO\n", is_read ? "read" : "write", rc);
406 : 0 : g_run_rc = rc;
407 : : }
408 : 0 : task->is_draining = true;
409 : 0 : dma_test_check_and_signal_task_done(task);
410 : 0 : return rc;
411 : : }
412 : :
413 : 1370697 : task->io_inflight++;
414 : :
415 : 1370697 : return 0;
416 : : }
417 : :
418 : : static void
419 : 0 : dma_test_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx)
420 : : {
421 : 0 : struct dma_test_task *task = event_ctx;
422 : :
423 [ # # ]: 0 : if (type == SPDK_BDEV_EVENT_REMOVE) {
424 : 0 : task->is_draining = true;
425 : : }
426 : 0 : }
427 : :
428 : : static void
429 : 0 : dma_test_bdev_dummy_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
430 : : void *event_ctx)
431 : : {
432 : 0 : }
433 : :
434 : : static void
435 : 8 : dma_test_task_run(void *ctx)
436 : : {
437 : 8 : struct dma_test_task *task = ctx;
438 : : uint32_t i;
439 : 8 : int rc = 0;
440 : :
441 [ + + + - ]: 136 : for (i = 0; i < g_queue_depth && rc == 0; i++) {
442 : 128 : rc = dma_test_submit_io(&task->reqs[i]);
443 : : }
444 : 8 : }
445 : :
446 : : static void
447 : 8 : dma_test_drain_task(void *ctx)
448 : : {
449 : 8 : struct dma_test_task *task = ctx;
450 : :
451 : 8 : task->is_draining = true;
452 : 8 : }
453 : :
454 : : static void
455 : 4 : dma_test_shutdown_cb(void)
456 : : {
457 : : struct dma_test_task *task;
458 : :
459 : 4 : spdk_poller_unregister(&g_runtime_poller);
460 : :
461 [ + + ]: 12 : TAILQ_FOREACH(task, &g_tasks, link) {
462 : 8 : spdk_thread_send_msg(task->thread, dma_test_drain_task, task);
463 : : }
464 : 4 : }
465 : :
466 : : static int
467 : 20 : dma_test_run_time_poller(void *ctx)
468 : : {
469 : 20 : g_run_count++;
470 : :
471 [ + + ]: 20 : if (g_run_count < g_run_time_sec) {
472 [ - + ]: 16 : if (isatty(STDOUT_FILENO)) {
473 : 0 : print_periodic_stats();
474 : : }
475 : : } else {
476 : 4 : dma_test_shutdown_cb();
477 : : }
478 : :
479 : 20 : return SPDK_POLLER_BUSY;
480 : : }
481 : :
482 : : static void
483 : 8 : dma_test_construct_task_done(void *ctx)
484 : : {
485 : : struct dma_test_task *task;
486 : :
487 [ - + ]: 8 : assert(g_num_construct_tasks > 0);
488 : 8 : --g_num_construct_tasks;
489 : :
490 [ + + ]: 8 : if (g_num_construct_tasks != 0) {
491 : 4 : return;
492 : : }
493 : :
494 [ - + ]: 4 : if (g_run_rc) {
495 [ # # # # ]: 0 : fprintf(stderr, "Initialization failed with error %d\n", g_run_rc);
496 : 0 : spdk_app_stop(g_run_rc);
497 : 0 : return;
498 : : }
499 : :
500 : 4 : g_runtime_poller = spdk_poller_register_named(dma_test_run_time_poller, NULL, 1 * 1000 * 1000,
501 : : "dma_test_run_time_poller");
502 [ - + ]: 4 : if (!g_runtime_poller) {
503 [ # # # # ]: 0 : fprintf(stderr, "Failed to run timer\n");
504 : 0 : spdk_app_stop(-1);
505 : 0 : return;
506 : : }
507 : :
508 [ - + ]: 4 : printf("Initialization complete, running %s IO for %u sec on %u cores\n", g_rw_mode_str,
509 : : g_run_time_sec, spdk_env_get_core_count());
510 : 4 : g_start_tsc = spdk_get_ticks();
511 [ + + ]: 12 : TAILQ_FOREACH(task, &g_tasks, link) {
512 : 8 : spdk_thread_send_msg(task->thread, dma_test_task_run, task);
513 : : }
514 : : }
515 : :
516 : : static void
517 : 8 : dma_test_construct_task_on_thread(void *ctx)
518 : : {
519 : 8 : struct dma_test_task *task = ctx;
520 : : int rc;
521 : :
522 : 8 : rc = spdk_bdev_open_ext(task->bdev_name, true, dma_test_bdev_event_cb, task, &task->desc);
523 [ - + ]: 8 : if (rc) {
524 [ # # # # ]: 0 : fprintf(stderr, "Failed to open bdev %s, rc %d\n", task->bdev_name, rc);
525 : 0 : g_run_rc = rc;
526 : 0 : spdk_thread_send_msg(g_main_thread, dma_test_construct_task_done, NULL);
527 : 0 : return;
528 : : }
529 : :
530 : 8 : task->channel = spdk_bdev_get_io_channel(task->desc);
531 [ - + ]: 8 : if (!task->channel) {
532 : 0 : spdk_bdev_close(task->desc);
533 : 0 : task->desc = NULL;
534 [ # # # # ]: 0 : fprintf(stderr, "Failed to open bdev %s, rc %d\n", task->bdev_name, rc);
535 : 0 : g_run_rc = rc;
536 : 0 : spdk_thread_send_msg(g_main_thread, dma_test_construct_task_done, NULL);
537 : 0 : return;
538 : : }
539 : :
540 [ - + ]: 16 : task->max_offset_in_ios = spdk_bdev_get_num_blocks(spdk_bdev_desc_get_bdev(
541 : 16 : task->desc)) / task->num_blocks_per_io;
542 : :
543 : 8 : spdk_thread_send_msg(g_main_thread, dma_test_construct_task_done, task);
544 : : }
545 : :
546 : : static bool
547 : 4 : dma_test_check_bdev_supports_rdma_memory_domain(struct spdk_bdev *bdev)
548 : : {
549 : : struct spdk_memory_domain **bdev_domains;
550 : : int bdev_domains_count, bdev_domains_count_tmp, i;
551 : 4 : bool rdma_domain_supported = false;
552 : :
553 : 4 : bdev_domains_count = spdk_bdev_get_memory_domains(bdev, NULL, 0);
554 : :
555 [ - + ]: 4 : if (bdev_domains_count < 0) {
556 [ # # # # ]: 0 : fprintf(stderr, "Failed to get bdev memory domains count, rc %d\n", bdev_domains_count);
557 : 0 : return false;
558 [ - + ]: 4 : } else if (bdev_domains_count == 0) {
559 [ # # # # ]: 0 : fprintf(stderr, "bdev %s doesn't support any memory domains\n", spdk_bdev_get_name(bdev));
560 : 0 : return false;
561 : : }
562 : :
563 [ - + - + ]: 4 : fprintf(stdout, "bdev %s reports %d memory domains\n", spdk_bdev_get_name(bdev),
564 : : bdev_domains_count);
565 : :
566 : 4 : bdev_domains = calloc((size_t)bdev_domains_count, sizeof(*bdev_domains));
567 [ - + ]: 4 : if (!bdev_domains) {
568 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate memory domains\n");
569 : 0 : return false;
570 : : }
571 : :
572 : 4 : bdev_domains_count_tmp = spdk_bdev_get_memory_domains(bdev, bdev_domains, bdev_domains_count);
573 [ - + ]: 4 : if (bdev_domains_count_tmp != bdev_domains_count) {
574 [ # # # # ]: 0 : fprintf(stderr, "Unexpected bdev domains return value %d\n", bdev_domains_count_tmp);
575 : 0 : return false;
576 : : }
577 : :
578 [ + + ]: 6 : for (i = 0; i < bdev_domains_count; i++) {
579 [ + + ]: 5 : if (spdk_memory_domain_get_dma_device_type(bdev_domains[i]) == SPDK_DMA_DEVICE_TYPE_RDMA) {
580 : : /* Bdev supports memory domain of RDMA type, we can try to submit IO request to it using
581 : : * bdev ext API */
582 : 3 : rdma_domain_supported = true;
583 : 3 : break;
584 : : }
585 : : }
586 : :
587 [ + + - + : 4 : fprintf(stdout, "bdev %s %s RDMA memory domain\n", spdk_bdev_get_name(bdev),
- + ]
588 : : rdma_domain_supported ? "supports" : "doesn't support");
589 : 4 : free(bdev_domains);
590 : :
591 : 4 : return rdma_domain_supported;
592 : : }
593 : :
594 : : static int
595 : 8 : allocate_task(uint32_t core, const char *bdev_name)
596 : : {
597 : 0 : char thread_name[32];
598 : 0 : struct spdk_cpuset cpu_set;
599 : : uint32_t i;
600 : : struct dma_test_task *task;
601 : : struct dma_test_req *req;
602 : :
603 : 8 : task = calloc(1, sizeof(*task));
604 [ - + ]: 8 : if (!task) {
605 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate per thread task\n");
606 : 0 : return -ENOMEM;
607 : : }
608 : :
609 : 8 : TAILQ_INSERT_TAIL(&g_tasks, task, link);
610 : :
611 : 8 : task->reqs = calloc(g_queue_depth, sizeof(*task->reqs));
612 [ - + ]: 8 : if (!task->reqs) {
613 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate requests\n");
614 : 0 : return -ENOMEM;
615 : : }
616 : :
617 [ + + ]: 136 : for (i = 0; i < g_queue_depth; i++) {
618 : 128 : req = &task->reqs[i];
619 : 128 : req->task = task;
620 : 128 : req->iov.iov_len = g_io_size;
621 : 128 : req->iov.iov_base = malloc(req->iov.iov_len);
622 [ - + ]: 128 : if (!req->iov.iov_base) {
623 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate request data buffer\n");
624 : 0 : return -ENOMEM;
625 : : }
626 [ - + ]: 128 : memset(req->iov.iov_base, 0xc, req->iov.iov_len);
627 : 128 : req->io_opts.size = sizeof(req->io_opts);
628 : 128 : req->io_opts.memory_domain = g_domain;
629 : 128 : req->io_opts.memory_domain_ctx = req;
630 : : }
631 : :
632 [ - + ]: 8 : snprintf(thread_name, 32, "task_%u", core);
633 : 8 : spdk_cpuset_zero(&cpu_set);
634 : 8 : spdk_cpuset_set_cpu(&cpu_set, core, true);
635 : 8 : task->thread = spdk_thread_create(thread_name, &cpu_set);
636 [ - + ]: 8 : if (!task->thread) {
637 [ # # # # ]: 0 : fprintf(stderr, "Failed to create SPDK thread, core %u, cpu_mask %s\n", core,
638 : : spdk_cpuset_fmt(&cpu_set));
639 : 0 : return -ENOMEM;
640 : : }
641 : :
642 : 8 : task->seed = core;
643 : 8 : task->lcore = core;
644 : 8 : task->bdev_name = bdev_name;
645 [ - + ]: 8 : task->is_random = g_is_random;
646 : 8 : task->rw_percentage = g_rw_percentage;
647 : 8 : task->num_blocks_per_io = g_num_blocks_per_io;
648 : 8 : task->stats.min_tsc = UINT64_MAX;
649 : :
650 : 8 : return 0;
651 : : }
652 : :
653 : : static void
654 : 8 : destroy_task(struct dma_test_task *task)
655 : : {
656 : : struct dma_test_req *req;
657 : : uint32_t i;
658 : :
659 [ + + ]: 136 : for (i = 0; i < g_queue_depth; i++) {
660 : 128 : req = &task->reqs[i];
661 [ + + ]: 128 : if (req->mr) {
662 : 64 : ibv_dereg_mr(req->mr);
663 : : }
664 : 128 : free(req->iov.iov_base);
665 : : }
666 : 8 : free(task->reqs);
667 [ + + ]: 8 : TAILQ_REMOVE(&g_tasks, task, link);
668 : 8 : free(task);
669 : 8 : }
670 : :
671 : : static void
672 : 4 : destroy_tasks(void)
673 : : {
674 : : struct dma_test_task *task, *tmp_task;
675 : :
676 [ + + ]: 12 : TAILQ_FOREACH_SAFE(task, &g_tasks, link, tmp_task) {
677 : 8 : destroy_task(task);
678 : : }
679 : 4 : }
680 : :
681 : : static int
682 : 4 : verify_tasks(void)
683 : : {
684 : : struct dma_test_task *task;
685 : 4 : uint64_t total_requests = 0;
686 : 4 : uint64_t num_translations = 0;
687 : 4 : uint64_t num_pull_push = 0;
688 : 4 : uint64_t num_memzero = 0;
689 : 4 : int rc = 0;
690 : :
691 [ - + ]: 4 : if (!g_test_ops) {
692 : : /* No specific ops were requested, nothing to check */
693 : 0 : return rc;
694 : : }
695 : :
696 [ + + ]: 12 : TAILQ_FOREACH(task, &g_tasks, link) {
697 : 8 : total_requests += task->stats.io_completed;
698 : 8 : num_translations += task->num_translations;
699 : 8 : num_pull_push += task->num_pull_push;
700 : 8 : num_memzero += task->num_mem_zero;
701 : : }
702 : :
703 [ + + ]: 4 : if (g_test_ops & DMA_TEST_DOMAIN_OP_TRANSLATE) {
704 [ - + ]: 2 : if (num_translations == 0) {
705 [ # # # # ]: 0 : fprintf(stderr, "Requested \"translate\" operation, but it was not executed\n");
706 : 0 : rc = -EINVAL;
707 : : }
708 : : }
709 [ + + ]: 4 : if (g_test_ops & DMA_TEST_DOMAIN_OP_PULL_PUSH) {
710 [ - + ]: 1 : if (num_pull_push == 0) {
711 [ # # # # ]: 0 : fprintf(stderr, "Requested \"pull_push\" operation, but it was not executed\n");
712 : 0 : rc = -EINVAL;
713 : : }
714 : : }
715 [ + + ]: 4 : if (g_test_ops & DMA_TEST_DOMAIN_OP_MEMZERO) {
716 [ - + ]: 1 : if (num_memzero == 0) {
717 [ # # # # ]: 0 : fprintf(stderr, "Requested \"memzero\" operation, but it was not executed\n");
718 : 0 : rc = -EINVAL;
719 : : }
720 : : }
721 : :
722 : : /* bdev request can be split, so the total number of pull_push +translate operations
723 : : * can be bigger than total_number of requests */
724 [ - + ]: 4 : if (num_translations + num_pull_push + num_memzero < total_requests) {
725 [ # # # # ]: 0 : fprintf(stderr,
726 : : "Operations number mismatch: translate %"PRIu64", pull_push %"PRIu64", mem_zero %"PRIu64" expected total %"PRIu64"\n",
727 : : num_translations, num_pull_push, num_memzero, total_requests);
728 : 0 : rc = -EINVAL;
729 : : } else {
730 [ - + - + ]: 4 : fprintf(stdout,
731 : : "Total operations: %"PRIu64", translate %"PRIu64" pull_push %"PRIu64" memzero %"PRIu64"\n",
732 : : total_requests, num_translations, num_pull_push, num_memzero);
733 : : }
734 : :
735 : 4 : return rc;
736 : : }
737 : :
738 : : static void
739 : 4 : dma_test_start(void *arg)
740 : : {
741 : 0 : struct spdk_bdev_desc *desc;
742 : : struct spdk_bdev *bdev;
743 : : struct dma_test_task *task;
744 : : uint32_t block_size, i;
745 : : int rc;
746 : :
747 : 4 : rc = spdk_bdev_open_ext(g_bdev_name, true, dma_test_bdev_dummy_event_cb, NULL, &desc);
748 [ - + ]: 4 : if (rc) {
749 [ # # # # ]: 0 : fprintf(stderr, "Can't find bdev %s\n", g_bdev_name);
750 : 0 : spdk_app_stop(-ENODEV);
751 : 0 : return;
752 : : }
753 : 4 : bdev = spdk_bdev_desc_get_bdev(desc);
754 : : /* This function checks if bdev supports memory domains. Test is not failed if there are
755 : : * no memory domains since bdev layer can pull/push data */
756 [ + + - + : 4 : if (!dma_test_check_bdev_supports_rdma_memory_domain(bdev) && g_force_memory_domains_support) {
- + ]
757 [ # # # # ]: 0 : fprintf(stderr, "Test aborted due to \"-f\" (force memory domains support) option\n");
758 : 0 : spdk_bdev_close(desc);
759 : 0 : spdk_app_stop(-ENODEV);
760 : 0 : return;
761 : : }
762 : :
763 : 4 : g_main_thread = spdk_get_thread();
764 : :
765 : 4 : block_size = spdk_bdev_get_block_size(bdev);
766 [ + - - + : 4 : if (g_io_size < block_size || g_io_size % block_size != 0) {
- + ]
767 [ # # # # ]: 0 : fprintf(stderr, "Invalid io_size %u requested, bdev block size %u\n", g_io_size, block_size);
768 : 0 : spdk_bdev_close(desc);
769 : 0 : spdk_app_stop(-EINVAL);
770 : 0 : return;
771 : : }
772 [ - + ]: 4 : g_num_blocks_per_io = g_io_size / block_size;
773 : :
774 : : /* Create a memory domain to represent the source memory domain.
775 : : * Since we don't actually have a remote memory domain in this test, this will describe memory
776 : : * on the local system and the translation to the destination memory domain will be trivial.
777 : : * But this at least allows us to demonstrate the flow and test the functionality. */
778 : 4 : rc = spdk_memory_domain_create(&g_domain, SPDK_DMA_DEVICE_TYPE_RDMA, NULL, "test_dma");
779 [ - + ]: 4 : if (rc != 0) {
780 : 0 : spdk_bdev_close(desc);
781 : 0 : spdk_app_stop(rc);
782 : 0 : return;
783 : : }
784 : 4 : spdk_memory_domain_set_translation(g_domain, dma_test_translate_memory_cb);
785 : 4 : spdk_memory_domain_set_pull(g_domain, dma_test_pull_memory_cb);
786 : 4 : spdk_memory_domain_set_push(g_domain, dma_test_push_memory_cb);
787 : 4 : spdk_memory_domain_set_memzero(g_domain, dma_test_memzero_cb);
788 : :
789 [ + + ]: 12 : SPDK_ENV_FOREACH_CORE(i) {
790 : 8 : rc = allocate_task(i, g_bdev_name);
791 [ - + ]: 8 : if (rc) {
792 : 0 : destroy_tasks();
793 : 0 : spdk_bdev_close(desc);
794 : 0 : spdk_app_stop(rc);
795 : 0 : return;
796 : : }
797 : 8 : g_num_construct_tasks++;
798 : 8 : g_num_complete_tasks++;
799 : : }
800 : :
801 [ + + ]: 12 : TAILQ_FOREACH(task, &g_tasks, link) {
802 : 8 : spdk_thread_send_msg(task->thread, dma_test_construct_task_on_thread, task);
803 : : }
804 : :
805 : 4 : spdk_bdev_close(desc);
806 : : }
807 : :
808 : : static void
809 : 0 : print_usage(void)
810 : : {
811 [ # # ]: 0 : printf(" -b <bdev> bdev name for test\n");
812 [ # # ]: 0 : printf(" -f force memory domains support - abort test if bdev doesn't report memory domains\n");
813 [ # # ]: 0 : printf(" -q <val> io depth\n");
814 [ # # ]: 0 : printf(" -o <val> io size in bytes\n");
815 [ # # ]: 0 : printf(" -t <val> run time in seconds\n");
816 [ # # ]: 0 : printf(" -x <op,op> Comma separated memory domain operations expected in the test. Values are \"translate\" and \"pull_push\"\n");
817 [ # # ]: 0 : printf(" -w <str> io pattern (read, write, randread, randwrite, randrw)\n");
818 [ # # ]: 0 : printf(" -M <0-100> rw percentage (100 for reads, 0 for writes)\n");
819 : 0 : }
820 : :
821 : : static int
822 : 4 : parse_expected_ops(const char *_str)
823 : : {
824 [ - + ]: 4 : char *str = strdup(_str);
825 : : char *tok;
826 : 4 : int rc = 0;
827 : :
828 [ - + ]: 4 : if (!str) {
829 [ # # ]: 0 : fprintf(stderr, "Failed to dup args\n");
830 : 0 : return -ENOMEM;
831 : : }
832 : :
833 : 4 : tok = strtok(str, ",");
834 [ + + ]: 8 : while (tok) {
835 [ - + + + ]: 4 : if (strcmp(tok, "translate") == 0) {
836 : 2 : g_test_ops |= DMA_TEST_DOMAIN_OP_TRANSLATE;
837 [ - + + + ]: 2 : } else if (strcmp(tok, "pull_push") == 0) {
838 : 1 : g_test_ops |= DMA_TEST_DOMAIN_OP_PULL_PUSH;
839 [ - + + - ]: 1 : } else if (strcmp(tok, "memzero") == 0) {
840 : 1 : g_test_ops |= DMA_TEST_DOMAIN_OP_MEMZERO;
841 : : } else {
842 [ # # ]: 0 : fprintf(stderr, "Unknown value %s\n", tok);
843 : 0 : rc = -EINVAL;
844 : 0 : break;
845 : : }
846 : 4 : tok = strtok(NULL, ",");
847 : : }
848 : :
849 : 4 : free(str);
850 : :
851 [ + - - + ]: 4 : if (g_test_ops == 0 || rc) {
852 [ # # ]: 0 : fprintf(stderr, "-e \"%s\" specified but nothing was parsed\n", _str);
853 : 0 : return -EINVAL;
854 : : }
855 : :
856 : 4 : return rc;
857 : : }
858 : :
859 : : static int
860 : 31 : parse_arg(int ch, char *arg)
861 : : {
862 : : long tmp;
863 : :
864 [ + + + + : 31 : switch (ch) {
+ - ]
865 : 16 : case 'q':
866 : : case 'o':
867 : : case 't':
868 : : case 'M':
869 : 16 : tmp = spdk_strtol(arg, 10);
870 [ - + ]: 16 : if (tmp < 0) {
871 [ # # # # ]: 0 : fprintf(stderr, "Invalid option %c value %s\n", ch, arg);
872 : 0 : return 1;
873 : : }
874 : :
875 [ + + + + : 16 : switch (ch) {
- ]
876 : 4 : case 'q':
877 : 4 : g_queue_depth = (uint32_t) tmp;
878 : 4 : break;
879 : 4 : case 'o':
880 : 4 : g_io_size = (uint32_t) tmp;
881 : 4 : break;
882 : 4 : case 't':
883 : 4 : g_run_time_sec = (uint32_t) tmp;
884 : 4 : break;
885 : 4 : case 'M':
886 : 4 : g_rw_percentage = (uint32_t) tmp;
887 : 4 : break;
888 : : }
889 : 16 : break;
890 : 4 : case 'w':
891 : 4 : g_rw_mode_str = arg;
892 : 4 : break;
893 : 4 : case 'b':
894 : 4 : g_bdev_name = arg;
895 : 4 : break;
896 : 3 : case 'f':
897 : 3 : g_force_memory_domains_support = true;
898 : 3 : break;
899 : 4 : case 'x':
900 [ - + ]: 4 : if (parse_expected_ops(arg)) {
901 : 0 : return 1;
902 : : }
903 : 4 : break;
904 : 0 : default:
905 [ # # # # ]: 0 : fprintf(stderr, "Unknown option %c\n", ch);
906 : 0 : return 1;
907 : : }
908 : :
909 : 31 : return 0;
910 : : }
911 : :
912 : : static int
913 : 4 : verify_args(void)
914 : : {
915 : 4 : const char *rw_mode = g_rw_mode_str;
916 : :
917 [ - + ]: 4 : if (g_queue_depth == 0) {
918 [ # # ]: 0 : fprintf(stderr, "queue depth (-q) is not set\n");
919 : 0 : return 1;
920 : : }
921 [ - + ]: 4 : if (g_io_size == 0) {
922 [ # # ]: 0 : fprintf(stderr, "io size (-o) is not set\n");
923 : 0 : return 1;
924 : : }
925 [ - + ]: 4 : if (g_run_time_sec == 0) {
926 [ # # ]: 0 : fprintf(stderr, "test run time (-t) is not set\n");
927 : 0 : return 1;
928 : : }
929 [ - + ]: 4 : if (!rw_mode) {
930 [ # # ]: 0 : fprintf(stderr, "io pattern (-w) is not set\n");
931 : 0 : return 1;
932 : : }
933 [ - + + - ]: 4 : if (strncmp(rw_mode, "rand", 4) == 0) {
934 : 4 : g_is_random = true;
935 : 4 : rw_mode = &rw_mode[4];
936 : : }
937 [ - + + + : 4 : if (strcmp(rw_mode, "read") == 0 || strcmp(rw_mode, "write") == 0) {
- + - + ]
938 [ + - ]: 1 : if (g_rw_percentage > 0) {
939 [ - + ]: 1 : fprintf(stderr, "Ignoring -M option\n");
940 : : }
941 [ - + + - ]: 1 : g_rw_percentage = strcmp(rw_mode, "read") == 0 ? 100 : 0;
942 [ - + + - ]: 3 : } else if (strcmp(rw_mode, "rw") == 0) {
943 [ + - - + ]: 3 : if (g_rw_percentage < 0 || g_rw_percentage > 100) {
944 [ # # ]: 0 : fprintf(stderr, "Invalid -M value (%d) must be 0..100\n", g_rw_percentage);
945 : 0 : return 1;
946 : : }
947 : : } else {
948 [ # # ]: 0 : fprintf(stderr, "io pattern (-w) one of [read, write, randread, randwrite, rw, randrw]\n");
949 : 0 : return 1;
950 : : }
951 [ - + ]: 4 : if (!g_bdev_name) {
952 [ # # ]: 0 : fprintf(stderr, "bdev name (-b) is not set\n");
953 : 0 : return 1;
954 : : }
955 : :
956 : 4 : return 0;
957 : : }
958 : :
959 : : int
960 : 4 : main(int argc, char **argv)
961 : : {
962 : 4 : struct spdk_app_opts opts = {};
963 : : int rc;
964 : :
965 : 4 : spdk_app_opts_init(&opts, sizeof(opts));
966 : 4 : opts.name = "test_dma";
967 : 4 : opts.shutdown_cb = dma_test_shutdown_cb;
968 : 4 : opts.rpc_addr = NULL;
969 : :
970 : 4 : rc = spdk_app_parse_args(argc, argv, &opts, "b:fq:o:t:x:w:M:", NULL, parse_arg, print_usage);
971 [ - + ]: 4 : if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) {
972 : 0 : exit(rc);
973 : : }
974 : :
975 : 4 : rc = verify_args();
976 [ - + ]: 4 : if (rc) {
977 : 0 : exit(rc);
978 : : }
979 : :
980 : 4 : rc = spdk_app_start(&opts, dma_test_start, NULL);
981 [ + - ]: 4 : if (rc == 0) {
982 : 4 : rc = verify_tasks();
983 : : }
984 : 4 : destroy_tasks();
985 : 4 : spdk_app_fini();
986 : :
987 : 4 : return rc;
988 : : }
|