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