xref: /spdk/app/spdk_dd/spdk_dd.c (revision 8d3947977640da882a3cdcc21a7575115b7e7787)
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 == -EAGAIN) {
814 			break;
815 		}
816 		assert(cqe != NULL);
817 
818 		io = io_uring_cqe_get_data(cqe);
819 		if (cqe->res < 0) {
820 			SPDK_ERRLOG("%s\n", strerror(-cqe->res));
821 			dd_exit(cqe->res);
822 		}
823 
824 		io_uring_cqe_seen(&g_job.u.uring.ring, cqe);
825 		dd_complete_poll(io);
826 	}
827 
828 	return (i ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE);
829 }
830 
831 #endif
832 
833 static int
834 dd_aio_poll(void *ctx)
835 {
836 	struct io_event events[32];
837 	int rc = 0;
838 	int i;
839 	struct timespec timeout;
840 	struct dd_io *io;
841 
842 	timeout.tv_sec = 0;
843 	timeout.tv_nsec = 0;
844 
845 	rc = io_getevents(g_job.u.aio.io_ctx, 0, 32, events, &timeout);
846 
847 	if (rc < 0) {
848 		SPDK_ERRLOG("%s\n", strerror(-rc));
849 		dd_exit(rc);
850 	}
851 
852 	for (i = 0; i < rc; i++) {
853 		io = events[i].data;
854 		if (events[i].res != io->length) {
855 			g_error = -ENOSPC;
856 		}
857 
858 		dd_complete_poll(io);
859 	}
860 
861 	return (i ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE);
862 }
863 
864 static int
865 dd_open_file(struct dd_target *target, const char *fname, int flags, uint64_t skip_blocks,
866 	     bool input)
867 {
868 	int *fd;
869 
870 #ifdef SPDK_CONFIG_URING
871 	if (g_opts.aio == false) {
872 		fd = &target->u.uring.fd;
873 	} else
874 #endif
875 	{
876 		fd = &target->u.aio.fd;
877 	}
878 
879 	flags |= O_RDWR;
880 
881 	if (input == false && ((flags & O_DIRECTORY) == 0)) {
882 		flags |= O_CREAT;
883 	}
884 
885 	if (input == false && ((flags & O_APPEND) == 0)) {
886 		flags |= O_TRUNC;
887 	}
888 
889 	target->type = DD_TARGET_TYPE_FILE;
890 	*fd = open(fname, flags, 0600);
891 	if (*fd < 0) {
892 		SPDK_ERRLOG("Could not open file %s: %s\n", fname, strerror(errno));
893 		return *fd;
894 	}
895 
896 	target->block_size = spdk_max(spdk_fd_get_blocklen(*fd), 1);
897 
898 	target->total_size = spdk_fd_get_size(*fd);
899 	if (target->total_size == 0) {
900 		target->total_size = g_opts.io_unit_size * g_opts.io_unit_count;
901 	}
902 
903 	if (input == true) {
904 		g_opts.queue_depth = spdk_min(g_opts.queue_depth,
905 					      (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
906 	}
907 
908 	if (g_opts.io_unit_count != 0) {
909 		g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
910 	}
911 
912 	return 0;
913 }
914 
915 static void
916 dd_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
917 		 void *event_ctx)
918 {
919 	SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
920 }
921 
922 static int
923 dd_open_bdev(struct dd_target *target, const char *bdev_name, uint64_t skip_blocks)
924 {
925 	int rc;
926 
927 	target->type = DD_TARGET_TYPE_BDEV;
928 
929 	rc = spdk_bdev_open_ext(bdev_name, true, dd_bdev_event_cb, NULL, &target->u.bdev.desc);
930 	if (rc < 0) {
931 		SPDK_ERRLOG("Could not open bdev %s: %s\n", bdev_name, strerror(-rc));
932 		return rc;
933 	}
934 
935 	target->u.bdev.bdev = spdk_bdev_desc_get_bdev(target->u.bdev.desc);
936 	target->open = true;
937 
938 	target->u.bdev.ch = spdk_bdev_get_io_channel(target->u.bdev.desc);
939 	if (target->u.bdev.ch == NULL) {
940 		spdk_bdev_close(target->u.bdev.desc);
941 		SPDK_ERRLOG("Could not get I/O channel: %s\n", strerror(ENOMEM));
942 		return -ENOMEM;
943 	}
944 
945 	target->block_size = spdk_bdev_get_block_size(target->u.bdev.bdev);
946 	target->total_size = spdk_bdev_get_num_blocks(target->u.bdev.bdev) * target->block_size;
947 
948 	g_opts.queue_depth = spdk_min(g_opts.queue_depth,
949 				      (target->total_size / g_opts.io_unit_size) - skip_blocks + 1);
950 
951 	if (g_opts.io_unit_count != 0) {
952 		g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count);
953 	}
954 
955 	return 0;
956 }
957 
958 static void
959 dd_finish(void)
960 {
961 	/* Interrupt operation */
962 	g_interrupt = true;
963 }
964 
965 static int
966 parse_flags(char *file_flags)
967 {
968 	char *input_flag;
969 	int flags = 0;
970 	int i;
971 	bool found = false;
972 
973 	/* Translate input flags to file open flags */
974 	while ((input_flag = strsep(&file_flags, ","))) {
975 		for (i = 0; g_flags[i].name != NULL; i++) {
976 			if (!strcmp(input_flag, g_flags[i].name)) {
977 				flags |= g_flags[i].flag;
978 				found = true;
979 				break;
980 			}
981 		}
982 
983 		if (found == false) {
984 			SPDK_ERRLOG("Unknown file flag: %s\n", input_flag);
985 			dd_exit(-EINVAL);
986 			return 0;
987 		}
988 
989 		found = false;
990 	}
991 
992 	return flags;
993 }
994 
995 #ifdef SPDK_CONFIG_URING
996 static bool
997 dd_is_blk(int fd)
998 {
999 	struct stat st;
1000 
1001 	if (fstat(fd, &st) != 0) {
1002 		return false;
1003 	}
1004 
1005 	return S_ISBLK(st.st_mode);
1006 }
1007 
1008 struct dd_uring_init_ctx {
1009 	unsigned int io_uring_flags;
1010 	int rc;
1011 };
1012 
1013 static void *
1014 dd_uring_init(void *arg)
1015 {
1016 	struct dd_uring_init_ctx *ctx = arg;
1017 
1018 	ctx->rc = io_uring_queue_init(g_opts.queue_depth * 2, &g_job.u.uring.ring, ctx->io_uring_flags);
1019 	return ctx;
1020 }
1021 
1022 static int
1023 dd_register_files(void)
1024 {
1025 	int fds[2];
1026 	unsigned count = 0;
1027 
1028 	if (g_opts.input_file) {
1029 		fds[count] = g_job.input.u.uring.fd;
1030 		g_job.input.u.uring.idx = count;
1031 		count++;
1032 	}
1033 
1034 	if (g_opts.output_file) {
1035 		fds[count] = g_job.output.u.uring.fd;
1036 		g_job.output.u.uring.idx = count;
1037 		count++;
1038 	}
1039 
1040 	return io_uring_register_files(&g_job.u.uring.ring, fds, count);
1041 
1042 }
1043 
1044 static int
1045 dd_register_buffers(void)
1046 {
1047 	struct iovec *iovs;
1048 	int i, rc;
1049 
1050 	iovs = calloc(g_opts.queue_depth, sizeof(struct iovec));
1051 	if (iovs == NULL) {
1052 		return -ENOMEM;
1053 	}
1054 
1055 	for (i = 0; i < (int)g_opts.queue_depth; i++) {
1056 		iovs[i].iov_base = g_job.ios[i].buf;
1057 		iovs[i].iov_len = g_opts.io_unit_size;
1058 		g_job.ios[i].idx = i;
1059 	}
1060 
1061 	rc = io_uring_register_buffers(&g_job.u.uring.ring, iovs, g_opts.queue_depth);
1062 
1063 	free(iovs);
1064 	return rc;
1065 }
1066 #endif
1067 
1068 static void
1069 dd_run(void *arg1)
1070 {
1071 	uint64_t write_size;
1072 	uint32_t i;
1073 	int rc, flags = 0;
1074 
1075 	if (g_opts.input_file) {
1076 		if (g_opts.input_file_flags) {
1077 			flags = parse_flags(g_opts.input_file_flags);
1078 		}
1079 
1080 		if (dd_open_file(&g_job.input, g_opts.input_file, flags, g_opts.input_offset, true) < 0) {
1081 			SPDK_ERRLOG("%s: %s\n", g_opts.input_file, strerror(errno));
1082 			dd_exit(-errno);
1083 			return;
1084 		}
1085 	} else if (g_opts.input_bdev) {
1086 		rc = dd_open_bdev(&g_job.input, g_opts.input_bdev, g_opts.input_offset);
1087 		if (rc < 0) {
1088 			SPDK_ERRLOG("%s: %s\n", g_opts.input_bdev, strerror(-rc));
1089 			dd_exit(rc);
1090 			return;
1091 		}
1092 	}
1093 
1094 	write_size = g_opts.io_unit_count * g_opts.io_unit_size;
1095 	g_job.input.pos = g_opts.input_offset * g_opts.io_unit_size;
1096 
1097 	/* We cannot check write size for input files because /dev/zeros, /dev/random, etc would not work.
1098 	 * We will handle that during copying */
1099 	if (g_opts.input_bdev && g_job.input.pos > g_job.input.total_size) {
1100 		SPDK_ERRLOG("--skip value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in input\n",
1101 			    g_opts.input_offset, g_job.input.total_size / g_opts.io_unit_size);
1102 		dd_exit(-ENOSPC);
1103 		return;
1104 	}
1105 
1106 	if (g_opts.io_unit_count != 0 && g_opts.input_bdev &&
1107 	    write_size + g_job.input.pos > g_job.input.total_size) {
1108 		SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available from input\n",
1109 			    g_opts.io_unit_count, (g_job.input.total_size - g_job.input.pos) / g_opts.io_unit_size);
1110 		dd_exit(-ENOSPC);
1111 		return;
1112 	}
1113 
1114 	if (g_opts.io_unit_count != 0) {
1115 		g_job.copy_size = write_size;
1116 	} else {
1117 		g_job.copy_size = g_job.input.total_size - g_job.input.pos;
1118 	}
1119 
1120 	g_job.output.pos = g_opts.output_offset * g_opts.io_unit_size;
1121 
1122 	if (g_opts.output_file) {
1123 		flags = 0;
1124 
1125 		if (g_opts.output_file_flags) {
1126 			flags = parse_flags(g_opts.output_file_flags);
1127 		}
1128 
1129 		if (dd_open_file(&g_job.output, g_opts.output_file, flags, g_opts.output_offset, false) < 0) {
1130 			SPDK_ERRLOG("%s: %s\n", g_opts.output_file, strerror(errno));
1131 			dd_exit(-errno);
1132 			return;
1133 		}
1134 	} else if (g_opts.output_bdev) {
1135 		rc = dd_open_bdev(&g_job.output, g_opts.output_bdev, g_opts.output_offset);
1136 		if (rc < 0) {
1137 			SPDK_ERRLOG("%s: %s\n", g_opts.output_bdev, strerror(-rc));
1138 			dd_exit(rc);
1139 			return;
1140 		}
1141 
1142 		if (g_job.output.pos > g_job.output.total_size) {
1143 			SPDK_ERRLOG("--seek value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
1144 				    g_opts.output_offset, g_job.output.total_size / g_opts.io_unit_size);
1145 			dd_exit(-ENOSPC);
1146 			return;
1147 		}
1148 
1149 		if (g_opts.io_unit_count != 0 && write_size + g_job.output.pos > g_job.output.total_size) {
1150 			SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n",
1151 				    g_opts.io_unit_count, (g_job.output.total_size - g_job.output.pos) / g_opts.io_unit_size);
1152 			dd_exit(-ENOSPC);
1153 			return;
1154 		}
1155 	}
1156 
1157 	if ((g_job.output.block_size > g_opts.io_unit_size) ||
1158 	    (g_job.input.block_size > g_opts.io_unit_size)) {
1159 		SPDK_ERRLOG("--bs value cannot be less than input (%d) neither output (%d) native block size\n",
1160 			    g_job.input.block_size, g_job.output.block_size);
1161 		dd_exit(-EINVAL);
1162 		return;
1163 	}
1164 
1165 	if (g_opts.input_bdev && g_opts.io_unit_size % g_job.input.block_size != 0) {
1166 		SPDK_ERRLOG("--bs value must be a multiple of input native block size (%d)\n",
1167 			    g_job.input.block_size);
1168 		dd_exit(-EINVAL);
1169 		return;
1170 	}
1171 
1172 	g_job.ios = calloc(g_opts.queue_depth, sizeof(struct dd_io));
1173 	if (g_job.ios == NULL) {
1174 		SPDK_ERRLOG("%s\n", strerror(ENOMEM));
1175 		dd_exit(-ENOMEM);
1176 		return;
1177 	}
1178 
1179 	for (i = 0; i < g_opts.queue_depth; i++) {
1180 		g_job.ios[i].buf = spdk_malloc(g_opts.io_unit_size, 0x1000, NULL, 0, SPDK_MALLOC_DMA);
1181 		if (g_job.ios[i].buf == NULL) {
1182 			SPDK_ERRLOG("%s - try smaller block size value\n", strerror(ENOMEM));
1183 			dd_exit(-ENOMEM);
1184 			return;
1185 		}
1186 		g_job.ios[i].length = (uint64_t)g_opts.io_unit_size;
1187 	}
1188 
1189 	if (g_opts.input_file || g_opts.output_file) {
1190 #ifdef SPDK_CONFIG_URING
1191 		if (g_opts.aio == false) {
1192 			struct dd_uring_init_ctx ctx;
1193 			int flags = parse_flags(g_opts.input_file_flags) & parse_flags(g_opts.output_file_flags);
1194 
1195 			ctx.io_uring_flags = IORING_SETUP_SQPOLL;
1196 			if ((flags & O_DIRECT) != 0 &&
1197 			    dd_is_blk(g_job.input.u.uring.fd) &&
1198 			    dd_is_blk(g_job.output.u.uring.fd)) {
1199 				ctx.io_uring_flags = IORING_SETUP_IOPOLL;
1200 			}
1201 
1202 			g_job.u.uring.poller = SPDK_POLLER_REGISTER(dd_uring_poll, NULL, 0);
1203 
1204 			/* Initialized uring kernel threads inherit parent process CPU mask, to avoid conflicting
1205 			 * with SPDK cores initialize uring without any affinity. */
1206 			if (spdk_call_unaffinitized(dd_uring_init, &ctx) == NULL || ctx.rc) {
1207 				SPDK_ERRLOG("Failed to create io_uring: %d (%s)\n", ctx.rc, spdk_strerror(-ctx.rc));
1208 				dd_exit(ctx.rc);
1209 				return;
1210 			}
1211 			g_job.u.uring.active = true;
1212 
1213 			/* Register the files */
1214 			rc = dd_register_files();
1215 			if (rc) {
1216 				SPDK_ERRLOG("Failed to register files with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1217 				dd_exit(rc);
1218 				return;
1219 			}
1220 
1221 			/* Register the buffers */
1222 			rc = dd_register_buffers();
1223 			if (rc) {
1224 				SPDK_ERRLOG("Failed to register buffers with io_uring: %d (%s)\n", rc, spdk_strerror(-rc));
1225 				dd_exit(rc);
1226 				return;
1227 			}
1228 
1229 		} else
1230 #endif
1231 		{
1232 			g_job.u.aio.poller = SPDK_POLLER_REGISTER(dd_aio_poll, NULL, 0);
1233 			io_setup(g_opts.queue_depth, &g_job.u.aio.io_ctx);
1234 		}
1235 	}
1236 
1237 	clock_gettime(CLOCK_REALTIME, &g_job.start_time);
1238 
1239 	g_job.status_poller = SPDK_POLLER_REGISTER(dd_status_poller, NULL,
1240 			      STATUS_POLLER_PERIOD_SEC * SPDK_SEC_TO_USEC);
1241 
1242 	STAILQ_INIT(&g_job.seek_queue);
1243 
1244 	for (i = 0; i < g_opts.queue_depth; i++) {
1245 		dd_target_seek(&g_job.ios[i]);
1246 	}
1247 
1248 }
1249 
1250 enum dd_cmdline_opts {
1251 	DD_OPTION_IF = 0x1000,
1252 	DD_OPTION_OF,
1253 	DD_OPTION_IFLAGS,
1254 	DD_OPTION_OFLAGS,
1255 	DD_OPTION_IB,
1256 	DD_OPTION_OB,
1257 	DD_OPTION_SKIP,
1258 	DD_OPTION_SEEK,
1259 	DD_OPTION_BS,
1260 	DD_OPTION_QD,
1261 	DD_OPTION_COUNT,
1262 	DD_OPTION_AIO,
1263 	DD_OPTION_SPARSE,
1264 };
1265 
1266 static struct option g_cmdline_opts[] = {
1267 	{
1268 		.name = "if",
1269 		.has_arg = 1,
1270 		.flag = NULL,
1271 		.val = DD_OPTION_IF,
1272 	},
1273 	{
1274 		.name = "of",
1275 		.has_arg = 1,
1276 		.flag = NULL,
1277 		.val = DD_OPTION_OF,
1278 	},
1279 	{
1280 		.name = "iflag",
1281 		.has_arg = 1,
1282 		.flag = NULL,
1283 		.val = DD_OPTION_IFLAGS,
1284 	},
1285 	{
1286 		.name = "oflag",
1287 		.has_arg = 1,
1288 		.flag = NULL,
1289 		.val = DD_OPTION_OFLAGS,
1290 	},
1291 	{
1292 		.name = "ib",
1293 		.has_arg = 1,
1294 		.flag = NULL,
1295 		.val = DD_OPTION_IB,
1296 	},
1297 	{
1298 		.name = "ob",
1299 		.has_arg = 1,
1300 		.flag = NULL,
1301 		.val = DD_OPTION_OB,
1302 	},
1303 	{
1304 		.name = "skip",
1305 		.has_arg = 1,
1306 		.flag = NULL,
1307 		.val = DD_OPTION_SKIP,
1308 	},
1309 	{
1310 		.name = "seek",
1311 		.has_arg = 1,
1312 		.flag = NULL,
1313 		.val = DD_OPTION_SEEK,
1314 	},
1315 	{
1316 		.name = "bs",
1317 		.has_arg = 1,
1318 		.flag = NULL,
1319 		.val = DD_OPTION_BS,
1320 	},
1321 	{
1322 		.name = "qd",
1323 		.has_arg = 1,
1324 		.flag = NULL,
1325 		.val = DD_OPTION_QD,
1326 	},
1327 	{
1328 		.name = "count",
1329 		.has_arg = 1,
1330 		.flag = NULL,
1331 		.val = DD_OPTION_COUNT,
1332 	},
1333 	{
1334 		.name = "aio",
1335 		.has_arg = 0,
1336 		.flag = NULL,
1337 		.val = DD_OPTION_AIO,
1338 	},
1339 	{
1340 		.name = "sparse",
1341 		.has_arg = 0,
1342 		.flag = NULL,
1343 		.val = DD_OPTION_SPARSE,
1344 	},
1345 	{
1346 		.name = NULL
1347 	}
1348 };
1349 
1350 static void
1351 usage(void)
1352 {
1353 	printf("[--------- DD Options ---------]\n");
1354 	printf(" --if Input file. Must specify either --if or --ib.\n");
1355 	printf(" --ib Input bdev. Must specifier either --if or --ib\n");
1356 	printf(" --of Output file. Must specify either --of or --ob.\n");
1357 	printf(" --ob Output bdev. Must specify either --of or --ob.\n");
1358 	printf(" --iflag Input file flags.\n");
1359 	printf(" --oflag Output file flags.\n");
1360 	printf(" --bs I/O unit size (default: %" PRId64 ")\n", g_opts.io_unit_size);
1361 	printf(" --qd Queue depth (default: %d)\n", g_opts.queue_depth);
1362 	printf(" --count I/O unit count. The number of I/O units to copy. (default: all)\n");
1363 	printf(" --skip Skip this many I/O units at start of input. (default: 0)\n");
1364 	printf(" --seek Skip this many I/O units at start of output. (default: 0)\n");
1365 	printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n");
1366 	printf(" --sparse Enable hole skipping in input target\n");
1367 	printf(" Available iflag and oflag values:\n");
1368 	printf("  append - append mode\n");
1369 	printf("  direct - use direct I/O for data\n");
1370 	printf("  directory - fail unless a directory\n");
1371 	printf("  dsync - use synchronized I/O for data\n");
1372 	printf("  noatime - do not update access time\n");
1373 	printf("  noctty - do not assign controlling terminal from file\n");
1374 	printf("  nofollow - do not follow symlinks\n");
1375 	printf("  nonblock - use non-blocking I/O\n");
1376 	printf("  sync - use synchronized I/O for data and metadata\n");
1377 }
1378 
1379 static int
1380 parse_args(int argc, char *argv)
1381 {
1382 	switch (argc) {
1383 	case DD_OPTION_IF:
1384 		g_opts.input_file = strdup(argv);
1385 		break;
1386 	case DD_OPTION_OF:
1387 		g_opts.output_file = strdup(argv);
1388 		break;
1389 	case DD_OPTION_IFLAGS:
1390 		g_opts.input_file_flags = strdup(argv);
1391 		break;
1392 	case DD_OPTION_OFLAGS:
1393 		g_opts.output_file_flags = strdup(argv);
1394 		break;
1395 	case DD_OPTION_IB:
1396 		g_opts.input_bdev = strdup(argv);
1397 		break;
1398 	case DD_OPTION_OB:
1399 		g_opts.output_bdev = strdup(argv);
1400 		break;
1401 	case DD_OPTION_SKIP:
1402 		g_opts.input_offset = spdk_strtol(optarg, 10);
1403 		break;
1404 	case DD_OPTION_SEEK:
1405 		g_opts.output_offset = spdk_strtol(optarg, 10);
1406 		break;
1407 	case DD_OPTION_BS:
1408 		g_opts.io_unit_size = spdk_strtol(optarg, 10);
1409 		break;
1410 	case DD_OPTION_QD:
1411 		g_opts.queue_depth = spdk_strtol(optarg, 10);
1412 		break;
1413 	case DD_OPTION_COUNT:
1414 		g_opts.io_unit_count = spdk_strtol(optarg, 10);
1415 		break;
1416 	case DD_OPTION_AIO:
1417 		g_opts.aio = true;
1418 		break;
1419 	case DD_OPTION_SPARSE:
1420 		g_opts.sparse = true;
1421 		break;
1422 	default:
1423 		usage();
1424 		return 1;
1425 	}
1426 	return 0;
1427 }
1428 
1429 static void
1430 dd_free(void)
1431 {
1432 	uint32_t i;
1433 
1434 	free(g_opts.input_file);
1435 	free(g_opts.output_file);
1436 	free(g_opts.input_bdev);
1437 	free(g_opts.output_bdev);
1438 	free(g_opts.input_file_flags);
1439 	free(g_opts.output_file_flags);
1440 
1441 
1442 	if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) {
1443 #ifdef SPDK_CONFIG_URING
1444 		if (g_opts.aio == false) {
1445 			if (g_job.u.uring.active) {
1446 				io_uring_unregister_files(&g_job.u.uring.ring);
1447 				io_uring_queue_exit(&g_job.u.uring.ring);
1448 			}
1449 		} else
1450 #endif
1451 		{
1452 			io_destroy(g_job.u.aio.io_ctx);
1453 		}
1454 	}
1455 
1456 	if (g_job.ios) {
1457 		for (i = 0; i < g_opts.queue_depth; i++) {
1458 			spdk_free(g_job.ios[i].buf);
1459 		}
1460 
1461 		free(g_job.ios);
1462 	}
1463 }
1464 
1465 int
1466 main(int argc, char **argv)
1467 {
1468 	struct spdk_app_opts opts = {};
1469 	int rc = 1;
1470 
1471 	spdk_app_opts_init(&opts, sizeof(opts));
1472 	opts.name = "spdk_dd";
1473 	opts.reactor_mask = "0x1";
1474 	opts.shutdown_cb = dd_finish;
1475 	opts.rpc_addr = NULL;
1476 	rc = spdk_app_parse_args(argc, argv, &opts, "", g_cmdline_opts, parse_args, usage);
1477 	if (rc == SPDK_APP_PARSE_ARGS_FAIL) {
1478 		SPDK_ERRLOG("Invalid arguments\n");
1479 		goto end;
1480 	} else if (rc == SPDK_APP_PARSE_ARGS_HELP) {
1481 		goto end;
1482 	}
1483 
1484 	if (g_opts.input_file != NULL && g_opts.input_bdev != NULL) {
1485 		SPDK_ERRLOG("You may specify either --if or --ib, but not both.\n");
1486 		rc = EINVAL;
1487 		goto end;
1488 	}
1489 
1490 	if (g_opts.output_file != NULL && g_opts.output_bdev != NULL) {
1491 		SPDK_ERRLOG("You may specify either --of or --ob, but not both.\n");
1492 		rc = EINVAL;
1493 		goto end;
1494 	}
1495 
1496 	if (g_opts.input_file == NULL && g_opts.input_bdev == NULL) {
1497 		SPDK_ERRLOG("You must specify either --if or --ib\n");
1498 		rc = EINVAL;
1499 		goto end;
1500 	}
1501 
1502 	if (g_opts.output_file == NULL && g_opts.output_bdev == NULL) {
1503 		SPDK_ERRLOG("You must specify either --of or --ob\n");
1504 		rc = EINVAL;
1505 		goto end;
1506 	}
1507 
1508 	if (g_opts.io_unit_size <= 0) {
1509 		SPDK_ERRLOG("Invalid --bs value\n");
1510 		rc = EINVAL;
1511 		goto end;
1512 	}
1513 
1514 	if (g_opts.io_unit_count < 0) {
1515 		SPDK_ERRLOG("Invalid --count value\n");
1516 		rc = EINVAL;
1517 		goto end;
1518 	}
1519 
1520 	if (g_opts.output_file == NULL && g_opts.output_file_flags != NULL) {
1521 		SPDK_ERRLOG("--oflags may be used only with --of\n");
1522 		rc = EINVAL;
1523 		goto end;
1524 	}
1525 
1526 	if (g_opts.input_file == NULL && g_opts.input_file_flags != NULL) {
1527 		SPDK_ERRLOG("--iflags may be used only with --if\n");
1528 		rc = EINVAL;
1529 		goto end;
1530 	}
1531 
1532 	rc = spdk_app_start(&opts, dd_run, NULL);
1533 	if (rc) {
1534 		SPDK_ERRLOG("Error occurred while performing copy\n");
1535 	}
1536 
1537 	dd_free();
1538 	spdk_app_fini();
1539 
1540 end:
1541 	return rc;
1542 }
1543