Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2020 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : : #include "spdk/config.h"
8 : :
9 : : #include "spdk/bdev.h"
10 : : #include "spdk/event.h"
11 : : #include "spdk/fd.h"
12 : : #include "spdk/string.h"
13 : : #include "spdk/util.h"
14 : : #include "spdk/vmd.h"
15 : :
16 : : #include <libaio.h>
17 : :
18 : : #ifdef SPDK_CONFIG_URING
19 : : #include <liburing.h>
20 : : #endif
21 : :
22 : : #define TIMESPEC_TO_MS(time) ((time.tv_sec * 1000) + (time.tv_nsec / 1000000))
23 : : #define STATUS_POLLER_PERIOD_SEC 1
24 : :
25 : : struct spdk_dd_opts {
26 : : char *input_file;
27 : : char *output_file;
28 : : char *input_file_flags;
29 : : char *output_file_flags;
30 : : char *input_bdev;
31 : : char *output_bdev;
32 : : uint64_t input_offset;
33 : : uint64_t output_offset;
34 : : int64_t io_unit_size;
35 : : int64_t io_unit_count;
36 : : uint32_t queue_depth;
37 : : bool aio;
38 : : bool sparse;
39 : : };
40 : :
41 : : static struct spdk_dd_opts g_opts = {
42 : : .io_unit_size = 4096,
43 : : .queue_depth = 2,
44 : : };
45 : :
46 : : enum dd_submit_type {
47 : : DD_POPULATE,
48 : : DD_READ,
49 : : DD_WRITE,
50 : : };
51 : :
52 : : struct dd_io {
53 : : uint64_t offset;
54 : : uint64_t length;
55 : : struct iocb iocb;
56 : : enum dd_submit_type type;
57 : : #ifdef SPDK_CONFIG_URING
58 : : int idx;
59 : : #endif
60 : : void *buf;
61 : : STAILQ_ENTRY(dd_io) link;
62 : : };
63 : :
64 : : enum dd_target_type {
65 : : DD_TARGET_TYPE_FILE,
66 : : DD_TARGET_TYPE_BDEV,
67 : : };
68 : :
69 : : struct dd_target {
70 : : enum dd_target_type type;
71 : :
72 : : union {
73 : : struct {
74 : : struct spdk_bdev *bdev;
75 : : struct spdk_bdev_desc *desc;
76 : : struct spdk_io_channel *ch;
77 : : } bdev;
78 : :
79 : : #ifdef SPDK_CONFIG_URING
80 : : struct {
81 : : int fd;
82 : : int idx;
83 : : } uring;
84 : : #endif
85 : : struct {
86 : : int fd;
87 : : } aio;
88 : : } u;
89 : :
90 : : /* Block size of underlying device. */
91 : : uint32_t block_size;
92 : :
93 : : /* Position of next I/O in bytes */
94 : : uint64_t pos;
95 : :
96 : : /* Total size of target in bytes */
97 : : uint64_t total_size;
98 : :
99 : : bool open;
100 : : };
101 : :
102 : : struct dd_job {
103 : : struct dd_target input;
104 : : struct dd_target output;
105 : :
106 : : struct dd_io *ios;
107 : :
108 : : union {
109 : : #ifdef SPDK_CONFIG_URING
110 : : struct {
111 : : struct io_uring ring;
112 : : bool active;
113 : : struct spdk_poller *poller;
114 : : } uring;
115 : : #endif
116 : : struct {
117 : : io_context_t io_ctx;
118 : : struct spdk_poller *poller;
119 : : } aio;
120 : : } u;
121 : :
122 : : uint32_t outstanding;
123 : : uint64_t copy_size;
124 : : STAILQ_HEAD(, dd_io) seek_queue;
125 : :
126 : : struct timespec start_time;
127 : : uint64_t total_bytes;
128 : : uint64_t incremental_bytes;
129 : : struct spdk_poller *status_poller;
130 : : };
131 : :
132 : : struct dd_flags {
133 : : char *name;
134 : : int flag;
135 : : };
136 : :
137 : : static struct dd_flags g_flags[] = {
138 : : {"append", O_APPEND},
139 : : {"direct", O_DIRECT},
140 : : {"directory", O_DIRECTORY},
141 : : {"dsync", O_DSYNC},
142 : : {"noatime", O_NOATIME},
143 : : {"noctty", O_NOCTTY},
144 : : {"nofollow", O_NOFOLLOW},
145 : : {"nonblock", O_NONBLOCK},
146 : : {"sync", O_SYNC},
147 : : {NULL, 0}
148 : : };
149 : :
150 : : static struct dd_job g_job = {};
151 : : static int g_error = 0;
152 : : static bool g_interrupt;
153 : :
154 : : static void dd_target_seek(struct dd_io *io);
155 : : static void _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg);
156 : :
157 : : static void
158 : 225 : dd_cleanup_bdev(struct dd_target io)
159 : : {
160 : : /* This can be only on the error path.
161 : : * To prevent the SEGV, need add checks here.
162 : : */
163 [ + - ]: 225 : if (io.u.bdev.ch) {
164 : 225 : spdk_put_io_channel(io.u.bdev.ch);
165 : : }
166 : :
167 : 225 : spdk_bdev_close(io.u.bdev.desc);
168 : 225 : }
169 : :
170 : : static void
171 : 398 : dd_exit(int rc)
172 : : {
173 [ + + ]: 398 : if (g_job.input.type == DD_TARGET_TYPE_FILE) {
174 : : #ifdef SPDK_CONFIG_URING
175 [ - + + + ]: 120 : if (g_opts.aio == false) {
176 : 88 : close(g_job.input.u.uring.fd);
177 : : } else
178 : : #endif
179 : : {
180 : 216 : close(g_job.input.u.aio.fd);
181 : : }
182 [ + - + + : 94 : } else if (g_job.input.type == DD_TARGET_TYPE_BDEV && g_job.input.open) {
+ + ]
183 : 92 : dd_cleanup_bdev(g_job.input);
184 : : }
185 : :
186 [ + + ]: 398 : if (g_job.output.type == DD_TARGET_TYPE_FILE) {
187 : : #ifdef SPDK_CONFIG_URING
188 [ - + + + ]: 104 : if (g_opts.aio == false) {
189 : 72 : close(g_job.output.u.uring.fd);
190 : : } else
191 : : #endif
192 : : {
193 : 193 : close(g_job.output.u.aio.fd);
194 : : }
195 [ + - + + : 133 : } else if (g_job.output.type == DD_TARGET_TYPE_BDEV && g_job.output.open) {
+ - ]
196 : 133 : dd_cleanup_bdev(g_job.output);
197 : : }
198 : :
199 [ + + + + ]: 398 : if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
200 : : #ifdef SPDK_CONFIG_URING
201 [ - + + + ]: 144 : if (g_opts.aio == false) {
202 : 112 : spdk_poller_unregister(&g_job.u.uring.poller);
203 : : } else
204 : : #endif
205 : : {
206 : 260 : spdk_poller_unregister(&g_job.u.aio.poller);
207 : : }
208 : : }
209 : :
210 : 398 : spdk_poller_unregister(&g_job.status_poller);
211 : :
212 : 398 : spdk_app_stop(rc);
213 : 398 : }
214 : :
215 : : static void
216 : 774 : dd_show_progress(bool finish)
217 : : {
218 : 774 : char *unit_str[5] = {"", "k", "M", "G", "T"};
219 : 774 : char *speed_type_str[2] = {"", "average "};
220 : 774 : char *size_unit_str = "";
221 : 774 : char *speed_unit_str = "";
222 : : char *speed_type;
223 : 774 : uint64_t size_unit = 1;
224 : 774 : uint64_t speed_unit = 1;
225 : : uint64_t speed, tmp_speed;
226 : 774 : int i = 0;
227 : : uint64_t milliseconds;
228 : : uint64_t size, tmp_size;
229 : :
230 : 774 : size = g_job.incremental_bytes;
231 : :
232 : 774 : g_job.incremental_bytes = 0;
233 : 774 : g_job.total_bytes += size;
234 : :
235 [ + + ]: 774 : if (finish) {
236 : 206 : struct timespec time_now;
237 : :
238 [ - + ]: 337 : clock_gettime(CLOCK_REALTIME, &time_now);
239 : :
240 [ + + ]: 337 : milliseconds = spdk_max(1, TIMESPEC_TO_MS(time_now) - TIMESPEC_TO_MS(g_job.start_time));
241 : 337 : size = g_job.total_bytes;
242 : : } else {
243 : 437 : milliseconds = STATUS_POLLER_PERIOD_SEC * 1000;
244 : : }
245 : :
246 : : /* Find the right unit for size displaying (B vs kB vs MB vs GB vs TB) */
247 : 774 : tmp_size = size;
248 [ + + ]: 1928 : while (tmp_size > 1024 * 10) {
249 : 1154 : tmp_size >>= 10;
250 : 1154 : size_unit <<= 10;
251 : 1154 : size_unit_str = unit_str[++i];
252 [ - + ]: 1154 : if (i == 4) {
253 : 0 : break;
254 : : }
255 : : }
256 : :
257 [ + + ]: 774 : speed_type = finish ? speed_type_str[1] : speed_type_str[0];
258 [ - + ]: 774 : speed = (size * 1000) / milliseconds;
259 : :
260 : 774 : i = 0;
261 : :
262 : : /* Find the right unit for speed displaying (Bps vs kBps vs MBps vs GBps vs TBps) */
263 : 774 : tmp_speed = speed;
264 [ + + ]: 2176 : while (tmp_speed > 1024 * 10) {
265 : 1402 : tmp_speed >>= 10;
266 : 1402 : speed_unit <<= 10;
267 : 1402 : speed_unit_str = unit_str[++i];
268 [ - + ]: 1402 : if (i == 4) {
269 : 0 : break;
270 : : }
271 : : }
272 : :
273 [ - + ]: 1486 : printf("\33[2K\rCopying: %" PRIu64 "/%" PRIu64 " [%sB] (%s%" PRIu64 " %sBps)",
274 [ - + - + ]: 774 : g_job.total_bytes / size_unit, g_job.copy_size / size_unit, size_unit_str, speed_type,
275 [ - + ]: 712 : speed / speed_unit, speed_unit_str);
276 : 774 : fflush(stdout);
277 : 774 : }
278 : :
279 : : static int
280 : 437 : dd_status_poller(void *ctx)
281 : : {
282 : 437 : dd_show_progress(false);
283 : 437 : return SPDK_POLLER_BUSY;
284 : : }
285 : :
286 : : static void
287 : 0 : dd_finalize_output(void)
288 : : {
289 : : off_t curr_offset;
290 : 0 : int rc = 0;
291 : :
292 [ # # ]: 0 : if (g_job.outstanding > 0) {
293 : 0 : return;
294 : : }
295 : :
296 [ # # ]: 0 : if (g_opts.output_file) {
297 : 0 : curr_offset = lseek(g_job.output.u.aio.fd, 0, SEEK_END);
298 [ # # ]: 0 : if (curr_offset == (off_t) -1) {
299 : 0 : SPDK_ERRLOG("Could not seek output file for finalize: %s\n", strerror(errno));
300 : 0 : g_error = errno;
301 [ # # ]: 0 : } else if ((uint64_t)curr_offset < g_job.copy_size + g_job.output.pos) {
302 : 0 : rc = ftruncate(g_job.output.u.aio.fd, g_job.copy_size + g_job.output.pos);
303 [ # # ]: 0 : if (rc != 0) {
304 : 0 : SPDK_ERRLOG("Could not truncate output file for finalize: %s\n", strerror(errno));
305 : 0 : g_error = errno;
306 : : }
307 : : }
308 : : }
309 : :
310 [ # # ]: 0 : if (g_error == 0) {
311 : 0 : dd_show_progress(true);
312 : 0 : printf("\n\n");
313 : : }
314 : 0 : dd_exit(g_error);
315 : : }
316 : :
317 : : #ifdef SPDK_CONFIG_URING
318 : : static void
319 : 557632 : dd_uring_submit(struct dd_io *io, struct dd_target *target, uint64_t length, uint64_t offset)
320 : : {
321 : : struct io_uring_sqe *sqe;
322 : :
323 : 557632 : sqe = io_uring_get_sqe(&g_job.u.uring.ring);
324 [ + + - + ]: 557632 : if (io->type == DD_READ || io->type == DD_POPULATE) {
325 : 295216 : io_uring_prep_read_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx);
326 : : } else {
327 : 262416 : io_uring_prep_write_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx);
328 : : }
329 : 557632 : sqe->flags |= IOSQE_FIXED_FILE;
330 : 557632 : io_uring_sqe_set_data(sqe, io);
331 : 557632 : io_uring_submit(&g_job.u.uring.ring);
332 : 557632 : }
333 : : #endif
334 : :
335 : : static void
336 : 3821572 : _dd_write_bdev_done(struct spdk_bdev_io *bdev_io,
337 : : bool success,
338 : : void *cb_arg)
339 : : {
340 : 3821572 : struct dd_io *io = cb_arg;
341 : :
342 [ - + ]: 3821572 : assert(g_job.outstanding > 0);
343 : 3821572 : g_job.outstanding--;
344 : 3821572 : spdk_bdev_free_io(bdev_io);
345 : 3821572 : dd_target_seek(io);
346 : 3821572 : }
347 : :
348 : : static void
349 : 6057462 : dd_target_write(struct dd_io *io)
350 : : {
351 : 6057462 : struct dd_target *target = &g_job.output;
352 [ - + ]: 6057462 : uint64_t length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
353 : 6057462 : uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
354 : 6057462 : uint64_t read_offset = io->offset - read_region_start;
355 : 6057462 : uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
356 : 6057462 : uint64_t write_offset = write_region_start + read_offset;
357 : 6057462 : int rc = 0;
358 : :
359 [ + - - + : 6057462 : if (g_error != 0 || g_interrupt == true) {
- + ]
360 [ # # ]: 0 : if (g_job.outstanding == 0) {
361 [ # # ]: 0 : if (g_error == 0) {
362 : 0 : dd_show_progress(true);
363 : 0 : printf("\n\n");
364 : : }
365 : 0 : dd_exit(g_error);
366 : : }
367 : 0 : return;
368 : : }
369 : :
370 : 6057462 : g_job.incremental_bytes += io->length;
371 : 6057462 : g_job.outstanding++;
372 : 6057462 : io->type = DD_WRITE;
373 : :
374 [ + + ]: 6057462 : if (target->type == DD_TARGET_TYPE_FILE) {
375 : : #ifdef SPDK_CONFIG_URING
376 [ - + + + ]: 262440 : if (g_opts.aio == false) {
377 : 262416 : dd_uring_submit(io, target, length, write_offset);
378 : : } else
379 : : #endif
380 : : {
381 : 1973474 : struct iocb *iocb = &io->iocb;
382 : :
383 : 1973474 : io_prep_pwrite(iocb, target->u.aio.fd, io->buf, length, write_offset);
384 : 1973474 : iocb->data = io;
385 [ - + ]: 1973474 : if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
386 : 0 : rc = -errno;
387 : : }
388 : : }
389 [ + - ]: 3821572 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
390 : 3821572 : rc = spdk_bdev_write(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length,
391 : : _dd_write_bdev_done, io);
392 : : }
393 : :
394 [ - + ]: 6057462 : if (rc != 0) {
395 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
396 [ # # ]: 0 : assert(g_job.outstanding > 0);
397 : 0 : g_job.outstanding--;
398 : 0 : g_error = rc;
399 [ # # ]: 0 : if (g_job.outstanding == 0) {
400 : 0 : dd_exit(rc);
401 : : }
402 : 0 : return;
403 : : }
404 : : }
405 : :
406 : : static void
407 : 4070323 : _dd_read_bdev_done(struct spdk_bdev_io *bdev_io,
408 : : bool success,
409 : : void *cb_arg)
410 : : {
411 : 4070323 : struct dd_io *io = cb_arg;
412 : :
413 : 4070323 : spdk_bdev_free_io(bdev_io);
414 : :
415 [ - + ]: 4070323 : assert(g_job.outstanding > 0);
416 : 4070323 : g_job.outstanding--;
417 : 4070323 : dd_target_write(io);
418 : 4070323 : }
419 : :
420 : : static void
421 : 6057462 : dd_target_read(struct dd_io *io)
422 : : {
423 : 6057462 : struct dd_target *target = &g_job.input;
424 : 6057462 : int rc = 0;
425 : :
426 [ + - - + : 6057462 : if (g_error != 0 || g_interrupt == true) {
- + ]
427 [ # # ]: 0 : if (g_job.outstanding == 0) {
428 : 0 : dd_exit(g_error);
429 : : }
430 : 0 : return;
431 : : }
432 : :
433 : 6057462 : g_job.outstanding++;
434 : 6057462 : io->type = DD_READ;
435 : :
436 [ + + ]: 6057462 : if (target->type == DD_TARGET_TYPE_FILE) {
437 : : #ifdef SPDK_CONFIG_URING
438 [ - + + + ]: 295240 : if (g_opts.aio == false) {
439 : 295216 : dd_uring_submit(io, target, io->length, io->offset);
440 : : } else
441 : : #endif
442 : : {
443 : 1691923 : struct iocb *iocb = &io->iocb;
444 : :
445 : 1691923 : io_prep_pread(iocb, target->u.aio.fd, io->buf, io->length, io->offset);
446 : 1691923 : iocb->data = io;
447 [ - + ]: 1691923 : if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
448 : 0 : rc = -errno;
449 : : }
450 : : }
451 [ + - ]: 4070323 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
452 : 4070323 : rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, io->offset, io->length,
453 : : _dd_read_bdev_done, io);
454 : : }
455 : :
456 [ - + ]: 6057462 : if (rc != 0) {
457 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
458 [ # # ]: 0 : assert(g_job.outstanding > 0);
459 : 0 : g_job.outstanding--;
460 : 0 : g_error = rc;
461 [ # # ]: 0 : if (g_job.outstanding == 0) {
462 : 0 : dd_exit(rc);
463 : : }
464 : 0 : return;
465 : : }
466 : : }
467 : :
468 : : static void
469 : 5 : _dd_target_populate_buffer_done(struct spdk_bdev_io *bdev_io,
470 : : bool success,
471 : : void *cb_arg)
472 : : {
473 : 5 : struct dd_io *io = cb_arg;
474 : :
475 [ - + ]: 5 : assert(g_job.outstanding > 0);
476 : 5 : g_job.outstanding--;
477 : 5 : spdk_bdev_free_io(bdev_io);
478 : 5 : dd_target_read(io);
479 : 5 : }
480 : :
481 : : static void
482 : 6058101 : dd_target_populate_buffer(struct dd_io *io)
483 : : {
484 : 6058101 : struct dd_target *target = &g_job.output;
485 : 6058101 : uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
486 : 6058101 : uint64_t read_offset = g_job.input.pos - read_region_start;
487 : 6058101 : uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
488 : 6058101 : uint64_t write_offset = write_region_start + read_offset;
489 : : uint64_t length;
490 : 6058101 : int rc = 0;
491 : :
492 : 6058101 : io->offset = g_job.input.pos;
493 : 6058101 : io->length = spdk_min(io->length, g_job.copy_size - read_offset);
494 : :
495 [ + + + - : 6058101 : if (io->length == 0 || g_error != 0 || g_interrupt == true) {
- + - + ]
496 [ + + ]: 639 : if (g_job.outstanding == 0) {
497 [ + - ]: 322 : if (g_error == 0) {
498 : 322 : dd_show_progress(true);
499 : 322 : printf("\n\n");
500 : : }
501 : 322 : dd_exit(g_error);
502 : : }
503 : 639 : return;
504 : : }
505 : :
506 : 6057462 : g_job.input.pos += io->length;
507 : :
508 [ + + + + ]: 6057462 : if ((io->length % target->block_size) == 0) {
509 : 6057457 : dd_target_read(io);
510 : 6057457 : return;
511 : : }
512 : :
513 : : /* Read whole blocks from output to combine buffers later */
514 : 5 : g_job.outstanding++;
515 : 5 : io->type = DD_POPULATE;
516 : :
517 [ - + ]: 5 : length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
518 : :
519 [ - + ]: 5 : if (target->type == DD_TARGET_TYPE_FILE) {
520 : : #ifdef SPDK_CONFIG_URING
521 [ # # # # ]: 0 : if (g_opts.aio == false) {
522 : 0 : dd_uring_submit(io, target, length, write_offset);
523 : : } else
524 : : #endif
525 : : {
526 : 0 : struct iocb *iocb = &io->iocb;
527 : :
528 : 0 : io_prep_pread(iocb, target->u.aio.fd, io->buf, length, write_offset);
529 : 0 : iocb->data = io;
530 [ # # ]: 0 : if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
531 : 0 : rc = -errno;
532 : : }
533 : : }
534 [ + - ]: 5 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
535 : 5 : rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length,
536 : : _dd_target_populate_buffer_done, io);
537 : : }
538 : :
539 [ - + ]: 5 : if (rc != 0) {
540 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
541 [ # # ]: 0 : assert(g_job.outstanding > 0);
542 : 0 : g_job.outstanding--;
543 : 0 : g_error = rc;
544 [ # # ]: 0 : if (g_job.outstanding == 0) {
545 : 0 : dd_exit(rc);
546 : : }
547 : 0 : return;
548 : : }
549 : : }
550 : :
551 : : static off_t
552 : 30 : dd_file_seek_data(void)
553 : : {
554 : 30 : off_t next_data_offset = (off_t) -1;
555 : :
556 : 30 : next_data_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_DATA);
557 : :
558 [ - + ]: 30 : if (next_data_offset == (off_t) -1) {
559 : : /* NXIO with SEEK_DATA means there are no more data to read.
560 : : * But in case of input and output files, we may have to finalize output file
561 : : * inserting a hole to the end of the file.
562 : : */
563 [ # # ]: 0 : if (errno == ENXIO) {
564 : 0 : dd_finalize_output();
565 [ # # ]: 0 : } else if (g_job.outstanding == 0) {
566 : 0 : SPDK_ERRLOG("Could not seek input file for data: %s\n", strerror(errno));
567 : 0 : g_error = errno;
568 : 0 : dd_exit(g_error);
569 : : }
570 : : }
571 : :
572 : 30 : return next_data_offset;
573 : : }
574 : :
575 : : static off_t
576 : 30 : dd_file_seek_hole(void)
577 : : {
578 : 30 : off_t next_hole_offset = (off_t) -1;
579 : :
580 : 30 : next_hole_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_HOLE);
581 : :
582 [ - + - - ]: 30 : if (next_hole_offset == (off_t) -1 && g_job.outstanding == 0) {
583 : 0 : SPDK_ERRLOG("Could not seek input file for hole: %s\n", strerror(errno));
584 : 0 : g_error = errno;
585 : 0 : dd_exit(g_error);
586 : : }
587 : :
588 : 30 : return next_hole_offset;
589 : : }
590 : :
591 : : static void
592 : 15 : _dd_bdev_seek_data_done(struct spdk_bdev_io *bdev_io,
593 : : bool success,
594 : : void *cb_arg)
595 : : {
596 : 15 : struct dd_io *io = cb_arg;
597 : 15 : uint64_t next_data_offset_blocks = UINT64_MAX;
598 : 15 : struct dd_target *target = &g_job.input;
599 : 15 : int rc = 0;
600 : :
601 [ + - - + : 15 : if (g_error != 0 || g_interrupt == true) {
- + ]
602 [ # # ]: 0 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
603 [ # # ]: 0 : if (g_job.outstanding == 0) {
604 [ # # ]: 0 : if (g_error == 0) {
605 : 0 : dd_show_progress(true);
606 [ # # ]: 0 : printf("\n\n");
607 : : }
608 : 0 : dd_exit(g_error);
609 : : }
610 : 0 : return;
611 : : }
612 : :
613 [ - + ]: 15 : assert(g_job.outstanding > 0);
614 : 15 : g_job.outstanding--;
615 : :
616 : 15 : next_data_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
617 : 15 : spdk_bdev_free_io(bdev_io);
618 : :
619 : : /* UINT64_MAX means there are no more data to read.
620 : : * But in case of input and output files, we may have to finalize output file
621 : : * inserting a hole to the end of the file.
622 : : */
623 [ - + ]: 15 : if (next_data_offset_blocks == UINT64_MAX) {
624 [ # # ]: 0 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
625 : 0 : dd_finalize_output();
626 : 0 : return;
627 : : }
628 : :
629 : 15 : g_job.input.pos = next_data_offset_blocks * g_job.input.block_size;
630 : :
631 : 15 : g_job.outstanding++;
632 : 15 : rc = spdk_bdev_seek_hole(target->u.bdev.desc, target->u.bdev.ch,
633 [ - + ]: 15 : g_job.input.pos / g_job.input.block_size,
634 : : _dd_bdev_seek_hole_done, io);
635 : :
636 [ - + ]: 15 : if (rc != 0) {
637 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
638 [ # # ]: 0 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
639 [ # # ]: 0 : assert(g_job.outstanding > 0);
640 : 0 : g_job.outstanding--;
641 : 0 : g_error = rc;
642 [ # # ]: 0 : if (g_job.outstanding == 0) {
643 : 0 : dd_exit(rc);
644 : : }
645 : : }
646 : : }
647 : :
648 : : static void
649 : 15 : _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io,
650 : : bool success,
651 : : void *cb_arg)
652 : : {
653 : 15 : struct dd_io *io = cb_arg;
654 : 15 : struct dd_target *target = &g_job.input;
655 : 15 : uint64_t next_hole_offset_blocks = UINT64_MAX;
656 : : struct dd_io *seek_io;
657 : 15 : int rc = 0;
658 : :
659 : : /* First seek operation is the one in progress, i.e. this one just ended */
660 [ + + ]: 15 : STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
661 : :
662 [ + - - + : 15 : if (g_error != 0 || g_interrupt == true) {
- + ]
663 [ # # ]: 0 : if (g_job.outstanding == 0) {
664 [ # # ]: 0 : if (g_error == 0) {
665 : 0 : dd_show_progress(true);
666 [ # # ]: 0 : printf("\n\n");
667 : : }
668 : 0 : dd_exit(g_error);
669 : : }
670 : 0 : return;
671 : : }
672 : :
673 [ - + ]: 15 : assert(g_job.outstanding > 0);
674 : 15 : g_job.outstanding--;
675 : :
676 : 15 : next_hole_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
677 : 15 : spdk_bdev_free_io(bdev_io);
678 : :
679 : : /* UINT64_MAX means there are no more holes. */
680 [ + + ]: 15 : if (next_hole_offset_blocks == UINT64_MAX) {
681 : 5 : io->length = g_opts.io_unit_size;
682 : : } else {
683 : 10 : io->length = spdk_min((uint64_t)g_opts.io_unit_size,
684 : : next_hole_offset_blocks * g_job.input.block_size - g_job.input.pos);
685 : : }
686 : :
687 : 15 : dd_target_populate_buffer(io);
688 : :
689 : : /* If input reading is not at the end, start following seek operation in the queue */
690 [ + + + + ]: 15 : if (!STAILQ_EMPTY(&g_job.seek_queue) && g_job.input.pos < g_job.input.total_size) {
691 : 5 : seek_io = STAILQ_FIRST(&g_job.seek_queue);
692 [ - + ]: 5 : assert(seek_io != NULL);
693 : 5 : g_job.outstanding++;
694 : 5 : rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
695 [ - + ]: 5 : g_job.input.pos / g_job.input.block_size,
696 : : _dd_bdev_seek_data_done, seek_io);
697 : :
698 [ - + ]: 5 : if (rc != 0) {
699 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
700 [ # # ]: 0 : assert(g_job.outstanding > 0);
701 : 0 : g_job.outstanding--;
702 : 0 : g_error = rc;
703 [ # # ]: 0 : if (g_job.outstanding == 0) {
704 : 0 : dd_exit(rc);
705 : : }
706 : : }
707 : : }
708 : : }
709 : :
710 : : static void
711 : 6058131 : dd_target_seek(struct dd_io *io)
712 : : {
713 : 6058131 : struct dd_target *target = &g_job.input;
714 : 6058131 : uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
715 : 6058131 : uint64_t read_offset = g_job.input.pos - read_region_start;
716 : 6058131 : off_t next_data_offset = (off_t) -1;
717 : 6058131 : off_t next_hole_offset = (off_t) -1;
718 : 6058131 : int rc = 0;
719 : :
720 [ + + + + ]: 6058131 : if (!g_opts.sparse) {
721 : 6058056 : dd_target_populate_buffer(io);
722 : 6058056 : return;
723 : : }
724 : :
725 [ + + + - : 75 : if (g_job.copy_size - read_offset == 0 || g_error != 0 || g_interrupt == true) {
- + - + ]
726 [ + + ]: 27 : if (g_job.outstanding == 0) {
727 [ + - ]: 15 : if (g_error == 0) {
728 : 15 : dd_show_progress(true);
729 [ - + ]: 15 : printf("\n\n");
730 : : }
731 : 15 : dd_exit(g_error);
732 : : }
733 : 27 : return;
734 : : }
735 : :
736 [ + + ]: 48 : if (target->type == DD_TARGET_TYPE_FILE) {
737 : 30 : next_data_offset = dd_file_seek_data();
738 [ - + ]: 30 : if (next_data_offset < 0) {
739 : 0 : return;
740 [ + + ]: 30 : } else if ((uint64_t)next_data_offset > g_job.input.pos) {
741 : 20 : g_job.input.pos = next_data_offset;
742 : : }
743 : :
744 : 30 : next_hole_offset = dd_file_seek_hole();
745 [ - + ]: 30 : if (next_hole_offset < 0) {
746 : 0 : return;
747 [ + - ]: 30 : } else if ((uint64_t)next_hole_offset > g_job.input.pos) {
748 : 30 : io->length = spdk_min((uint64_t)g_opts.io_unit_size,
749 : : (uint64_t)(next_hole_offset - g_job.input.pos));
750 : : } else {
751 : 0 : io->length = g_opts.io_unit_size;
752 : : }
753 : :
754 : 30 : dd_target_populate_buffer(io);
755 [ + - ]: 18 : } else if (target->type == DD_TARGET_TYPE_BDEV) {
756 : : /* Check if other seek operation is in progress */
757 [ + + ]: 18 : if (STAILQ_EMPTY(&g_job.seek_queue)) {
758 : 10 : g_job.outstanding++;
759 : 10 : rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
760 [ - + ]: 10 : g_job.input.pos / g_job.input.block_size,
761 : : _dd_bdev_seek_data_done, io);
762 : :
763 : : }
764 : :
765 : 18 : STAILQ_INSERT_TAIL(&g_job.seek_queue, io, link);
766 : : }
767 : :
768 [ - + ]: 48 : if (rc != 0) {
769 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
770 [ # # ]: 0 : assert(g_job.outstanding > 0);
771 : 0 : g_job.outstanding--;
772 : 0 : g_error = rc;
773 [ # # ]: 0 : if (g_job.outstanding == 0) {
774 : 0 : dd_exit(rc);
775 : : }
776 : 0 : return;
777 : : }
778 : : }
779 : :
780 : : static void
781 : 4223029 : dd_complete_poll(struct dd_io *io)
782 : : {
783 [ - + ]: 4223029 : assert(g_job.outstanding > 0);
784 : 4223029 : g_job.outstanding--;
785 : :
786 [ - + + - ]: 4223029 : switch (io->type) {
787 : 0 : case DD_POPULATE:
788 : 0 : dd_target_read(io);
789 : 0 : break;
790 : 1987139 : case DD_READ:
791 : 1987139 : dd_target_write(io);
792 : 1987139 : break;
793 : 2235890 : case DD_WRITE:
794 : 2235890 : dd_target_seek(io);
795 : 2235890 : break;
796 : 0 : default:
797 : 0 : assert(false);
798 : : break;
799 : : }
800 : 4223029 : }
801 : :
802 : : #ifdef SPDK_CONFIG_URING
803 : : static int
804 : 3512418 : dd_uring_poll(void *ctx)
805 : : {
806 : : struct io_uring_cqe *cqe;
807 : : struct dd_io *io;
808 : 3512418 : int rc = 0;
809 : : int i;
810 : :
811 [ + + ]: 9747081 : for (i = 0; i < (int)g_opts.queue_depth; i++) {
812 : 6234663 : rc = io_uring_peek_cqe(&g_job.u.uring.ring, &cqe);
813 [ + + ]: 6234663 : if (rc == 0) {
814 [ - + ]: 557632 : if (cqe->res == -EAGAIN) {
815 : 0 : continue;
816 [ - + ]: 557632 : } else if (cqe->res < 0) {
817 : 0 : SPDK_ERRLOG("%s\n", strerror(-cqe->res));
818 : 0 : g_error = cqe->res;
819 : : }
820 : :
821 : 557632 : io = io_uring_cqe_get_data(cqe);
822 : 557632 : io_uring_cqe_seen(&g_job.u.uring.ring, cqe);
823 : :
824 : 557632 : dd_complete_poll(io);
825 [ - + ]: 5677031 : } else if (rc != - EAGAIN) {
826 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
827 : 0 : g_error = rc;
828 : : }
829 : : }
830 : :
831 : 3512418 : return rc;
832 : : }
833 : : #endif
834 : :
835 : : static int
836 : 46228837 : dd_aio_poll(void *ctx)
837 : : {
838 : 46202105 : struct io_event events[32];
839 : 46228837 : int rc = 0;
840 : : int i;
841 : 46202105 : struct timespec timeout;
842 : : struct dd_io *io;
843 : :
844 : 46228837 : timeout.tv_sec = 0;
845 : 46228837 : timeout.tv_nsec = 0;
846 : :
847 : 46228837 : rc = io_getevents(g_job.u.aio.io_ctx, 0, 32, events, &timeout);
848 : :
849 [ - + ]: 46228837 : if (rc < 0) {
850 : 0 : SPDK_ERRLOG("%s\n", strerror(-rc));
851 : 0 : dd_exit(rc);
852 : : }
853 : :
854 [ + + ]: 49894234 : for (i = 0; i < rc; i++) {
855 : 3665397 : io = events[i].data;
856 [ - + ]: 3665397 : if (events[i].res != io->length) {
857 : 0 : g_error = -ENOSPC;
858 : : }
859 : :
860 : 3665397 : dd_complete_poll(io);
861 : : }
862 : :
863 : 46228837 : return rc;
864 : : }
865 : :
866 : : static int
867 : 533 : dd_open_file(struct dd_target *target, const char *fname, int flags, uint64_t skip_blocks,
868 : : bool input)
869 : : {
870 : : int *fd;
871 : :
872 : : #ifdef SPDK_CONFIG_URING
873 [ - + + + ]: 206 : if (g_opts.aio == false) {
874 : 146 : fd = &target->u.uring.fd;
875 : : } else
876 : : #endif
877 : : {
878 : 387 : fd = &target->u.aio.fd;
879 : : }
880 : :
881 : 533 : flags |= O_RDWR;
882 : :
883 [ + + + + ]: 533 : if (input == false && ((flags & O_DIRECTORY) == 0)) {
884 : 226 : flags |= O_CREAT;
885 : : }
886 : :
887 [ + + + + ]: 533 : if (input == false && ((flags & O_APPEND) == 0)) {
888 : 219 : flags |= O_TRUNC;
889 : : }
890 : :
891 : 533 : target->type = DD_TARGET_TYPE_FILE;
892 [ - + ]: 533 : *fd = open(fname, flags, 0600);
893 [ + + ]: 533 : if (*fd < 0) {
894 : 40 : SPDK_ERRLOG("Could not open file %s: %s\n", fname, strerror(errno));
895 : 40 : return *fd;
896 : : }
897 : :
898 [ + + ]: 493 : target->block_size = spdk_max(spdk_fd_get_blocklen(*fd), 1);
899 : :
900 : 493 : target->total_size = spdk_fd_get_size(*fd);
901 [ + + ]: 493 : if (target->total_size == 0) {
902 : 275 : target->total_size = g_opts.io_unit_size * g_opts.io_unit_count;
903 : : }
904 : :
905 [ + + ]: 493 : if (input == true) {
906 [ - + + + : 277 : g_opts.queue_depth = spdk_min(g_opts.queue_depth,
- + ]
907 : : (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
908 : : }
909 : :
910 [ + + ]: 493 : if (g_opts.io_unit_count != 0) {
911 : 137 : g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
912 : : }
913 : :
914 : 493 : return 0;
915 : : }
916 : :
917 : : static void
918 : 0 : dd_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
919 : : void *event_ctx)
920 : : {
921 : 0 : SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
922 : 0 : }
923 : :
924 : : static int
925 : 227 : dd_open_bdev(struct dd_target *target, const char *bdev_name, uint64_t skip_blocks)
926 : : {
927 : : int rc;
928 : :
929 : 227 : target->type = DD_TARGET_TYPE_BDEV;
930 : :
931 : 227 : rc = spdk_bdev_open_ext(bdev_name, true, dd_bdev_event_cb, NULL, &target->u.bdev.desc);
932 [ + + ]: 227 : if (rc < 0) {
933 : 2 : SPDK_ERRLOG("Could not open bdev %s: %s\n", bdev_name, strerror(-rc));
934 : 2 : return rc;
935 : : }
936 : :
937 : 225 : target->u.bdev.bdev = spdk_bdev_desc_get_bdev(target->u.bdev.desc);
938 : 225 : target->open = true;
939 : :
940 : 225 : target->u.bdev.ch = spdk_bdev_get_io_channel(target->u.bdev.desc);
941 [ - + ]: 225 : if (target->u.bdev.ch == NULL) {
942 : 0 : spdk_bdev_close(target->u.bdev.desc);
943 : 0 : SPDK_ERRLOG("Could not get I/O channel: %s\n", strerror(ENOMEM));
944 : 0 : return -ENOMEM;
945 : : }
946 : :
947 : 225 : target->block_size = spdk_bdev_get_block_size(target->u.bdev.bdev);
948 : 225 : target->total_size = spdk_bdev_get_num_blocks(target->u.bdev.bdev) * target->block_size;
949 : :
950 [ - + + - : 225 : g_opts.queue_depth = spdk_min(g_opts.queue_depth,
- - ]
951 : : (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
952 : :
953 [ + + ]: 225 : if (g_opts.io_unit_count != 0) {
954 : 131 : g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
955 : : }
956 : :
957 : 225 : return 0;
958 : : }
959 : :
960 : : static void
961 : 0 : dd_finish(void)
962 : : {
963 : : /* Interrupt operation */
964 : 0 : g_interrupt = true;
965 : 0 : }
966 : :
967 : : static int
968 : 421 : parse_flags(char *file_flags)
969 : : {
970 : : char *input_flag;
971 : 421 : int flags = 0;
972 : : int i;
973 : 421 : bool found = false;
974 : :
975 : : /* Translate input flags to file open flags */
976 [ + + ]: 689 : while ((input_flag = strsep(&file_flags, ","))) {
977 [ + + ]: 1387 : for (i = 0; g_flags[i].name != NULL; i++) {
978 [ + + - + : 1380 : if (!strcmp(input_flag, g_flags[i].name)) {
+ + ]
979 : 268 : flags |= g_flags[i].flag;
980 : 268 : found = true;
981 : 268 : break;
982 : : }
983 : : }
984 : :
985 [ + + ]: 275 : if (found == false) {
986 : 7 : SPDK_ERRLOG("Unknown file flag: %s\n", input_flag);
987 : 7 : dd_exit(-EINVAL);
988 : 7 : return 0;
989 : : }
990 : :
991 : 268 : found = false;
992 : : }
993 : :
994 : 414 : return flags;
995 : : }
996 : :
997 : : #ifdef SPDK_CONFIG_URING
998 : : static bool
999 : 2 : dd_is_blk(int fd)
1000 : : {
1001 : : struct stat st;
1002 : :
1003 [ - + - + ]: 2 : if (fstat(fd, &st) != 0) {
1004 : 0 : return false;
1005 : : }
1006 : :
1007 : 2 : return S_ISBLK(st.st_mode);
1008 : : }
1009 : :
1010 : : struct dd_uring_init_ctx {
1011 : : unsigned int io_uring_flags;
1012 : : int rc;
1013 : : };
1014 : :
1015 : : static void *
1016 : 94 : dd_uring_init(void *arg)
1017 : : {
1018 : 94 : struct dd_uring_init_ctx *ctx = arg;
1019 : :
1020 : 94 : ctx->rc = io_uring_queue_init(g_opts.queue_depth * 2, &g_job.u.uring.ring, ctx->io_uring_flags);
1021 : 94 : return ctx;
1022 : : }
1023 : :
1024 : : static int
1025 : 94 : dd_register_files(void)
1026 : : {
1027 : : int fds[2];
1028 : 94 : unsigned count = 0;
1029 : :
1030 [ + + ]: 94 : if (g_opts.input_file) {
1031 : 72 : fds[count] = g_job.input.u.uring.fd;
1032 : 72 : g_job.input.u.uring.idx = count;
1033 : 72 : count++;
1034 : : }
1035 : :
1036 [ + + ]: 94 : if (g_opts.output_file) {
1037 : 56 : fds[count] = g_job.output.u.uring.fd;
1038 : 56 : g_job.output.u.uring.idx = count;
1039 : 56 : count++;
1040 : : }
1041 : :
1042 : 94 : return io_uring_register_files(&g_job.u.uring.ring, fds, count);
1043 : :
1044 : : }
1045 : :
1046 : : static int
1047 : 92 : dd_register_buffers(void)
1048 : : {
1049 : : struct iovec *iovs;
1050 : : int i, rc;
1051 : :
1052 : 92 : iovs = calloc(g_opts.queue_depth, sizeof(struct iovec));
1053 [ - + ]: 92 : if (iovs == NULL) {
1054 : 0 : return -ENOMEM;
1055 : : }
1056 : :
1057 [ + + ]: 298 : for (i = 0; i < (int)g_opts.queue_depth; i++) {
1058 : 206 : iovs[i].iov_base = g_job.ios[i].buf;
1059 : 206 : iovs[i].iov_len = g_opts.io_unit_size;
1060 : 206 : g_job.ios[i].idx = i;
1061 : : }
1062 : :
1063 : 92 : rc = io_uring_register_buffers(&g_job.u.uring.ring, iovs, g_opts.queue_depth);
1064 : :
1065 : 92 : free(iovs);
1066 : 92 : return rc;
1067 : : }
1068 : : #endif
1069 : :
1070 : : static void
1071 : 391 : dd_run(void *arg1)
1072 : : {
1073 : : uint64_t write_size;
1074 : : uint32_t i;
1075 : 391 : int rc, flags = 0;
1076 : :
1077 [ + + ]: 391 : if (g_opts.input_file) {
1078 [ + + ]: 297 : if (g_opts.input_file_flags) {
1079 : 110 : flags = parse_flags(g_opts.input_file_flags);
1080 : : }
1081 : :
1082 [ + + ]: 297 : if (dd_open_file(&g_job.input, g_opts.input_file, flags, g_opts.input_offset, true) < 0) {
1083 : 20 : SPDK_ERRLOG("%s: %s\n", g_opts.input_file, strerror(errno));
1084 : 20 : dd_exit(-errno);
1085 : 20 : return;
1086 : : }
1087 [ + - ]: 94 : } else if (g_opts.input_bdev) {
1088 : 94 : rc = dd_open_bdev(&g_job.input, g_opts.input_bdev, g_opts.input_offset);
1089 [ + + ]: 94 : if (rc < 0) {
1090 : 2 : SPDK_ERRLOG("%s: %s\n", g_opts.input_bdev, strerror(-rc));
1091 : 2 : dd_exit(rc);
1092 : 2 : return;
1093 : : }
1094 : : }
1095 : :
1096 : 369 : write_size = g_opts.io_unit_count * g_opts.io_unit_size;
1097 : 369 : g_job.input.pos = g_opts.input_offset * g_opts.io_unit_size;
1098 : :
1099 : : /* We cannot check write size for input files because /dev/zeros, /dev/random, etc would not work.
1100 : : * We will handle that during copying */
1101 [ + + - + ]: 369 : if (g_opts.input_bdev && g_job.input.pos > g_job.input.total_size) {
1102 [ # # ]: 0 : SPDK_ERRLOG("--skip value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in input\n",
1103 : : g_opts.input_offset, g_job.input.total_size / g_opts.io_unit_size);
1104 : 0 : dd_exit(-ENOSPC);
1105 : 0 : return;
1106 : : }
1107 : :
1108 [ + + + + ]: 369 : if (g_opts.io_unit_count != 0 && g_opts.input_bdev &&
1109 [ - + ]: 69 : write_size + g_job.input.pos > g_job.input.total_size) {
1110 [ # # ]: 0 : SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available from input\n",
1111 : : g_opts.io_unit_count, (g_job.input.total_size - g_job.input.pos) / g_opts.io_unit_size);
1112 : 0 : dd_exit(-ENOSPC);
1113 : 0 : return;
1114 : : }
1115 : :
1116 [ + + ]: 369 : if (g_opts.io_unit_count != 0) {
1117 : 134 : g_job.copy_size = write_size;
1118 : : } else {
1119 : 235 : g_job.copy_size = g_job.input.total_size - g_job.input.pos;
1120 : : }
1121 : :
1122 : 369 : g_job.output.pos = g_opts.output_offset * g_opts.io_unit_size;
1123 : :
1124 [ + + ]: 369 : if (g_opts.output_file) {
1125 : 236 : flags = 0;
1126 : :
1127 [ + + ]: 236 : if (g_opts.output_file_flags) {
1128 : 123 : flags = parse_flags(g_opts.output_file_flags);
1129 : : }
1130 : :
1131 [ + + ]: 236 : if (dd_open_file(&g_job.output, g_opts.output_file, flags, g_opts.output_offset, false) < 0) {
1132 : 20 : SPDK_ERRLOG("%s: %s\n", g_opts.output_file, strerror(errno));
1133 : 20 : dd_exit(-errno);
1134 : 20 : return;
1135 : : }
1136 [ + - ]: 133 : } else if (g_opts.output_bdev) {
1137 : 133 : rc = dd_open_bdev(&g_job.output, g_opts.output_bdev, g_opts.output_offset);
1138 [ - + ]: 133 : if (rc < 0) {
1139 : 0 : SPDK_ERRLOG("%s: %s\n", g_opts.output_bdev, strerror(-rc));
1140 : 0 : dd_exit(rc);
1141 : 0 : return;
1142 : : }
1143 : :
1144 [ - + ]: 133 : if (g_job.output.pos > g_job.output.total_size) {
1145 [ # # ]: 0 : SPDK_ERRLOG("--seek value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
1146 : : g_opts.output_offset, g_job.output.total_size / g_opts.io_unit_size);
1147 : 0 : dd_exit(-ENOSPC);
1148 : 0 : return;
1149 : : }
1150 : :
1151 [ + + - + ]: 133 : if (g_opts.io_unit_count != 0 && write_size + g_job.output.pos > g_job.output.total_size) {
1152 [ # # ]: 0 : SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
1153 : : g_opts.io_unit_count, (g_job.output.total_size - g_job.output.pos) / g_opts.io_unit_size);
1154 : 0 : dd_exit(-ENOSPC);
1155 : 0 : return;
1156 : : }
1157 : : }
1158 : :
1159 [ + + ]: 349 : if ((g_job.output.block_size > g_opts.io_unit_size) ||
1160 [ - + ]: 344 : (g_job.input.block_size > g_opts.io_unit_size)) {
1161 : 5 : SPDK_ERRLOG("--bs value cannot be less than input (%d) neither output (%d) native block size\n",
1162 : : g_job.input.block_size, g_job.output.block_size);
1163 : 5 : dd_exit(-EINVAL);
1164 : 5 : return;
1165 : : }
1166 : :
1167 [ + + - + : 344 : if (g_opts.input_bdev && g_opts.io_unit_size % g_job.input.block_size != 0) {
- + ]
1168 : 0 : SPDK_ERRLOG("--bs value must be a multiple of input native block size (%d)\n",
1169 : : g_job.input.block_size);
1170 : 0 : dd_exit(-EINVAL);
1171 : 0 : return;
1172 : : }
1173 : :
1174 : 344 : g_job.ios = calloc(g_opts.queue_depth, sizeof(struct dd_io));
1175 [ - + ]: 344 : if (g_job.ios == NULL) {
1176 : 0 : SPDK_ERRLOG("%s\n", strerror(ENOMEM));
1177 : 0 : dd_exit(-ENOMEM);
1178 : 0 : return;
1179 : : }
1180 : :
1181 [ + + ]: 1015 : for (i = 0; i < g_opts.queue_depth; i++) {
1182 : 676 : g_job.ios[i].buf = spdk_malloc(g_opts.io_unit_size, 0x1000, NULL, 0, SPDK_MALLOC_DMA);
1183 [ + + ]: 676 : if (g_job.ios[i].buf == NULL) {
1184 : 5 : SPDK_ERRLOG("%s - try smaller block size value\n", strerror(ENOMEM));
1185 : 5 : dd_exit(-ENOMEM);
1186 : 5 : return;
1187 : : }
1188 : 671 : g_job.ios[i].length = (uint64_t)g_opts.io_unit_size;
1189 : : }
1190 : :
1191 [ + + + + ]: 339 : if (g_opts.input_file || g_opts.output_file) {
1192 : : #ifdef SPDK_CONFIG_URING
1193 [ - + + + ]: 118 : if (g_opts.aio == false) {
1194 : : struct dd_uring_init_ctx ctx;
1195 : 94 : int flags = parse_flags(g_opts.input_file_flags) & parse_flags(g_opts.output_file_flags);
1196 : :
1197 : 94 : ctx.io_uring_flags = IORING_SETUP_SQPOLL;
1198 [ + + - + ]: 96 : if ((flags & O_DIRECT) != 0 &&
1199 [ - - ]: 2 : dd_is_blk(g_job.input.u.uring.fd) &&
1200 : 0 : dd_is_blk(g_job.output.u.uring.fd)) {
1201 : 0 : ctx.io_uring_flags = IORING_SETUP_IOPOLL;
1202 : : }
1203 : :
1204 : 94 : g_job.u.uring.poller = SPDK_POLLER_REGISTER(dd_uring_poll, NULL, 0);
1205 : :
1206 : : /* Initialized uring kernel threads inherit parent process CPU mask, to avoid conflicting
1207 : : * with SPDK cores initialize uring without any affinity. */
1208 [ + - - + ]: 94 : if (spdk_call_unaffinitized(dd_uring_init, &ctx) == NULL || ctx.rc) {
1209 : 0 : SPDK_ERRLOG("Failed to create io_uring: %d (%s)\n", ctx.rc, spdk_strerror(-ctx.rc));
1210 : 0 : dd_exit(ctx.rc);
1211 : 2 : return;
1212 : : }
1213 : 94 : g_job.u.uring.active = true;
1214 : :
1215 : : /* Register the files */
1216 : 94 : rc = dd_register_files();
1217 [ + + ]: 94 : if (rc) {
1218 : 2 : SPDK_ERRLOG("Failed to register files with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1219 : 2 : dd_exit(rc);
1220 : 2 : return;
1221 : : }
1222 : :
1223 : : /* Register the buffers */
1224 : 92 : rc = dd_register_buffers();
1225 [ - + ]: 92 : if (rc) {
1226 : 0 : SPDK_ERRLOG("Failed to register buffers with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1227 : 0 : dd_exit(rc);
1228 : 0 : return;
1229 : : }
1230 : :
1231 : : } else
1232 : : #endif
1233 : : {
1234 : 219 : g_job.u.aio.poller = SPDK_POLLER_REGISTER(dd_aio_poll, NULL, 0);
1235 : 219 : io_setup(g_opts.queue_depth, &g_job.u.aio.io_ctx);
1236 : : }
1237 : : }
1238 : :
1239 : 337 : clock_gettime(CLOCK_REALTIME, &g_job.start_time);
1240 : :
1241 : 337 : g_job.status_poller = SPDK_POLLER_REGISTER(dd_status_poller, NULL,
1242 : : STATUS_POLLER_PERIOD_SEC * SPDK_SEC_TO_USEC);
1243 : :
1244 : 337 : STAILQ_INIT(&g_job.seek_queue);
1245 : :
1246 [ + + ]: 1006 : for (i = 0; i < g_opts.queue_depth; i++) {
1247 : 669 : dd_target_seek(&g_job.ios[i]);
1248 : : }
1249 : :
1250 : : }
1251 : :
1252 : : enum dd_cmdline_opts {
1253 : : DD_OPTION_IF = 0x1000,
1254 : : DD_OPTION_OF,
1255 : : DD_OPTION_IFLAGS,
1256 : : DD_OPTION_OFLAGS,
1257 : : DD_OPTION_IB,
1258 : : DD_OPTION_OB,
1259 : : DD_OPTION_SKIP,
1260 : : DD_OPTION_SEEK,
1261 : : DD_OPTION_BS,
1262 : : DD_OPTION_QD,
1263 : : DD_OPTION_COUNT,
1264 : : DD_OPTION_AIO,
1265 : : DD_OPTION_SPARSE,
1266 : : };
1267 : :
1268 : : static struct option g_cmdline_opts[] = {
1269 : : {
1270 : : .name = "if",
1271 : : .has_arg = 1,
1272 : : .flag = NULL,
1273 : : .val = DD_OPTION_IF,
1274 : : },
1275 : : {
1276 : : .name = "of",
1277 : : .has_arg = 1,
1278 : : .flag = NULL,
1279 : : .val = DD_OPTION_OF,
1280 : : },
1281 : : {
1282 : : .name = "iflag",
1283 : : .has_arg = 1,
1284 : : .flag = NULL,
1285 : : .val = DD_OPTION_IFLAGS,
1286 : : },
1287 : : {
1288 : : .name = "oflag",
1289 : : .has_arg = 1,
1290 : : .flag = NULL,
1291 : : .val = DD_OPTION_OFLAGS,
1292 : : },
1293 : : {
1294 : : .name = "ib",
1295 : : .has_arg = 1,
1296 : : .flag = NULL,
1297 : : .val = DD_OPTION_IB,
1298 : : },
1299 : : {
1300 : : .name = "ob",
1301 : : .has_arg = 1,
1302 : : .flag = NULL,
1303 : : .val = DD_OPTION_OB,
1304 : : },
1305 : : {
1306 : : .name = "skip",
1307 : : .has_arg = 1,
1308 : : .flag = NULL,
1309 : : .val = DD_OPTION_SKIP,
1310 : : },
1311 : : {
1312 : : .name = "seek",
1313 : : .has_arg = 1,
1314 : : .flag = NULL,
1315 : : .val = DD_OPTION_SEEK,
1316 : : },
1317 : : {
1318 : : .name = "bs",
1319 : : .has_arg = 1,
1320 : : .flag = NULL,
1321 : : .val = DD_OPTION_BS,
1322 : : },
1323 : : {
1324 : : .name = "qd",
1325 : : .has_arg = 1,
1326 : : .flag = NULL,
1327 : : .val = DD_OPTION_QD,
1328 : : },
1329 : : {
1330 : : .name = "count",
1331 : : .has_arg = 1,
1332 : : .flag = NULL,
1333 : : .val = DD_OPTION_COUNT,
1334 : : },
1335 : : {
1336 : : .name = "aio",
1337 : : .has_arg = 0,
1338 : : .flag = NULL,
1339 : : .val = DD_OPTION_AIO,
1340 : : },
1341 : : {
1342 : : .name = "sparse",
1343 : : .has_arg = 0,
1344 : : .flag = NULL,
1345 : : .val = DD_OPTION_SPARSE,
1346 : : },
1347 : : {
1348 : : .name = NULL
1349 : : }
1350 : : };
1351 : :
1352 : : static void
1353 : 5 : usage(void)
1354 : : {
1355 [ - + ]: 5 : printf("[--------- DD Options ---------]\n");
1356 [ - + ]: 5 : printf(" --if Input file. Must specify either --if or --ib.\n");
1357 [ - + ]: 5 : printf(" --ib Input bdev. Must specifier either --if or --ib\n");
1358 [ - + ]: 5 : printf(" --of Output file. Must specify either --of or --ob.\n");
1359 [ - + ]: 5 : printf(" --ob Output bdev. Must specify either --of or --ob.\n");
1360 [ - + ]: 5 : printf(" --iflag Input file flags.\n");
1361 [ - + ]: 5 : printf(" --oflag Output file flags.\n");
1362 [ - + ]: 5 : printf(" --bs I/O unit size (default: %" PRId64 ")\n", g_opts.io_unit_size);
1363 [ - + ]: 5 : printf(" --qd Queue depth (default: %d)\n", g_opts.queue_depth);
1364 [ - + ]: 5 : printf(" --count I/O unit count. The number of I/O units to copy. (default: all)\n");
1365 [ - + ]: 5 : printf(" --skip Skip this many I/O units at start of input. (default: 0)\n");
1366 [ - + ]: 5 : printf(" --seek Skip this many I/O units at start of output. (default: 0)\n");
1367 [ - + ]: 5 : printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n");
1368 [ - + ]: 5 : printf(" --sparse Enable hole skipping in input target\n");
1369 [ - + ]: 5 : printf(" Available iflag and oflag values:\n");
1370 [ - + ]: 5 : printf(" append - append mode\n");
1371 [ - + ]: 5 : printf(" direct - use direct I/O for data\n");
1372 [ - + ]: 5 : printf(" directory - fail unless a directory\n");
1373 [ - + ]: 5 : printf(" dsync - use synchronized I/O for data\n");
1374 [ - + ]: 5 : printf(" noatime - do not update access time\n");
1375 [ - + ]: 5 : printf(" noctty - do not assign controlling terminal from file\n");
1376 [ - + ]: 5 : printf(" nofollow - do not follow symlinks\n");
1377 [ - + ]: 5 : printf(" nonblock - use non-blocking I/O\n");
1378 [ - + ]: 5 : printf(" sync - use synchronized I/O for data and metadata\n");
1379 : 5 : }
1380 : :
1381 : : static int
1382 : 1640 : parse_args(int argc, char *argv)
1383 : : {
1384 [ + + + + : 1640 : switch (argc) {
+ + + + +
+ + + +
- ]
1385 : 327 : case DD_OPTION_IF:
1386 [ - + ]: 327 : g_opts.input_file = strdup(argv);
1387 : 327 : break;
1388 : 278 : case DD_OPTION_OF:
1389 [ - + ]: 278 : g_opts.output_file = strdup(argv);
1390 : 278 : break;
1391 : 115 : case DD_OPTION_IFLAGS:
1392 [ - + ]: 115 : g_opts.input_file_flags = strdup(argv);
1393 : 115 : break;
1394 : 128 : case DD_OPTION_OFLAGS:
1395 [ - + ]: 128 : g_opts.output_file_flags = strdup(argv);
1396 : 128 : break;
1397 : 109 : case DD_OPTION_IB:
1398 [ - + ]: 109 : g_opts.input_bdev = strdup(argv);
1399 : 109 : break;
1400 : 158 : case DD_OPTION_OB:
1401 [ - + ]: 158 : g_opts.output_bdev = strdup(argv);
1402 : 158 : break;
1403 : 23 : case DD_OPTION_SKIP:
1404 : 23 : g_opts.input_offset = spdk_strtol(optarg, 10);
1405 : 23 : break;
1406 : 19 : case DD_OPTION_SEEK:
1407 : 19 : g_opts.output_offset = spdk_strtol(optarg, 10);
1408 : 19 : break;
1409 : 181 : case DD_OPTION_BS:
1410 : 181 : g_opts.io_unit_size = spdk_strtol(optarg, 10);
1411 : 181 : break;
1412 : 68 : case DD_OPTION_QD:
1413 : 68 : g_opts.queue_depth = spdk_strtol(optarg, 10);
1414 : 68 : break;
1415 : 139 : case DD_OPTION_COUNT:
1416 : 139 : g_opts.io_unit_count = spdk_strtol(optarg, 10);
1417 : 139 : break;
1418 : 80 : case DD_OPTION_AIO:
1419 : 80 : g_opts.aio = true;
1420 : 80 : break;
1421 : 15 : case DD_OPTION_SPARSE:
1422 : 15 : g_opts.sparse = true;
1423 : 15 : break;
1424 : 0 : default:
1425 : 0 : usage();
1426 : 0 : return 1;
1427 : : }
1428 : 1640 : return 0;
1429 : : }
1430 : :
1431 : : static void
1432 : 396 : dd_free(void)
1433 : : {
1434 : : uint32_t i;
1435 : :
1436 : 396 : free(g_opts.input_file);
1437 : 396 : free(g_opts.output_file);
1438 : 396 : free(g_opts.input_bdev);
1439 : 396 : free(g_opts.output_bdev);
1440 : 396 : free(g_opts.input_file_flags);
1441 : 396 : free(g_opts.output_file_flags);
1442 : :
1443 : :
1444 [ + + + + ]: 396 : if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
1445 : : #ifdef SPDK_CONFIG_URING
1446 [ - + + + ]: 142 : if (g_opts.aio == false) {
1447 [ - + + + ]: 110 : if (g_job.u.uring.active) {
1448 : 94 : io_uring_unregister_files(&g_job.u.uring.ring);
1449 : 94 : io_uring_queue_exit(&g_job.u.uring.ring);
1450 : : }
1451 : : } else
1452 : : #endif
1453 : : {
1454 : 260 : io_destroy(g_job.u.aio.io_ctx);
1455 : : }
1456 : : }
1457 : :
1458 [ + + ]: 396 : if (g_job.ios) {
1459 [ + + ]: 1020 : for (i = 0; i < g_opts.queue_depth; i++) {
1460 : 676 : spdk_free(g_job.ios[i].buf);
1461 : : }
1462 : :
1463 : 344 : free(g_job.ios);
1464 : : }
1465 : 396 : }
1466 : :
1467 : : int
1468 : 441 : main(int argc, char **argv)
1469 : : {
1470 : 441 : struct spdk_app_opts opts = {};
1471 : 441 : int rc = 1;
1472 : :
1473 : 441 : spdk_app_opts_init(&opts, sizeof(opts));
1474 : 441 : opts.name = "spdk_dd";
1475 : 441 : opts.reactor_mask = "0x1";
1476 : 441 : opts.shutdown_cb = dd_finish;
1477 : 441 : opts.rpc_addr = NULL;
1478 : 441 : rc = spdk_app_parse_args(argc, argv, &opts, "", g_cmdline_opts, parse_args, usage);
1479 [ + + ]: 441 : if (rc == SPDK_APP_PARSE_ARGS_FAIL) {
1480 : 5 : SPDK_ERRLOG("Invalid arguments\n");
1481 : 5 : goto end;
1482 [ - + ]: 436 : } else if (rc == SPDK_APP_PARSE_ARGS_HELP) {
1483 : 0 : goto end;
1484 : : }
1485 : :
1486 [ + + + + ]: 436 : if (g_opts.input_file != NULL && g_opts.input_bdev != NULL) {
1487 : 5 : SPDK_ERRLOG("You may specify either --if or --ib, but not both.\n");
1488 : 5 : rc = EINVAL;
1489 : 5 : goto end;
1490 : : }
1491 : :
1492 [ + + + + ]: 431 : if (g_opts.output_file != NULL && g_opts.output_bdev != NULL) {
1493 : 5 : SPDK_ERRLOG("You may specify either --of or --ob, but not both.\n");
1494 : 5 : rc = EINVAL;
1495 : 5 : goto end;
1496 : : }
1497 : :
1498 [ + + + + ]: 426 : if (g_opts.input_file == NULL && g_opts.input_bdev == NULL) {
1499 : 5 : SPDK_ERRLOG("You must specify either --if or --ib\n");
1500 : 5 : rc = EINVAL;
1501 : 5 : goto end;
1502 : : }
1503 : :
1504 [ + + + + ]: 421 : if (g_opts.output_file == NULL && g_opts.output_bdev == NULL) {
1505 : 5 : SPDK_ERRLOG("You must specify either --of or --ob\n");
1506 : 5 : rc = EINVAL;
1507 : 5 : goto end;
1508 : : }
1509 : :
1510 [ + + ]: 416 : if (g_opts.io_unit_size <= 0) {
1511 : 5 : SPDK_ERRLOG("Invalid --bs value\n");
1512 : 5 : rc = EINVAL;
1513 : 5 : goto end;
1514 : : }
1515 : :
1516 [ + + ]: 411 : if (g_opts.io_unit_count < 0) {
1517 : 5 : SPDK_ERRLOG("Invalid --count value\n");
1518 : 5 : rc = EINVAL;
1519 : 5 : goto end;
1520 : : }
1521 : :
1522 [ + + + + ]: 406 : if (g_opts.output_file == NULL && g_opts.output_file_flags != NULL) {
1523 : 5 : SPDK_ERRLOG("--oflags may be used only with --of\n");
1524 : 5 : rc = EINVAL;
1525 : 5 : goto end;
1526 : : }
1527 : :
1528 [ + + + + ]: 401 : if (g_opts.input_file == NULL && g_opts.input_file_flags != NULL) {
1529 : 5 : SPDK_ERRLOG("--iflags may be used only with --if\n");
1530 : 5 : rc = EINVAL;
1531 : 5 : goto end;
1532 : : }
1533 : :
1534 : 396 : rc = spdk_app_start(&opts, dd_run, NULL);
1535 [ + + ]: 396 : if (rc) {
1536 : 62 : SPDK_ERRLOG("Error occurred while performing copy\n");
1537 : : }
1538 : :
1539 : 396 : dd_free();
1540 : 396 : spdk_app_fini();
1541 : :
1542 : 441 : end:
1543 : 441 : return rc;
1544 : : }
|