xref: /spdk/app/spdk_dd/spdk_dd.c (revision 18c8b52afa69f39481ebb75711b2f30b11693f9d)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/stdinc.h"
7 
8 #include "spdk/bdev.h"
9 #include "spdk/event.h"
10 #include "spdk/fd.h"
11 #include "spdk/string.h"
12 #include "spdk/util.h"
13 #include "spdk/vmd.h"
14 
15 #include <libaio.h>
16 
17 #ifdef SPDK_CONFIG_URING
18 #include <liburing.h>
19 #endif
20 
21 #define DD_NSEC_SINCE_X(time_now, time_x) ((1000000000 * time_now.tv_sec + time_now.tv_nsec) \
22 											- (1000000000 * time_x.tv_sec + time_x.tv_nsec))
23 
24 struct spdk_dd_opts {
25 	char		*input_file;
26 	char		*output_file;
27 	char		*input_file_flags;
28 	char		*output_file_flags;
29 	char		*input_bdev;
30 	char		*output_bdev;
31 	uint64_t	input_offset;
32 	uint64_t	output_offset;
33 	int64_t		io_unit_size;
34 	int64_t		io_unit_count;
35 	uint32_t	queue_depth;
36 	bool		aio;
37 };
38 
39 static struct spdk_dd_opts g_opts = {
40 	.io_unit_size = 4096,
41 	.queue_depth = 2,
42 };
43 
44 enum dd_submit_type {
45 	DD_POPULATE,
46 	DD_READ,
47 	DD_WRITE,
48 };
49 
50 struct dd_io {
51 	uint64_t		offset;
52 	uint64_t		length;
53 	struct iocb		iocb;
54 	enum dd_submit_type	type;
55 #ifdef SPDK_CONFIG_URING
56 	struct iovec		iov;
57 #endif
58 	void			*buf;
59 };
60 
61 enum dd_target_type {
62 	DD_TARGET_TYPE_FILE,
63 	DD_TARGET_TYPE_BDEV,
64 };
65 
66 struct dd_target {
67 	enum dd_target_type	type;
68 
69 	union {
70 		struct {
71 			struct spdk_bdev *bdev;
72 			struct spdk_bdev_desc *desc;
73 			struct spdk_io_channel *ch;
74 		} bdev;
75 
76 #ifdef SPDK_CONFIG_URING
77 		struct {
78 			int fd;
79 		} uring;
80 #endif
81 		struct {
82 			int fd;
83 		} aio;
84 	} u;
85 
86 	/* Block size of underlying device. */
87 	uint32_t	block_size;
88 
89 	/* Position of next I/O in bytes */
90 	uint64_t	pos;
91 
92 	/* Total size of target in bytes */
93 	uint64_t	total_size;
94 
95 	bool open;
96 };
97 
98 struct dd_job {
99 	struct dd_target	input;
100 	struct dd_target	output;
101 
102 	struct dd_io		*ios;
103 
104 	union {
105 #ifdef SPDK_CONFIG_URING
106 		struct {
107 			struct io_uring ring;
108 			bool active;
109 			struct spdk_poller *poller;
110 		} uring;
111 #endif
112 		struct {
113 			io_context_t io_ctx;
114 			struct spdk_poller *poller;
115 		} aio;
116 	} u;
117 
118 	uint32_t		outstanding;
119 	uint64_t		copy_size;
120 };
121 
122 struct dd_flags {
123 	char *name;
124 	int flag;
125 };
126 
127 static struct dd_flags g_flags[] = {
128 	{"append", O_APPEND},
129 	{"direct", O_DIRECT},
130 	{"directory", O_DIRECTORY},
131 	{"dsync", O_DSYNC},
132 	{"noatime", O_NOATIME},
133 	{"noctty", O_NOCTTY},
134 	{"nofollow", O_NOFOLLOW},
135 	{"nonblock", O_NONBLOCK},
136 	{"sync", O_SYNC},
137 	{NULL, 0}
138 };
139 
140 static struct dd_job g_job = {};
141 static int g_error = 0;
142 static struct timespec g_start_time;
143 static bool g_interrupt;
144 
145 static void dd_target_populate_buffer(struct dd_io *io);
146 
147 static void
148 dd_cleanup_bdev(struct dd_target io)
149 {
150 	/* This can be only on the error path.
151 	 * To prevent the SEGV, need add checks here.
152 	 */
153 	if (io.u.bdev.ch) {
154 		spdk_put_io_channel(io.u.bdev.ch);
155 	}
156 
157 	spdk_bdev_close(io.u.bdev.desc);
158 }
159 
160 static void
161 dd_exit(int rc)
162 {
163 	if (g_job.input.type == DD_TARGET_TYPE_FILE) {
164 #ifdef SPDK_CONFIG_URING
165 		if (g_opts.aio == false) {
166 			close(g_job.input.u.uring.fd);
167 		} else
168 #endif
169 		{
170 			close(g_job.input.u.aio.fd);
171 		}
172 	} else if (g_job.input.type == DD_TARGET_TYPE_BDEV && g_job.input.open) {
173 		dd_cleanup_bdev(g_job.input);
174 	}
175 
176 	if (g_job.output.type == DD_TARGET_TYPE_FILE) {
177 #ifdef SPDK_CONFIG_URING
178 		if (g_opts.aio == false) {
179 			close(g_job.output.u.uring.fd);
180 		} else
181 #endif
182 		{
183 			close(g_job.output.u.aio.fd);
184 		}
185 	} else if (g_job.output.type == DD_TARGET_TYPE_BDEV && g_job.output.open) {
186 		dd_cleanup_bdev(g_job.output);
187 	}
188 
189 	if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
190 #ifdef SPDK_CONFIG_URING
191 		if (g_opts.aio == false) {
192 			spdk_poller_unregister(&g_job.u.uring.poller);
193 		} else
194 #endif
195 		{
196 			spdk_poller_unregister(&g_job.u.aio.poller);
197 		}
198 	}
199 
200 	spdk_app_stop(rc);
201 }
202 
203 static void
204 dd_show_progress(uint64_t offset, uint64_t length, bool finish)
205 {
206 	char *unit_str[5] = {"", "k", "M", "G", "T"};
207 	char *speed_type_str[2] = {"", "average "};
208 	char *size_unit_str = "";
209 	char *speed_unit_str = "";
210 	char *speed_type = "";
211 	uint64_t size = g_job.copy_size;
212 	uint64_t size_unit = 1;
213 	uint64_t speed_unit = 1;
214 	uint64_t speed, tmp_speed;
215 	static struct timespec g_time_last = {.tv_nsec = 0};
216 	static uint64_t g_data_last = 0;
217 	struct timespec time_now;
218 	int i = 0;
219 
220 	clock_gettime(CLOCK_REALTIME, &time_now);
221 
222 	if (((time_now.tv_sec == g_time_last.tv_sec && offset + length != g_job.copy_size) ||
223 	     (offset < g_data_last)) && !finish) {
224 		/* refresh every one second */
225 		return;
226 	}
227 
228 	/* Find the right unit for size displaying (B vs kB vs MB vs GB vs TB) */
229 	while (size > 1024 * 10) {
230 		size >>= 10;
231 		size_unit <<= 10;
232 		size_unit_str = unit_str[++i];
233 		if (i == 4) {
234 			break;
235 		}
236 	}
237 
238 	if (!finish) {
239 		speed_type = speed_type_str[0];
240 		tmp_speed = speed = (offset - g_data_last) * 1000000000 / DD_NSEC_SINCE_X(time_now, g_time_last);
241 	} else {
242 		speed_type = speed_type_str[1];
243 		tmp_speed = speed = offset * 1000000000 / DD_NSEC_SINCE_X(time_now, g_start_time);
244 	}
245 
246 	i = 0;
247 
248 	/* Find the right unit for speed displaying (Bps vs kBps vs MBps vs GBps vs TBps) */
249 	while (tmp_speed > 1024) {
250 		tmp_speed >>= 10;
251 		speed_unit <<= 10;
252 		speed_unit_str = unit_str[++i];
253 		if (i == 4) {
254 			break;
255 		}
256 	}
257 
258 	printf("\33[2K\rCopying: %" PRIu64 "/%" PRIu64 " [%sB] (%s%" PRIu64 " %sBps)",
259 	       (offset + length) / size_unit, g_job.copy_size / size_unit, size_unit_str, speed_type,
260 	       speed / speed_unit, speed_unit_str);
261 	fflush(stdout);
262 
263 	g_data_last = offset;
264 	g_time_last = time_now;
265 }
266 
267 #ifdef SPDK_CONFIG_URING
268 static void
269 dd_uring_submit(struct dd_io *io, struct dd_target *target, uint64_t length, uint64_t offset)
270 {
271 	struct io_uring_sqe *sqe;
272 
273 	io->iov.iov_base = io->buf;
274 	io->iov.iov_len = length;
275 	sqe = io_uring_get_sqe(&g_job.u.uring.ring);
276 	if (io->type == DD_READ || io->type == DD_POPULATE) {
277 		io_uring_prep_readv(sqe, target->u.uring.fd, &io->iov, 1, offset);
278 	} else {
279 		io_uring_prep_writev(sqe, target->u.uring.fd, &io->iov, 1, offset);
280 	}
281 	io_uring_sqe_set_data(sqe, io);
282 	io_uring_submit(&g_job.u.uring.ring);
283 }
284 #endif
285 
286 static void
287 _dd_write_bdev_done(struct spdk_bdev_io *bdev_io,
288 		    bool success,
289 		    void *cb_arg)
290 {
291 	struct dd_io *io = cb_arg;
292 
293 	assert(g_job.outstanding > 0);
294 	g_job.outstanding--;
295 	spdk_bdev_free_io(bdev_io);
296 	dd_target_populate_buffer(io);
297 }
298 
299 static void
300 dd_target_write(struct dd_io *io)
301 {
302 	struct dd_target *target = &g_job.output;
303 	uint64_t length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
304 	uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
305 	uint64_t read_offset = io->offset - read_region_start;
306 	uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
307 	uint64_t write_offset = write_region_start + read_offset;
308 	int rc = 0;
309 
310 	if (g_error != 0 || g_interrupt == true) {
311 		if (g_job.outstanding == 0) {
312 			if (g_error == 0) {
313 				dd_show_progress(io->offset, io->length, true);
314 				printf("\n\n");
315 			}
316 			dd_exit(g_error);
317 		}
318 		return;
319 	}
320 
321 	dd_show_progress(read_offset, io->length, false);
322 
323 	g_job.outstanding++;
324 	io->type = DD_WRITE;
325 
326 	if (target->type == DD_TARGET_TYPE_FILE) {
327 #ifdef SPDK_CONFIG_URING
328 		if (g_opts.aio == false) {
329 			dd_uring_submit(io, target, length, write_offset);
330 		} else
331 #endif
332 		{
333 			struct iocb *iocb = &io->iocb;
334 
335 			io_prep_pwrite(iocb, target->u.aio.fd, io->buf, length, write_offset);
336 			iocb->data = io;
337 			if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
338 				rc = -errno;
339 			}
340 		}
341 	} else if (target->type == DD_TARGET_TYPE_BDEV) {
342 		rc = spdk_bdev_write(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length,
343 				     _dd_write_bdev_done, io);
344 	}
345 
346 	if (rc != 0) {
347 		SPDK_ERRLOG("%s\n", strerror(-rc));
348 		assert(g_job.outstanding > 0);
349 		g_job.outstanding--;
350 		g_error = rc;
351 		if (g_job.outstanding == 0) {
352 			dd_exit(rc);
353 		}
354 		return;
355 	}
356 }
357 
358 static void
359 _dd_read_bdev_done(struct spdk_bdev_io *bdev_io,
360 		   bool success,
361 		   void *cb_arg)
362 {
363 	struct dd_io *io = cb_arg;
364 
365 	spdk_bdev_free_io(bdev_io);
366 
367 	assert(g_job.outstanding > 0);
368 	g_job.outstanding--;
369 	dd_target_write(io);
370 }
371 
372 static void
373 dd_target_read(struct dd_io *io)
374 {
375 	struct dd_target *target = &g_job.input;
376 	int rc = 0;
377 
378 	if (g_error != 0 || g_interrupt == true) {
379 		if (g_job.outstanding == 0) {
380 			dd_exit(g_error);
381 		}
382 		return;
383 	}
384 
385 	g_job.outstanding++;
386 	io->type = DD_READ;
387 
388 	if (target->type == DD_TARGET_TYPE_FILE) {
389 #ifdef SPDK_CONFIG_URING
390 		if (g_opts.aio == false) {
391 			dd_uring_submit(io, target, io->length, io->offset);
392 		} else
393 #endif
394 		{
395 			struct iocb *iocb = &io->iocb;
396 
397 			io_prep_pread(iocb, target->u.aio.fd, io->buf, io->length, io->offset);
398 			iocb->data = io;
399 			if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
400 				rc = -errno;
401 			}
402 		}
403 	} else if (target->type == DD_TARGET_TYPE_BDEV) {
404 		rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, io->offset, io->length,
405 				    _dd_read_bdev_done, io);
406 	}
407 
408 	if (rc != 0) {
409 		SPDK_ERRLOG("%s\n", strerror(-rc));
410 		assert(g_job.outstanding > 0);
411 		g_job.outstanding--;
412 		g_error = rc;
413 		if (g_job.outstanding == 0) {
414 			dd_exit(rc);
415 		}
416 		return;
417 	}
418 }
419 
420 static void
421 _dd_target_populate_buffer_done(struct spdk_bdev_io *bdev_io,
422 				bool success,
423 				void *cb_arg)
424 {
425 	struct dd_io *io = cb_arg;
426 
427 	assert(g_job.outstanding > 0);
428 	g_job.outstanding--;
429 	spdk_bdev_free_io(bdev_io);
430 	dd_target_read(io);
431 }
432 
433 static void
434 dd_target_populate_buffer(struct dd_io *io)
435 {
436 	struct dd_target *target = &g_job.output;
437 	uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
438 	uint64_t read_offset = g_job.input.pos - read_region_start;
439 	uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
440 	uint64_t write_offset = write_region_start + read_offset;
441 	uint64_t length;
442 	int rc = 0;
443 
444 	io->offset = g_job.input.pos;
445 	io->length = spdk_min((uint64_t)g_opts.io_unit_size, g_job.copy_size - read_offset);
446 
447 	if (io->length == 0 || g_error != 0 || g_interrupt == true) {
448 		if (g_job.outstanding == 0) {
449 			if (g_error == 0) {
450 				dd_show_progress(read_offset, io->length, true);
451 				printf("\n\n");
452 			}
453 			dd_exit(g_error);
454 		}
455 		return;
456 	}
457 
458 	g_job.input.pos += io->length;
459 
460 	if ((io->length % target->block_size) == 0) {
461 		dd_target_read(io);
462 		return;
463 	}
464 
465 	/* Read whole blocks from output to combine buffers later */
466 	g_job.outstanding++;
467 	io->type = DD_POPULATE;
468 
469 	length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
470 
471 	if (target->type == DD_TARGET_TYPE_FILE) {
472 #ifdef SPDK_CONFIG_URING
473 		if (g_opts.aio == false) {
474 			dd_uring_submit(io, target, length, write_offset);
475 		} else
476 #endif
477 		{
478 			struct iocb *iocb = &io->iocb;
479 
480 			io_prep_pread(iocb, target->u.aio.fd, io->buf, length, write_offset);
481 			iocb->data = io;
482 			if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
483 				rc = -errno;
484 			}
485 		}
486 	} else if (target->type == DD_TARGET_TYPE_BDEV) {
487 		rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length,
488 				    _dd_target_populate_buffer_done, io);
489 	}
490 
491 	if (rc != 0) {
492 		SPDK_ERRLOG("%s\n", strerror(-rc));
493 		assert(g_job.outstanding > 0);
494 		g_job.outstanding--;
495 		g_error = rc;
496 		if (g_job.outstanding == 0) {
497 			dd_exit(rc);
498 		}
499 		return;
500 	}
501 }
502 
503 static void
504 dd_complete_poll(struct dd_io *io)
505 {
506 	assert(g_job.outstanding > 0);
507 	g_job.outstanding--;
508 
509 	switch (io->type) {
510 	case DD_POPULATE:
511 		dd_target_read(io);
512 		break;
513 	case DD_READ:
514 		dd_target_write(io);
515 		break;
516 	case DD_WRITE:
517 		dd_target_populate_buffer(io);
518 		break;
519 	default:
520 		assert(false);
521 		break;
522 	}
523 }
524 
525 #ifdef SPDK_CONFIG_URING
526 static int
527 dd_uring_poll(void *ctx)
528 {
529 	struct io_uring_cqe *cqe;
530 	struct dd_io *io;
531 	int rc = 0;
532 	int i;
533 
534 	for (i = 0; i < (int)g_opts.queue_depth; i++) {
535 		rc = io_uring_peek_cqe(&g_job.u.uring.ring, &cqe);
536 		if (rc == 0) {
537 			if (cqe->res == -EAGAIN) {
538 				continue;
539 			} else if (cqe->res < 0) {
540 				SPDK_ERRLOG("%s\n", strerror(-cqe->res));
541 				g_error = cqe->res;
542 			}
543 
544 			io = io_uring_cqe_get_data(cqe);
545 			io_uring_cqe_seen(&g_job.u.uring.ring, cqe);
546 
547 			dd_complete_poll(io);
548 		} else if (rc != - EAGAIN) {
549 			SPDK_ERRLOG("%s\n", strerror(-rc));
550 			g_error = rc;
551 		}
552 	}
553 
554 	return rc;
555 }
556 #endif
557 
558 static int
559 dd_aio_poll(void *ctx)
560 {
561 	struct io_event events[32];
562 	int rc = 0;
563 	int i;
564 	struct timespec timeout;
565 	struct dd_io *io;
566 
567 	timeout.tv_sec = 0;
568 	timeout.tv_nsec = 0;
569 
570 	rc = io_getevents(g_job.u.aio.io_ctx, 0, 32, events, &timeout);
571 
572 	if (rc < 0) {
573 		SPDK_ERRLOG("%s\n", strerror(-rc));
574 		dd_exit(rc);
575 	}
576 
577 	for (i = 0; i < rc; i++) {
578 		io = events[i].data;
579 		if (events[i].res != io->length) {
580 			g_error = rc = -ENOSPC;
581 		}
582 
583 		dd_complete_poll(io);
584 	}
585 
586 	return rc;
587 }
588 
589 static int
590 dd_open_file(struct dd_target *target, const char *fname, int flags, uint64_t skip_blocks,
591 	     bool input)
592 {
593 	int *fd;
594 
595 #ifdef SPDK_CONFIG_URING
596 	if (g_opts.aio == false) {
597 		fd = &target->u.uring.fd;
598 	} else
599 #endif
600 	{
601 		fd = &target->u.aio.fd;
602 	}
603 
604 	flags |= O_RDWR;
605 
606 	if (input == false && ((flags & O_DIRECTORY) == 0)) {
607 		flags |= O_CREAT;
608 	}
609 
610 	if (input == false && ((flags & O_APPEND) == 0)) {
611 		flags |= O_TRUNC;
612 	}
613 
614 #ifdef SPDK_CONFIG_URING
615 	/* io_uring does not work correctly with O_NONBLOCK flag */
616 	if (flags & O_NONBLOCK && g_opts.aio == false) {
617 		flags &= ~O_NONBLOCK;
618 		SPDK_WARNLOG("Skipping 'nonblock' flag due to existing issue with uring implementation and this flag\n");
619 	}
620 #endif
621 
622 	target->type = DD_TARGET_TYPE_FILE;
623 	*fd = open(fname, flags, 0600);
624 	if (*fd < 0) {
625 		SPDK_ERRLOG("Could not open file %s: %s\n", fname, strerror(errno));
626 		return *fd;
627 	}
628 
629 	target->block_size = spdk_max(spdk_fd_get_blocklen(*fd), 1);
630 
631 	target->total_size = spdk_fd_get_size(*fd);
632 	if (target->total_size == 0) {
633 		target->total_size = g_opts.io_unit_size * g_opts.io_unit_count;
634 	}
635 
636 	if (input == true) {
637 		g_opts.queue_depth = spdk_min(g_opts.queue_depth,
638 					      (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
639 	}
640 
641 	if (g_opts.io_unit_count != 0) {
642 		g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
643 	}
644 
645 	return 0;
646 }
647 
648 static void
649 dd_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
650 		 void *event_ctx)
651 {
652 	SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
653 }
654 
655 static int
656 dd_open_bdev(struct dd_target *target, const char *bdev_name, uint64_t skip_blocks)
657 {
658 	int rc;
659 
660 	target->type = DD_TARGET_TYPE_BDEV;
661 
662 	rc = spdk_bdev_open_ext(bdev_name, true, dd_bdev_event_cb, NULL, &target->u.bdev.desc);
663 	if (rc < 0) {
664 		SPDK_ERRLOG("Could not open bdev %s: %s\n", bdev_name, strerror(-rc));
665 		return rc;
666 	}
667 
668 	target->u.bdev.bdev = spdk_bdev_desc_get_bdev(target->u.bdev.desc);
669 	target->open = true;
670 
671 	target->u.bdev.ch = spdk_bdev_get_io_channel(target->u.bdev.desc);
672 	if (target->u.bdev.ch == NULL) {
673 		spdk_bdev_close(target->u.bdev.desc);
674 		SPDK_ERRLOG("Could not get I/O channel: %s\n", strerror(ENOMEM));
675 		return -ENOMEM;
676 	}
677 
678 	target->block_size = spdk_bdev_get_block_size(target->u.bdev.bdev);
679 	target->total_size = spdk_bdev_get_num_blocks(target->u.bdev.bdev) * target->block_size;
680 
681 	g_opts.queue_depth = spdk_min(g_opts.queue_depth,
682 				      (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
683 
684 	if (g_opts.io_unit_count != 0) {
685 		g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
686 	}
687 
688 	return 0;
689 }
690 
691 static void
692 dd_finish(void)
693 {
694 	/* Interrupt operation */
695 	g_interrupt = true;
696 }
697 
698 static int
699 parse_flags(char *file_flags)
700 {
701 	char *input_flag;
702 	int flags = 0;
703 	int i;
704 	bool found = false;
705 
706 	/* Translate input flags to file open flags */
707 	while ((input_flag = strsep(&file_flags, ","))) {
708 		for (i = 0; g_flags[i].name != NULL; i++) {
709 			if (!strcmp(input_flag, g_flags[i].name)) {
710 				flags |= g_flags[i].flag;
711 				found = true;
712 				break;
713 			}
714 		}
715 
716 		if (found == false) {
717 			SPDK_ERRLOG("Unknown file flag: %s\n", input_flag);
718 			return -EINVAL;
719 		}
720 
721 		found = false;
722 	}
723 
724 	return flags;
725 }
726 
727 static void
728 dd_run(void *arg1)
729 {
730 	uint64_t write_size;
731 	uint32_t i;
732 	int rc, flags = 0;
733 
734 	if (g_opts.input_file) {
735 		if (g_opts.input_file_flags) {
736 			flags = parse_flags(g_opts.input_file_flags);
737 		}
738 
739 		if (dd_open_file(&g_job.input, g_opts.input_file, flags, g_opts.input_offset, true) < 0) {
740 			SPDK_ERRLOG("%s: %s\n", g_opts.input_file, strerror(errno));
741 			dd_exit(-errno);
742 			return;
743 		}
744 	} else if (g_opts.input_bdev) {
745 		rc = dd_open_bdev(&g_job.input, g_opts.input_bdev, g_opts.input_offset);
746 		if (rc < 0) {
747 			SPDK_ERRLOG("%s: %s\n", g_opts.input_bdev, strerror(-rc));
748 			dd_exit(rc);
749 			return;
750 		}
751 	}
752 
753 	write_size = g_opts.io_unit_count * g_opts.io_unit_size;
754 	g_job.input.pos = g_opts.input_offset * g_opts.io_unit_size;
755 
756 	/* We cannot check write size for input files because /dev/zeros, /dev/random, etc would not work.
757 	 * We will handle that during copying */
758 	if (g_opts.input_bdev && g_job.input.pos > g_job.input.total_size) {
759 		SPDK_ERRLOG("--skip value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in input\n",
760 			    g_opts.input_offset, g_job.input.total_size / g_opts.io_unit_size);
761 		dd_exit(-ENOSPC);
762 		return;
763 	}
764 
765 	if (g_opts.io_unit_count != 0 && g_opts.input_bdev &&
766 	    write_size + g_job.input.pos > g_job.input.total_size) {
767 		SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available from input\n",
768 			    g_opts.io_unit_count, (g_job.input.total_size - g_job.input.pos) / g_opts.io_unit_size);
769 		dd_exit(-ENOSPC);
770 		return;
771 	}
772 
773 	if (g_opts.io_unit_count != 0) {
774 		g_job.copy_size = write_size;
775 	} else {
776 		g_job.copy_size = g_job.input.total_size - g_job.input.pos;
777 	}
778 
779 	g_job.output.pos = g_opts.output_offset * g_opts.io_unit_size;
780 
781 	if (g_opts.output_file) {
782 		flags = 0;
783 
784 		if (g_opts.output_file_flags) {
785 			flags = parse_flags(g_opts.output_file_flags);
786 		}
787 
788 		if (dd_open_file(&g_job.output, g_opts.output_file, flags, g_opts.output_offset, false) < 0) {
789 			SPDK_ERRLOG("%s: %s\n", g_opts.output_file, strerror(errno));
790 			dd_exit(-errno);
791 			return;
792 		}
793 	} else if (g_opts.output_bdev) {
794 		rc = dd_open_bdev(&g_job.output, g_opts.output_bdev, g_opts.output_offset);
795 		if (rc < 0) {
796 			SPDK_ERRLOG("%s: %s\n", g_opts.output_bdev, strerror(-rc));
797 			dd_exit(rc);
798 			return;
799 		}
800 
801 		if (g_job.output.pos > g_job.output.total_size) {
802 			SPDK_ERRLOG("--seek value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
803 				    g_opts.output_offset, g_job.output.total_size / g_opts.io_unit_size);
804 			dd_exit(-ENOSPC);
805 			return;
806 		}
807 
808 		if (g_opts.io_unit_count != 0 && write_size + g_job.output.pos > g_job.output.total_size) {
809 			SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
810 				    g_opts.io_unit_count, (g_job.output.total_size - g_job.output.pos) / g_opts.io_unit_size);
811 			dd_exit(-ENOSPC);
812 			return;
813 		}
814 	}
815 
816 	if ((g_job.output.block_size > g_opts.io_unit_size) ||
817 	    (g_job.input.block_size > g_opts.io_unit_size)) {
818 		SPDK_ERRLOG("--bs value cannot be less than input (%d) neither output (%d) native block size\n",
819 			    g_job.input.block_size, g_job.output.block_size);
820 		dd_exit(-EINVAL);
821 		return;
822 	}
823 
824 	g_job.ios = calloc(g_opts.queue_depth, sizeof(struct dd_io));
825 	if (g_job.ios == NULL) {
826 		SPDK_ERRLOG("%s\n", strerror(ENOMEM));
827 		dd_exit(-ENOMEM);
828 		return;
829 	}
830 
831 	for (i = 0; i < g_opts.queue_depth; i++) {
832 		g_job.ios[i].buf = spdk_malloc(g_opts.io_unit_size, 0x1000, NULL, 0, SPDK_MALLOC_DMA);
833 		if (g_job.ios[i].buf == NULL) {
834 			SPDK_ERRLOG("%s - try smaller block size value\n", strerror(ENOMEM));
835 			dd_exit(-ENOMEM);
836 			return;
837 		}
838 	}
839 
840 	if (g_opts.input_file || g_opts.output_file) {
841 #ifdef SPDK_CONFIG_URING
842 		if (g_opts.aio == false) {
843 			g_job.u.uring.poller = spdk_poller_register(dd_uring_poll, NULL, 0);
844 			rc = io_uring_queue_init(g_opts.queue_depth * 2, &g_job.u.uring.ring, IORING_SETUP_SQPOLL);
845 			if (rc) {
846 				SPDK_ERRLOG("Failed to create io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
847 				dd_exit(rc);
848 				return;
849 			}
850 			g_job.u.uring.active = true;
851 		} else
852 #endif
853 		{
854 			g_job.u.aio.poller = spdk_poller_register(dd_aio_poll, NULL, 0);
855 			io_setup(g_opts.queue_depth, &g_job.u.aio.io_ctx);
856 		}
857 	}
858 
859 	clock_gettime(CLOCK_REALTIME, &g_start_time);
860 
861 	for (i = 0; i < g_opts.queue_depth; i++) {
862 		dd_target_populate_buffer(&g_job.ios[i]);
863 	}
864 
865 }
866 
867 enum dd_cmdline_opts {
868 	DD_OPTION_IF = 0x1000,
869 	DD_OPTION_OF,
870 	DD_OPTION_IFLAGS,
871 	DD_OPTION_OFLAGS,
872 	DD_OPTION_IB,
873 	DD_OPTION_OB,
874 	DD_OPTION_SKIP,
875 	DD_OPTION_SEEK,
876 	DD_OPTION_BS,
877 	DD_OPTION_QD,
878 	DD_OPTION_COUNT,
879 	DD_OPTION_AIO,
880 };
881 
882 static struct option g_cmdline_opts[] = {
883 	{
884 		.name = "if",
885 		.has_arg = 1,
886 		.flag = NULL,
887 		.val = DD_OPTION_IF,
888 	},
889 	{
890 		.name = "of",
891 		.has_arg = 1,
892 		.flag = NULL,
893 		.val = DD_OPTION_OF,
894 	},
895 	{
896 		.name = "iflag",
897 		.has_arg = 1,
898 		.flag = NULL,
899 		.val = DD_OPTION_IFLAGS,
900 	},
901 	{
902 		.name = "oflag",
903 		.has_arg = 1,
904 		.flag = NULL,
905 		.val = DD_OPTION_OFLAGS,
906 	},
907 	{
908 		.name = "ib",
909 		.has_arg = 1,
910 		.flag = NULL,
911 		.val = DD_OPTION_IB,
912 	},
913 	{
914 		.name = "ob",
915 		.has_arg = 1,
916 		.flag = NULL,
917 		.val = DD_OPTION_OB,
918 	},
919 	{
920 		.name = "skip",
921 		.has_arg = 1,
922 		.flag = NULL,
923 		.val = DD_OPTION_SKIP,
924 	},
925 	{
926 		.name = "seek",
927 		.has_arg = 1,
928 		.flag = NULL,
929 		.val = DD_OPTION_SEEK,
930 	},
931 	{
932 		.name = "bs",
933 		.has_arg = 1,
934 		.flag = NULL,
935 		.val = DD_OPTION_BS,
936 	},
937 	{
938 		.name = "qd",
939 		.has_arg = 1,
940 		.flag = NULL,
941 		.val = DD_OPTION_QD,
942 	},
943 	{
944 		.name = "count",
945 		.has_arg = 1,
946 		.flag = NULL,
947 		.val = DD_OPTION_COUNT,
948 	},
949 	{
950 		.name = "aio",
951 		.has_arg = 0,
952 		.flag = NULL,
953 		.val = DD_OPTION_AIO,
954 	},
955 	{
956 		.name = NULL
957 	}
958 };
959 
960 static void
961 usage(void)
962 {
963 	printf("[--------- DD Options ---------]\n");
964 	printf(" --if Input file. Must specify either --if or --ib.\n");
965 	printf(" --ib Input bdev. Must specifier either --if or --ib\n");
966 	printf(" --of Output file. Must specify either --of or --ob.\n");
967 	printf(" --ob Output bdev. Must specify either --of or --ob.\n");
968 	printf(" --iflag Input file flags.\n");
969 	printf(" --oflag Output file flags.\n");
970 	printf(" --bs I/O unit size (default: %" PRId64 ")\n", g_opts.io_unit_size);
971 	printf(" --qd Queue depth (default: %d)\n", g_opts.queue_depth);
972 	printf(" --count I/O unit count. The number of I/O units to copy. (default: all)\n");
973 	printf(" --skip Skip this many I/O units at start of input. (default: 0)\n");
974 	printf(" --seek Skip this many I/O units at start of output. (default: 0)\n");
975 	printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n");
976 	printf(" Available iflag and oflag values:\n");
977 	printf("  append - append mode\n");
978 	printf("  direct - use direct I/O for data\n");
979 	printf("  directory - fail unless a directory\n");
980 	printf("  dsync - use synchronized I/O for data\n");
981 	printf("  noatime - do not update access time\n");
982 	printf("  noctty - do not assign controlling terminal from file\n");
983 	printf("  nofollow - do not follow symlinks\n");
984 	printf("  nonblock - use non-blocking I/O\n");
985 	printf("  sync - use synchronized I/O for data and metadata\n");
986 }
987 
988 static int
989 parse_args(int argc, char *argv)
990 {
991 	switch (argc) {
992 	case DD_OPTION_IF:
993 		g_opts.input_file = strdup(argv);
994 		break;
995 	case DD_OPTION_OF:
996 		g_opts.output_file = strdup(argv);
997 		break;
998 	case DD_OPTION_IFLAGS:
999 		g_opts.input_file_flags = strdup(argv);
1000 		break;
1001 	case DD_OPTION_OFLAGS:
1002 		g_opts.output_file_flags = strdup(argv);
1003 		break;
1004 	case DD_OPTION_IB:
1005 		g_opts.input_bdev = strdup(argv);
1006 		break;
1007 	case DD_OPTION_OB:
1008 		g_opts.output_bdev = strdup(argv);
1009 		break;
1010 	case DD_OPTION_SKIP:
1011 		g_opts.input_offset = spdk_strtol(optarg, 10);
1012 		break;
1013 	case DD_OPTION_SEEK:
1014 		g_opts.output_offset = spdk_strtol(optarg, 10);
1015 		break;
1016 	case DD_OPTION_BS:
1017 		g_opts.io_unit_size = spdk_strtol(optarg, 10);
1018 		break;
1019 	case DD_OPTION_QD:
1020 		g_opts.queue_depth = spdk_strtol(optarg, 10);
1021 		break;
1022 	case DD_OPTION_COUNT:
1023 		g_opts.io_unit_count = spdk_strtol(optarg, 10);
1024 		break;
1025 	case DD_OPTION_AIO:
1026 		g_opts.aio = true;
1027 		break;
1028 	default:
1029 		usage();
1030 		return 1;
1031 	}
1032 	return 0;
1033 }
1034 
1035 static void
1036 dd_free(void)
1037 {
1038 	uint32_t i;
1039 
1040 	free(g_opts.input_file);
1041 	free(g_opts.output_file);
1042 	free(g_opts.input_bdev);
1043 	free(g_opts.output_bdev);
1044 	free(g_opts.input_file_flags);
1045 	free(g_opts.output_file_flags);
1046 
1047 
1048 	if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
1049 #ifdef SPDK_CONFIG_URING
1050 		if (g_opts.aio == false) {
1051 			if (g_job.u.uring.active) {
1052 				io_uring_queue_exit(&g_job.u.uring.ring);
1053 			}
1054 		} else
1055 #endif
1056 		{
1057 			io_destroy(g_job.u.aio.io_ctx);
1058 		}
1059 	}
1060 
1061 	if (g_job.ios) {
1062 		for (i = 0; i < g_opts.queue_depth; i++) {
1063 			spdk_free(g_job.ios[i].buf);
1064 		}
1065 
1066 		free(g_job.ios);
1067 	}
1068 }
1069 
1070 int
1071 main(int argc, char **argv)
1072 {
1073 	struct spdk_app_opts opts = {};
1074 	int rc = 1;
1075 
1076 	spdk_app_opts_init(&opts, sizeof(opts));
1077 	opts.name = "spdk_dd";
1078 	opts.reactor_mask = "0x1";
1079 	opts.shutdown_cb = dd_finish;
1080 	rc = spdk_app_parse_args(argc, argv, &opts, "", g_cmdline_opts, parse_args, usage);
1081 	if (rc == SPDK_APP_PARSE_ARGS_FAIL) {
1082 		SPDK_ERRLOG("Invalid arguments\n");
1083 		goto end;
1084 	} else if (rc == SPDK_APP_PARSE_ARGS_HELP) {
1085 		goto end;
1086 	}
1087 
1088 	if (g_opts.input_file != NULL && g_opts.input_bdev != NULL) {
1089 		SPDK_ERRLOG("You may specify either --if or --ib, but not both.\n");
1090 		rc = EINVAL;
1091 		goto end;
1092 	}
1093 
1094 	if (g_opts.output_file != NULL && g_opts.output_bdev != NULL) {
1095 		SPDK_ERRLOG("You may specify either --of or --ob, but not both.\n");
1096 		rc = EINVAL;
1097 		goto end;
1098 	}
1099 
1100 	if (g_opts.input_file == NULL && g_opts.input_bdev == NULL) {
1101 		SPDK_ERRLOG("You must specify either --if or --ib\n");
1102 		rc = EINVAL;
1103 		goto end;
1104 	}
1105 
1106 	if (g_opts.output_file == NULL && g_opts.output_bdev == NULL) {
1107 		SPDK_ERRLOG("You must specify either --of or --ob\n");
1108 		rc = EINVAL;
1109 		goto end;
1110 	}
1111 
1112 	if (g_opts.io_unit_size <= 0) {
1113 		SPDK_ERRLOG("Invalid --bs value\n");
1114 		rc = EINVAL;
1115 		goto end;
1116 	}
1117 
1118 	if (g_opts.io_unit_count < 0) {
1119 		SPDK_ERRLOG("Invalid --count value\n");
1120 		rc = EINVAL;
1121 		goto end;
1122 	}
1123 
1124 	if (g_opts.output_file == NULL && g_opts.output_file_flags != NULL) {
1125 		SPDK_ERRLOG("--oflags may be used only with --of\n");
1126 		rc = EINVAL;
1127 		goto end;
1128 	}
1129 
1130 	if (g_opts.input_file == NULL && g_opts.input_file_flags != NULL) {
1131 		SPDK_ERRLOG("--iflags may be used only with --if\n");
1132 		rc = EINVAL;
1133 		goto end;
1134 	}
1135 
1136 	rc = spdk_app_start(&opts, dd_run, NULL);
1137 	if (rc) {
1138 		SPDK_ERRLOG("Error occurred while performing copy\n");
1139 	}
1140 
1141 	dd_free();
1142 	spdk_app_fini();
1143 
1144 end:
1145 	return rc;
1146 }
1147