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