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