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