xref: /spdk/app/spdk_dd/spdk_dd.c (revision c6c1234de9e0015e670dd0b51bf6ce39ee0e07bd)
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 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 	if (io.u.bdev.ch) {
164 		spdk_put_io_channel(io.u.bdev.ch);
165 	}
166 
167 	spdk_bdev_close(io.u.bdev.desc);
168 }
169 
170 static void
171 dd_exit(int rc)
172 {
173 	if (g_job.input.type == DD_TARGET_TYPE_FILE) {
174 #ifdef SPDK_CONFIG_URING
175 		if (g_opts.aio == false) {
176 			close(g_job.input.u.uring.fd);
177 		} else
178 #endif
179 		{
180 			close(g_job.input.u.aio.fd);
181 		}
182 	} else if (g_job.input.type == DD_TARGET_TYPE_BDEV && g_job.input.open) {
183 		dd_cleanup_bdev(g_job.input);
184 	}
185 
186 	if (g_job.output.type == DD_TARGET_TYPE_FILE) {
187 #ifdef SPDK_CONFIG_URING
188 		if (g_opts.aio == false) {
189 			close(g_job.output.u.uring.fd);
190 		} else
191 #endif
192 		{
193 			close(g_job.output.u.aio.fd);
194 		}
195 	} else if (g_job.output.type == DD_TARGET_TYPE_BDEV && g_job.output.open) {
196 		dd_cleanup_bdev(g_job.output);
197 	}
198 
199 	if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
200 #ifdef SPDK_CONFIG_URING
201 		if (g_opts.aio == false) {
202 			spdk_poller_unregister(&g_job.u.uring.poller);
203 		} else
204 #endif
205 		{
206 			spdk_poller_unregister(&g_job.u.aio.poller);
207 		}
208 	}
209 
210 	spdk_poller_unregister(&g_job.status_poller);
211 
212 	spdk_app_stop(rc);
213 }
214 
215 static void
216 dd_show_progress(bool finish)
217 {
218 	char *unit_str[5] = {"", "k", "M", "G", "T"};
219 	char *speed_type_str[2] = {"", "average "};
220 	char *size_unit_str = "";
221 	char *speed_unit_str = "";
222 	char *speed_type;
223 	uint64_t size_unit = 1;
224 	uint64_t speed_unit = 1;
225 	uint64_t speed, tmp_speed;
226 	int i = 0;
227 	uint64_t milliseconds;
228 	uint64_t size, tmp_size;
229 
230 	size = g_job.incremental_bytes;
231 
232 	g_job.incremental_bytes = 0;
233 	g_job.total_bytes += size;
234 
235 	if (finish) {
236 		struct timespec time_now;
237 
238 		clock_gettime(CLOCK_REALTIME, &time_now);
239 
240 		milliseconds = spdk_max(1, TIMESPEC_TO_MS(time_now) - TIMESPEC_TO_MS(g_job.start_time));
241 		size = g_job.total_bytes;
242 	} else {
243 		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 	tmp_size = size;
248 	while (tmp_size > 1024 * 10) {
249 		tmp_size >>= 10;
250 		size_unit <<= 10;
251 		size_unit_str = unit_str[++i];
252 		if (i == 4) {
253 			break;
254 		}
255 	}
256 
257 	speed_type = finish ? speed_type_str[1] : speed_type_str[0];
258 	speed = (size * 1000) / milliseconds;
259 
260 	i = 0;
261 
262 	/* Find the right unit for speed displaying (Bps vs kBps vs MBps vs GBps vs TBps) */
263 	tmp_speed = speed;
264 	while (tmp_speed > 1024 * 10) {
265 		tmp_speed >>= 10;
266 		speed_unit <<= 10;
267 		speed_unit_str = unit_str[++i];
268 		if (i == 4) {
269 			break;
270 		}
271 	}
272 
273 	printf("\33[2K\rCopying: %" PRIu64 "/%" PRIu64 " [%sB] (%s%" PRIu64 " %sBps)",
274 	       g_job.total_bytes / size_unit, g_job.copy_size / size_unit, size_unit_str, speed_type,
275 	       speed / speed_unit, speed_unit_str);
276 	fflush(stdout);
277 }
278 
279 static int
280 dd_status_poller(void *ctx)
281 {
282 	dd_show_progress(false);
283 	return SPDK_POLLER_BUSY;
284 }
285 
286 static void
287 dd_finalize_output(void)
288 {
289 	off_t curr_offset;
290 	int rc = 0;
291 
292 	if (g_job.outstanding > 0) {
293 		return;
294 	}
295 
296 	if (g_opts.output_file) {
297 		curr_offset = lseek(g_job.output.u.aio.fd, 0, SEEK_END);
298 		if (curr_offset == (off_t) -1) {
299 			SPDK_ERRLOG("Could not seek output file for finalize: %s\n", strerror(errno));
300 			g_error = errno;
301 		} else if ((uint64_t)curr_offset < g_job.copy_size + g_job.output.pos) {
302 			rc = ftruncate(g_job.output.u.aio.fd, g_job.copy_size + g_job.output.pos);
303 			if (rc != 0) {
304 				SPDK_ERRLOG("Could not truncate output file for finalize: %s\n", strerror(errno));
305 				g_error = errno;
306 			}
307 		}
308 	}
309 
310 	if (g_error == 0) {
311 		dd_show_progress(true);
312 		printf("\n\n");
313 	}
314 	dd_exit(g_error);
315 }
316 
317 #ifdef SPDK_CONFIG_URING
318 static void
319 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 	sqe = io_uring_get_sqe(&g_job.u.uring.ring);
324 	if (io->type == DD_READ || io->type == DD_POPULATE) {
325 		io_uring_prep_read_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx);
326 	} else {
327 		io_uring_prep_write_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx);
328 	}
329 	sqe->flags |= IOSQE_FIXED_FILE;
330 	io_uring_sqe_set_data(sqe, io);
331 	io_uring_submit(&g_job.u.uring.ring);
332 }
333 #endif
334 
335 static void
336 _dd_write_bdev_done(struct spdk_bdev_io *bdev_io,
337 		    bool success,
338 		    void *cb_arg)
339 {
340 	struct dd_io *io = cb_arg;
341 
342 	assert(g_job.outstanding > 0);
343 	g_job.outstanding--;
344 	spdk_bdev_free_io(bdev_io);
345 	dd_target_seek(io);
346 }
347 
348 static void
349 dd_target_write(struct dd_io *io)
350 {
351 	struct dd_target *target = &g_job.output;
352 	uint64_t length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
353 	uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
354 	uint64_t read_offset = io->offset - read_region_start;
355 	uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
356 	uint64_t write_offset = write_region_start + read_offset;
357 	int rc = 0;
358 
359 	if (g_error != 0 || g_interrupt == true) {
360 		if (g_job.outstanding == 0) {
361 			if (g_error == 0) {
362 				dd_show_progress(true);
363 				printf("\n\n");
364 			}
365 			dd_exit(g_error);
366 		}
367 		return;
368 	}
369 
370 	g_job.incremental_bytes += io->length;
371 	g_job.outstanding++;
372 	io->type = DD_WRITE;
373 
374 	if (target->type == DD_TARGET_TYPE_FILE) {
375 #ifdef SPDK_CONFIG_URING
376 		if (g_opts.aio == false) {
377 			dd_uring_submit(io, target, length, write_offset);
378 		} else
379 #endif
380 		{
381 			struct iocb *iocb = &io->iocb;
382 
383 			io_prep_pwrite(iocb, target->u.aio.fd, io->buf, length, write_offset);
384 			iocb->data = io;
385 			if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
386 				rc = -errno;
387 			}
388 		}
389 	} else if (target->type == DD_TARGET_TYPE_BDEV) {
390 		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 	if (rc != 0) {
395 		SPDK_ERRLOG("%s\n", strerror(-rc));
396 		assert(g_job.outstanding > 0);
397 		g_job.outstanding--;
398 		g_error = rc;
399 		if (g_job.outstanding == 0) {
400 			dd_exit(rc);
401 		}
402 		return;
403 	}
404 }
405 
406 static void
407 _dd_read_bdev_done(struct spdk_bdev_io *bdev_io,
408 		   bool success,
409 		   void *cb_arg)
410 {
411 	struct dd_io *io = cb_arg;
412 
413 	spdk_bdev_free_io(bdev_io);
414 
415 	assert(g_job.outstanding > 0);
416 	g_job.outstanding--;
417 	dd_target_write(io);
418 }
419 
420 static void
421 dd_target_read(struct dd_io *io)
422 {
423 	struct dd_target *target = &g_job.input;
424 	int rc = 0;
425 
426 	if (g_error != 0 || g_interrupt == true) {
427 		if (g_job.outstanding == 0) {
428 			dd_exit(g_error);
429 		}
430 		return;
431 	}
432 
433 	g_job.outstanding++;
434 	io->type = DD_READ;
435 
436 	if (target->type == DD_TARGET_TYPE_FILE) {
437 #ifdef SPDK_CONFIG_URING
438 		if (g_opts.aio == false) {
439 			dd_uring_submit(io, target, io->length, io->offset);
440 		} else
441 #endif
442 		{
443 			struct iocb *iocb = &io->iocb;
444 
445 			io_prep_pread(iocb, target->u.aio.fd, io->buf, io->length, io->offset);
446 			iocb->data = io;
447 			if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
448 				rc = -errno;
449 			}
450 		}
451 	} else if (target->type == DD_TARGET_TYPE_BDEV) {
452 		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 	if (rc != 0) {
457 		SPDK_ERRLOG("%s\n", strerror(-rc));
458 		assert(g_job.outstanding > 0);
459 		g_job.outstanding--;
460 		g_error = rc;
461 		if (g_job.outstanding == 0) {
462 			dd_exit(rc);
463 		}
464 		return;
465 	}
466 }
467 
468 static void
469 _dd_target_populate_buffer_done(struct spdk_bdev_io *bdev_io,
470 				bool success,
471 				void *cb_arg)
472 {
473 	struct dd_io *io = cb_arg;
474 
475 	assert(g_job.outstanding > 0);
476 	g_job.outstanding--;
477 	spdk_bdev_free_io(bdev_io);
478 	dd_target_read(io);
479 }
480 
481 static void
482 dd_target_populate_buffer(struct dd_io *io)
483 {
484 	struct dd_target *target = &g_job.output;
485 	uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
486 	uint64_t read_offset = g_job.input.pos - read_region_start;
487 	uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size;
488 	uint64_t write_offset = write_region_start + read_offset;
489 	uint64_t length;
490 	int rc = 0;
491 
492 	io->offset = g_job.input.pos;
493 	io->length = spdk_min(io->length, g_job.copy_size - read_offset);
494 
495 	if (io->length == 0 || g_error != 0 || g_interrupt == true) {
496 		if (g_job.outstanding == 0) {
497 			if (g_error == 0) {
498 				dd_show_progress(true);
499 				printf("\n\n");
500 			}
501 			dd_exit(g_error);
502 		}
503 		return;
504 	}
505 
506 	g_job.input.pos += io->length;
507 
508 	if ((io->length % target->block_size) == 0) {
509 		dd_target_read(io);
510 		return;
511 	}
512 
513 	/* Read whole blocks from output to combine buffers later */
514 	g_job.outstanding++;
515 	io->type = DD_POPULATE;
516 
517 	length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size;
518 
519 	if (target->type == DD_TARGET_TYPE_FILE) {
520 #ifdef SPDK_CONFIG_URING
521 		if (g_opts.aio == false) {
522 			dd_uring_submit(io, target, length, write_offset);
523 		} else
524 #endif
525 		{
526 			struct iocb *iocb = &io->iocb;
527 
528 			io_prep_pread(iocb, target->u.aio.fd, io->buf, length, write_offset);
529 			iocb->data = io;
530 			if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) {
531 				rc = -errno;
532 			}
533 		}
534 	} else if (target->type == DD_TARGET_TYPE_BDEV) {
535 		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 	if (rc != 0) {
540 		SPDK_ERRLOG("%s\n", strerror(-rc));
541 		assert(g_job.outstanding > 0);
542 		g_job.outstanding--;
543 		g_error = rc;
544 		if (g_job.outstanding == 0) {
545 			dd_exit(rc);
546 		}
547 		return;
548 	}
549 }
550 
551 static off_t
552 dd_file_seek_data(void)
553 {
554 	off_t next_data_offset = (off_t) -1;
555 
556 	next_data_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_DATA);
557 
558 	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 		if (errno == ENXIO) {
564 			dd_finalize_output();
565 		} else if (g_job.outstanding == 0) {
566 			SPDK_ERRLOG("Could not seek input file for data: %s\n", strerror(errno));
567 			g_error = errno;
568 			dd_exit(g_error);
569 		}
570 	}
571 
572 	return next_data_offset;
573 }
574 
575 static off_t
576 dd_file_seek_hole(void)
577 {
578 	off_t next_hole_offset = (off_t) -1;
579 
580 	next_hole_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_HOLE);
581 
582 	if (next_hole_offset == (off_t) -1 && g_job.outstanding == 0) {
583 		SPDK_ERRLOG("Could not seek input file for hole: %s\n", strerror(errno));
584 		g_error = errno;
585 		dd_exit(g_error);
586 	}
587 
588 	return next_hole_offset;
589 }
590 
591 static void
592 _dd_bdev_seek_data_done(struct spdk_bdev_io *bdev_io,
593 			bool success,
594 			void *cb_arg)
595 {
596 	struct dd_io *io = cb_arg;
597 	uint64_t next_data_offset_blocks = UINT64_MAX;
598 	struct dd_target *target = &g_job.input;
599 	int rc = 0;
600 
601 	if (g_error != 0 || g_interrupt == true) {
602 		STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
603 		if (g_job.outstanding == 0) {
604 			if (g_error == 0) {
605 				dd_show_progress(true);
606 				printf("\n\n");
607 			}
608 			dd_exit(g_error);
609 		}
610 		return;
611 	}
612 
613 	assert(g_job.outstanding > 0);
614 	g_job.outstanding--;
615 
616 	next_data_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
617 	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 	if (next_data_offset_blocks == UINT64_MAX) {
624 		STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
625 		dd_finalize_output();
626 		return;
627 	}
628 
629 	g_job.input.pos = next_data_offset_blocks * g_job.input.block_size;
630 
631 	g_job.outstanding++;
632 	rc = spdk_bdev_seek_hole(target->u.bdev.desc, target->u.bdev.ch,
633 				 g_job.input.pos / g_job.input.block_size,
634 				 _dd_bdev_seek_hole_done, io);
635 
636 	if (rc != 0) {
637 		SPDK_ERRLOG("%s\n", strerror(-rc));
638 		STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
639 		assert(g_job.outstanding > 0);
640 		g_job.outstanding--;
641 		g_error = rc;
642 		if (g_job.outstanding == 0) {
643 			dd_exit(rc);
644 		}
645 	}
646 }
647 
648 static void
649 _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io,
650 			bool success,
651 			void *cb_arg)
652 {
653 	struct dd_io *io = cb_arg;
654 	struct dd_target *target = &g_job.input;
655 	uint64_t next_hole_offset_blocks = UINT64_MAX;
656 	struct dd_io *seek_io;
657 	int rc = 0;
658 
659 	/* First seek operation is the one in progress, i.e. this one just ended */
660 	STAILQ_REMOVE_HEAD(&g_job.seek_queue, link);
661 
662 	if (g_error != 0 || g_interrupt == true) {
663 		if (g_job.outstanding == 0) {
664 			if (g_error == 0) {
665 				dd_show_progress(true);
666 				printf("\n\n");
667 			}
668 			dd_exit(g_error);
669 		}
670 		return;
671 	}
672 
673 	assert(g_job.outstanding > 0);
674 	g_job.outstanding--;
675 
676 	next_hole_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io);
677 	spdk_bdev_free_io(bdev_io);
678 
679 	/* UINT64_MAX means there are no more holes. */
680 	if (next_hole_offset_blocks == UINT64_MAX) {
681 		io->length = g_opts.io_unit_size;
682 	} else {
683 		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 	dd_target_populate_buffer(io);
688 
689 	/* If input reading is not at the end, start following seek operation in the queue */
690 	if (!STAILQ_EMPTY(&g_job.seek_queue) && g_job.input.pos < g_job.input.total_size) {
691 		seek_io = STAILQ_FIRST(&g_job.seek_queue);
692 		assert(seek_io != NULL);
693 		g_job.outstanding++;
694 		rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
695 					 g_job.input.pos / g_job.input.block_size,
696 					 _dd_bdev_seek_data_done, seek_io);
697 
698 		if (rc != 0) {
699 			SPDK_ERRLOG("%s\n", strerror(-rc));
700 			assert(g_job.outstanding > 0);
701 			g_job.outstanding--;
702 			g_error = rc;
703 			if (g_job.outstanding == 0) {
704 				dd_exit(rc);
705 			}
706 		}
707 	}
708 }
709 
710 static void
711 dd_target_seek(struct dd_io *io)
712 {
713 	struct dd_target *target = &g_job.input;
714 	uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size;
715 	uint64_t read_offset = g_job.input.pos - read_region_start;
716 	off_t next_data_offset = (off_t) -1;
717 	off_t next_hole_offset = (off_t) -1;
718 	int rc = 0;
719 
720 	if (!g_opts.sparse) {
721 		dd_target_populate_buffer(io);
722 		return;
723 	}
724 
725 	if (g_job.copy_size - read_offset == 0 || g_error != 0 || g_interrupt == true) {
726 		if (g_job.outstanding == 0) {
727 			if (g_error == 0) {
728 				dd_show_progress(true);
729 				printf("\n\n");
730 			}
731 			dd_exit(g_error);
732 		}
733 		return;
734 	}
735 
736 	if (target->type == DD_TARGET_TYPE_FILE) {
737 		next_data_offset = dd_file_seek_data();
738 		if (next_data_offset < 0) {
739 			return;
740 		} else if ((uint64_t)next_data_offset > g_job.input.pos) {
741 			g_job.input.pos = next_data_offset;
742 		}
743 
744 		next_hole_offset = dd_file_seek_hole();
745 		if (next_hole_offset < 0) {
746 			return;
747 		} else if ((uint64_t)next_hole_offset > g_job.input.pos) {
748 			io->length = spdk_min((uint64_t)g_opts.io_unit_size,
749 					      (uint64_t)(next_hole_offset - g_job.input.pos));
750 		} else {
751 			io->length = g_opts.io_unit_size;
752 		}
753 
754 		dd_target_populate_buffer(io);
755 	} else if (target->type == DD_TARGET_TYPE_BDEV) {
756 		/* Check if other seek operation is in progress */
757 		if (STAILQ_EMPTY(&g_job.seek_queue)) {
758 			g_job.outstanding++;
759 			rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch,
760 						 g_job.input.pos / g_job.input.block_size,
761 						 _dd_bdev_seek_data_done, io);
762 
763 		}
764 
765 		STAILQ_INSERT_TAIL(&g_job.seek_queue, io, link);
766 	}
767 
768 	if (rc != 0) {
769 		SPDK_ERRLOG("%s\n", strerror(-rc));
770 		assert(g_job.outstanding > 0);
771 		g_job.outstanding--;
772 		g_error = rc;
773 		if (g_job.outstanding == 0) {
774 			dd_exit(rc);
775 		}
776 		return;
777 	}
778 }
779 
780 static void
781 dd_complete_poll(struct dd_io *io)
782 {
783 	assert(g_job.outstanding > 0);
784 	g_job.outstanding--;
785 
786 	switch (io->type) {
787 	case DD_POPULATE:
788 		dd_target_read(io);
789 		break;
790 	case DD_READ:
791 		dd_target_write(io);
792 		break;
793 	case DD_WRITE:
794 		dd_target_seek(io);
795 		break;
796 	default:
797 		assert(false);
798 		break;
799 	}
800 }
801 
802 #ifdef SPDK_CONFIG_URING
803 static int
804 dd_uring_poll(void *ctx)
805 {
806 	struct io_uring_cqe *cqe;
807 	struct dd_io *io;
808 	int rc = 0;
809 	int i;
810 
811 	for (i = 0; i < (int)g_opts.queue_depth; i++) {
812 		rc = io_uring_peek_cqe(&g_job.u.uring.ring, &cqe);
813 		if (rc == 0) {
814 			if (cqe->res == -EAGAIN) {
815 				continue;
816 			} else if (cqe->res < 0) {
817 				SPDK_ERRLOG("%s\n", strerror(-cqe->res));
818 				g_error = cqe->res;
819 			}
820 
821 			io = io_uring_cqe_get_data(cqe);
822 			io_uring_cqe_seen(&g_job.u.uring.ring, cqe);
823 
824 			dd_complete_poll(io);
825 		} else if (rc != - EAGAIN) {
826 			SPDK_ERRLOG("%s\n", strerror(-rc));
827 			g_error = rc;
828 		}
829 	}
830 
831 	return rc;
832 }
833 #endif
834 
835 static int
836 dd_aio_poll(void *ctx)
837 {
838 	struct io_event events[32];
839 	int rc = 0;
840 	int i;
841 	struct timespec timeout;
842 	struct dd_io *io;
843 
844 	timeout.tv_sec = 0;
845 	timeout.tv_nsec = 0;
846 
847 	rc = io_getevents(g_job.u.aio.io_ctx, 0, 32, events, &timeout);
848 
849 	if (rc < 0) {
850 		SPDK_ERRLOG("%s\n", strerror(-rc));
851 		dd_exit(rc);
852 	}
853 
854 	for (i = 0; i < rc; i++) {
855 		io = events[i].data;
856 		if (events[i].res != io->length) {
857 			g_error = -ENOSPC;
858 		}
859 
860 		dd_complete_poll(io);
861 	}
862 
863 	return rc;
864 }
865 
866 static int
867 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 	if (g_opts.aio == false) {
874 		fd = &target->u.uring.fd;
875 	} else
876 #endif
877 	{
878 		fd = &target->u.aio.fd;
879 	}
880 
881 	flags |= O_RDWR;
882 
883 	if (input == false && ((flags & O_DIRECTORY) == 0)) {
884 		flags |= O_CREAT;
885 	}
886 
887 	if (input == false && ((flags & O_APPEND) == 0)) {
888 		flags |= O_TRUNC;
889 	}
890 
891 	target->type = DD_TARGET_TYPE_FILE;
892 	*fd = open(fname, flags, 0600);
893 	if (*fd < 0) {
894 		SPDK_ERRLOG("Could not open file %s: %s\n", fname, strerror(errno));
895 		return *fd;
896 	}
897 
898 	target->block_size = spdk_max(spdk_fd_get_blocklen(*fd), 1);
899 
900 	target->total_size = spdk_fd_get_size(*fd);
901 	if (target->total_size == 0) {
902 		target->total_size = g_opts.io_unit_size * g_opts.io_unit_count;
903 	}
904 
905 	if (input == true) {
906 		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 	if (g_opts.io_unit_count != 0) {
911 		g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
912 	}
913 
914 	return 0;
915 }
916 
917 static void
918 dd_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
919 		 void *event_ctx)
920 {
921 	SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
922 }
923 
924 static int
925 dd_open_bdev(struct dd_target *target, const char *bdev_name, uint64_t skip_blocks)
926 {
927 	int rc;
928 
929 	target->type = DD_TARGET_TYPE_BDEV;
930 
931 	rc = spdk_bdev_open_ext(bdev_name, true, dd_bdev_event_cb, NULL, &target->u.bdev.desc);
932 	if (rc < 0) {
933 		SPDK_ERRLOG("Could not open bdev %s: %s\n", bdev_name, strerror(-rc));
934 		return rc;
935 	}
936 
937 	target->u.bdev.bdev = spdk_bdev_desc_get_bdev(target->u.bdev.desc);
938 	target->open = true;
939 
940 	target->u.bdev.ch = spdk_bdev_get_io_channel(target->u.bdev.desc);
941 	if (target->u.bdev.ch == NULL) {
942 		spdk_bdev_close(target->u.bdev.desc);
943 		SPDK_ERRLOG("Could not get I/O channel: %s\n", strerror(ENOMEM));
944 		return -ENOMEM;
945 	}
946 
947 	target->block_size = spdk_bdev_get_block_size(target->u.bdev.bdev);
948 	target->total_size = spdk_bdev_get_num_blocks(target->u.bdev.bdev) * target->block_size;
949 
950 	g_opts.queue_depth = spdk_min(g_opts.queue_depth,
951 				      (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
952 
953 	if (g_opts.io_unit_count != 0) {
954 		g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
955 	}
956 
957 	return 0;
958 }
959 
960 static void
961 dd_finish(void)
962 {
963 	/* Interrupt operation */
964 	g_interrupt = true;
965 }
966 
967 static int
968 parse_flags(char *file_flags)
969 {
970 	char *input_flag;
971 	int flags = 0;
972 	int i;
973 	bool found = false;
974 
975 	/* Translate input flags to file open flags */
976 	while ((input_flag = strsep(&file_flags, ","))) {
977 		for (i = 0; g_flags[i].name != NULL; i++) {
978 			if (!strcmp(input_flag, g_flags[i].name)) {
979 				flags |= g_flags[i].flag;
980 				found = true;
981 				break;
982 			}
983 		}
984 
985 		if (found == false) {
986 			SPDK_ERRLOG("Unknown file flag: %s\n", input_flag);
987 			dd_exit(-EINVAL);
988 			return 0;
989 		}
990 
991 		found = false;
992 	}
993 
994 	return flags;
995 }
996 
997 #ifdef SPDK_CONFIG_URING
998 static bool
999 dd_is_blk(int fd)
1000 {
1001 	struct stat st;
1002 
1003 	if (fstat(fd, &st) != 0) {
1004 		return false;
1005 	}
1006 
1007 	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 dd_uring_init(void *arg)
1017 {
1018 	struct dd_uring_init_ctx *ctx = arg;
1019 
1020 	ctx->rc = io_uring_queue_init(g_opts.queue_depth * 2, &g_job.u.uring.ring, ctx->io_uring_flags);
1021 	return ctx;
1022 }
1023 
1024 static int
1025 dd_register_files(void)
1026 {
1027 	int fds[2];
1028 	unsigned count = 0;
1029 
1030 	if (g_opts.input_file) {
1031 		fds[count] = g_job.input.u.uring.fd;
1032 		g_job.input.u.uring.idx = count;
1033 		count++;
1034 	}
1035 
1036 	if (g_opts.output_file) {
1037 		fds[count] = g_job.output.u.uring.fd;
1038 		g_job.output.u.uring.idx = count;
1039 		count++;
1040 	}
1041 
1042 	return io_uring_register_files(&g_job.u.uring.ring, fds, count);
1043 
1044 }
1045 
1046 static int
1047 dd_register_buffers(void)
1048 {
1049 	struct iovec *iovs;
1050 	int i, rc;
1051 
1052 	iovs = calloc(g_opts.queue_depth, sizeof(struct iovec));
1053 	if (iovs == NULL) {
1054 		return -ENOMEM;
1055 	}
1056 
1057 	for (i = 0; i < (int)g_opts.queue_depth; i++) {
1058 		iovs[i].iov_base = g_job.ios[i].buf;
1059 		iovs[i].iov_len = g_opts.io_unit_size;
1060 		g_job.ios[i].idx = i;
1061 	}
1062 
1063 	rc = io_uring_register_buffers(&g_job.u.uring.ring, iovs, g_opts.queue_depth);
1064 
1065 	free(iovs);
1066 	return rc;
1067 }
1068 #endif
1069 
1070 static void
1071 dd_run(void *arg1)
1072 {
1073 	uint64_t write_size;
1074 	uint32_t i;
1075 	int rc, flags = 0;
1076 
1077 	if (g_opts.input_file) {
1078 		if (g_opts.input_file_flags) {
1079 			flags = parse_flags(g_opts.input_file_flags);
1080 		}
1081 
1082 		if (dd_open_file(&g_job.input, g_opts.input_file, flags, g_opts.input_offset, true) < 0) {
1083 			SPDK_ERRLOG("%s: %s\n", g_opts.input_file, strerror(errno));
1084 			dd_exit(-errno);
1085 			return;
1086 		}
1087 	} else if (g_opts.input_bdev) {
1088 		rc = dd_open_bdev(&g_job.input, g_opts.input_bdev, g_opts.input_offset);
1089 		if (rc < 0) {
1090 			SPDK_ERRLOG("%s: %s\n", g_opts.input_bdev, strerror(-rc));
1091 			dd_exit(rc);
1092 			return;
1093 		}
1094 	}
1095 
1096 	write_size = g_opts.io_unit_count * g_opts.io_unit_size;
1097 	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 	if (g_opts.input_bdev && g_job.input.pos > g_job.input.total_size) {
1102 		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 		dd_exit(-ENOSPC);
1105 		return;
1106 	}
1107 
1108 	if (g_opts.io_unit_count != 0 && g_opts.input_bdev &&
1109 	    write_size + g_job.input.pos > g_job.input.total_size) {
1110 		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 		dd_exit(-ENOSPC);
1113 		return;
1114 	}
1115 
1116 	if (g_opts.io_unit_count != 0) {
1117 		g_job.copy_size = write_size;
1118 	} else {
1119 		g_job.copy_size = g_job.input.total_size - g_job.input.pos;
1120 	}
1121 
1122 	g_job.output.pos = g_opts.output_offset * g_opts.io_unit_size;
1123 
1124 	if (g_opts.output_file) {
1125 		flags = 0;
1126 
1127 		if (g_opts.output_file_flags) {
1128 			flags = parse_flags(g_opts.output_file_flags);
1129 		}
1130 
1131 		if (dd_open_file(&g_job.output, g_opts.output_file, flags, g_opts.output_offset, false) < 0) {
1132 			SPDK_ERRLOG("%s: %s\n", g_opts.output_file, strerror(errno));
1133 			dd_exit(-errno);
1134 			return;
1135 		}
1136 	} else if (g_opts.output_bdev) {
1137 		rc = dd_open_bdev(&g_job.output, g_opts.output_bdev, g_opts.output_offset);
1138 		if (rc < 0) {
1139 			SPDK_ERRLOG("%s: %s\n", g_opts.output_bdev, strerror(-rc));
1140 			dd_exit(rc);
1141 			return;
1142 		}
1143 
1144 		if (g_job.output.pos > g_job.output.total_size) {
1145 			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 			dd_exit(-ENOSPC);
1148 			return;
1149 		}
1150 
1151 		if (g_opts.io_unit_count != 0 && write_size + g_job.output.pos > g_job.output.total_size) {
1152 			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 			dd_exit(-ENOSPC);
1155 			return;
1156 		}
1157 	}
1158 
1159 	if ((g_job.output.block_size > g_opts.io_unit_size) ||
1160 	    (g_job.input.block_size > g_opts.io_unit_size)) {
1161 		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 		dd_exit(-EINVAL);
1164 		return;
1165 	}
1166 
1167 	if (g_opts.input_bdev && g_opts.io_unit_size % g_job.input.block_size != 0) {
1168 		SPDK_ERRLOG("--bs value must be a multiple of input native block size (%d)\n",
1169 			    g_job.input.block_size);
1170 		dd_exit(-EINVAL);
1171 		return;
1172 	}
1173 
1174 	g_job.ios = calloc(g_opts.queue_depth, sizeof(struct dd_io));
1175 	if (g_job.ios == NULL) {
1176 		SPDK_ERRLOG("%s\n", strerror(ENOMEM));
1177 		dd_exit(-ENOMEM);
1178 		return;
1179 	}
1180 
1181 	for (i = 0; i < g_opts.queue_depth; i++) {
1182 		g_job.ios[i].buf = spdk_malloc(g_opts.io_unit_size, 0x1000, NULL, 0, SPDK_MALLOC_DMA);
1183 		if (g_job.ios[i].buf == NULL) {
1184 			SPDK_ERRLOG("%s - try smaller block size value\n", strerror(ENOMEM));
1185 			dd_exit(-ENOMEM);
1186 			return;
1187 		}
1188 		g_job.ios[i].length = (uint64_t)g_opts.io_unit_size;
1189 	}
1190 
1191 	if (g_opts.input_file || g_opts.output_file) {
1192 #ifdef SPDK_CONFIG_URING
1193 		if (g_opts.aio == false) {
1194 			struct dd_uring_init_ctx ctx;
1195 			int flags = parse_flags(g_opts.input_file_flags) & parse_flags(g_opts.output_file_flags);
1196 
1197 			ctx.io_uring_flags = IORING_SETUP_SQPOLL;
1198 			if ((flags & O_DIRECT) != 0 &&
1199 			    dd_is_blk(g_job.input.u.uring.fd) &&
1200 			    dd_is_blk(g_job.output.u.uring.fd)) {
1201 				ctx.io_uring_flags = IORING_SETUP_IOPOLL;
1202 			}
1203 
1204 			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 			if (spdk_call_unaffinitized(dd_uring_init, &ctx) == NULL || ctx.rc) {
1209 				SPDK_ERRLOG("Failed to create io_uring: %d (%s)\n", ctx.rc, spdk_strerror(-ctx.rc));
1210 				dd_exit(ctx.rc);
1211 				return;
1212 			}
1213 			g_job.u.uring.active = true;
1214 
1215 			/* Register the files */
1216 			rc = dd_register_files();
1217 			if (rc) {
1218 				SPDK_ERRLOG("Failed to register files with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1219 				dd_exit(rc);
1220 				return;
1221 			}
1222 
1223 			/* Register the buffers */
1224 			rc = dd_register_buffers();
1225 			if (rc) {
1226 				SPDK_ERRLOG("Failed to register buffers with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1227 				dd_exit(rc);
1228 				return;
1229 			}
1230 
1231 		} else
1232 #endif
1233 		{
1234 			g_job.u.aio.poller = SPDK_POLLER_REGISTER(dd_aio_poll, NULL, 0);
1235 			io_setup(g_opts.queue_depth, &g_job.u.aio.io_ctx);
1236 		}
1237 	}
1238 
1239 	clock_gettime(CLOCK_REALTIME, &g_job.start_time);
1240 
1241 	g_job.status_poller = SPDK_POLLER_REGISTER(dd_status_poller, NULL,
1242 			      STATUS_POLLER_PERIOD_SEC * SPDK_SEC_TO_USEC);
1243 
1244 	STAILQ_INIT(&g_job.seek_queue);
1245 
1246 	for (i = 0; i < g_opts.queue_depth; i++) {
1247 		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 usage(void)
1354 {
1355 	printf("[--------- DD Options ---------]\n");
1356 	printf(" --if Input file. Must specify either --if or --ib.\n");
1357 	printf(" --ib Input bdev. Must specifier either --if or --ib\n");
1358 	printf(" --of Output file. Must specify either --of or --ob.\n");
1359 	printf(" --ob Output bdev. Must specify either --of or --ob.\n");
1360 	printf(" --iflag Input file flags.\n");
1361 	printf(" --oflag Output file flags.\n");
1362 	printf(" --bs I/O unit size (default: %" PRId64 ")\n", g_opts.io_unit_size);
1363 	printf(" --qd Queue depth (default: %d)\n", g_opts.queue_depth);
1364 	printf(" --count I/O unit count. The number of I/O units to copy. (default: all)\n");
1365 	printf(" --skip Skip this many I/O units at start of input. (default: 0)\n");
1366 	printf(" --seek Skip this many I/O units at start of output. (default: 0)\n");
1367 	printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n");
1368 	printf(" --sparse Enable hole skipping in input target\n");
1369 	printf(" Available iflag and oflag values:\n");
1370 	printf("  append - append mode\n");
1371 	printf("  direct - use direct I/O for data\n");
1372 	printf("  directory - fail unless a directory\n");
1373 	printf("  dsync - use synchronized I/O for data\n");
1374 	printf("  noatime - do not update access time\n");
1375 	printf("  noctty - do not assign controlling terminal from file\n");
1376 	printf("  nofollow - do not follow symlinks\n");
1377 	printf("  nonblock - use non-blocking I/O\n");
1378 	printf("  sync - use synchronized I/O for data and metadata\n");
1379 }
1380 
1381 static int
1382 parse_args(int argc, char *argv)
1383 {
1384 	switch (argc) {
1385 	case DD_OPTION_IF:
1386 		g_opts.input_file = strdup(argv);
1387 		break;
1388 	case DD_OPTION_OF:
1389 		g_opts.output_file = strdup(argv);
1390 		break;
1391 	case DD_OPTION_IFLAGS:
1392 		g_opts.input_file_flags = strdup(argv);
1393 		break;
1394 	case DD_OPTION_OFLAGS:
1395 		g_opts.output_file_flags = strdup(argv);
1396 		break;
1397 	case DD_OPTION_IB:
1398 		g_opts.input_bdev = strdup(argv);
1399 		break;
1400 	case DD_OPTION_OB:
1401 		g_opts.output_bdev = strdup(argv);
1402 		break;
1403 	case DD_OPTION_SKIP:
1404 		g_opts.input_offset = spdk_strtol(optarg, 10);
1405 		break;
1406 	case DD_OPTION_SEEK:
1407 		g_opts.output_offset = spdk_strtol(optarg, 10);
1408 		break;
1409 	case DD_OPTION_BS:
1410 		g_opts.io_unit_size = spdk_strtol(optarg, 10);
1411 		break;
1412 	case DD_OPTION_QD:
1413 		g_opts.queue_depth = spdk_strtol(optarg, 10);
1414 		break;
1415 	case DD_OPTION_COUNT:
1416 		g_opts.io_unit_count = spdk_strtol(optarg, 10);
1417 		break;
1418 	case DD_OPTION_AIO:
1419 		g_opts.aio = true;
1420 		break;
1421 	case DD_OPTION_SPARSE:
1422 		g_opts.sparse = true;
1423 		break;
1424 	default:
1425 		usage();
1426 		return 1;
1427 	}
1428 	return 0;
1429 }
1430 
1431 static void
1432 dd_free(void)
1433 {
1434 	uint32_t i;
1435 
1436 	free(g_opts.input_file);
1437 	free(g_opts.output_file);
1438 	free(g_opts.input_bdev);
1439 	free(g_opts.output_bdev);
1440 	free(g_opts.input_file_flags);
1441 	free(g_opts.output_file_flags);
1442 
1443 
1444 	if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
1445 #ifdef SPDK_CONFIG_URING
1446 		if (g_opts.aio == false) {
1447 			if (g_job.u.uring.active) {
1448 				io_uring_unregister_files(&g_job.u.uring.ring);
1449 				io_uring_queue_exit(&g_job.u.uring.ring);
1450 			}
1451 		} else
1452 #endif
1453 		{
1454 			io_destroy(g_job.u.aio.io_ctx);
1455 		}
1456 	}
1457 
1458 	if (g_job.ios) {
1459 		for (i = 0; i < g_opts.queue_depth; i++) {
1460 			spdk_free(g_job.ios[i].buf);
1461 		}
1462 
1463 		free(g_job.ios);
1464 	}
1465 }
1466 
1467 int
1468 main(int argc, char **argv)
1469 {
1470 	struct spdk_app_opts opts = {};
1471 	int rc = 1;
1472 
1473 	spdk_app_opts_init(&opts, sizeof(opts));
1474 	opts.name = "spdk_dd";
1475 	opts.reactor_mask = "0x1";
1476 	opts.shutdown_cb = dd_finish;
1477 	opts.rpc_addr = NULL;
1478 	rc = spdk_app_parse_args(argc, argv, &opts, "", g_cmdline_opts, parse_args, usage);
1479 	if (rc == SPDK_APP_PARSE_ARGS_FAIL) {
1480 		SPDK_ERRLOG("Invalid arguments\n");
1481 		goto end;
1482 	} else if (rc == SPDK_APP_PARSE_ARGS_HELP) {
1483 		goto end;
1484 	}
1485 
1486 	if (g_opts.input_file != NULL && g_opts.input_bdev != NULL) {
1487 		SPDK_ERRLOG("You may specify either --if or --ib, but not both.\n");
1488 		rc = EINVAL;
1489 		goto end;
1490 	}
1491 
1492 	if (g_opts.output_file != NULL && g_opts.output_bdev != NULL) {
1493 		SPDK_ERRLOG("You may specify either --of or --ob, but not both.\n");
1494 		rc = EINVAL;
1495 		goto end;
1496 	}
1497 
1498 	if (g_opts.input_file == NULL && g_opts.input_bdev == NULL) {
1499 		SPDK_ERRLOG("You must specify either --if or --ib\n");
1500 		rc = EINVAL;
1501 		goto end;
1502 	}
1503 
1504 	if (g_opts.output_file == NULL && g_opts.output_bdev == NULL) {
1505 		SPDK_ERRLOG("You must specify either --of or --ob\n");
1506 		rc = EINVAL;
1507 		goto end;
1508 	}
1509 
1510 	if (g_opts.io_unit_size <= 0) {
1511 		SPDK_ERRLOG("Invalid --bs value\n");
1512 		rc = EINVAL;
1513 		goto end;
1514 	}
1515 
1516 	if (g_opts.io_unit_count < 0) {
1517 		SPDK_ERRLOG("Invalid --count value\n");
1518 		rc = EINVAL;
1519 		goto end;
1520 	}
1521 
1522 	if (g_opts.output_file == NULL && g_opts.output_file_flags != NULL) {
1523 		SPDK_ERRLOG("--oflags may be used only with --of\n");
1524 		rc = EINVAL;
1525 		goto end;
1526 	}
1527 
1528 	if (g_opts.input_file == NULL && g_opts.input_file_flags != NULL) {
1529 		SPDK_ERRLOG("--iflags may be used only with --if\n");
1530 		rc = EINVAL;
1531 		goto end;
1532 	}
1533 
1534 	rc = spdk_app_start(&opts, dd_run, NULL);
1535 	if (rc) {
1536 		SPDK_ERRLOG("Error occurred while performing copy\n");
1537 	}
1538 
1539 	dd_free();
1540 	spdk_app_fini();
1541 
1542 end:
1543 	return rc;
1544 }
1545