1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2020 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 #include "spdk/config.h" 8 9 #include "spdk/bdev.h" 10 #include "spdk/event.h" 11 #include "spdk/fd.h" 12 #include "spdk/string.h" 13 #include "spdk/util.h" 14 #include "spdk/vmd.h" 15 16 #include <libaio.h> 17 18 #ifdef SPDK_CONFIG_URING 19 #include <liburing.h> 20 #endif 21 22 #define TIMESPEC_TO_MS(time) ((time.tv_sec * 1000) + (time.tv_nsec / 1000000)) 23 #define STATUS_POLLER_PERIOD_SEC 1 24 25 struct spdk_dd_opts { 26 char *input_file; 27 char *output_file; 28 char *input_file_flags; 29 char *output_file_flags; 30 char *input_bdev; 31 char *output_bdev; 32 uint64_t input_offset; 33 uint64_t output_offset; 34 int64_t io_unit_size; 35 int64_t io_unit_count; 36 uint32_t queue_depth; 37 bool aio; 38 bool sparse; 39 }; 40 41 static struct spdk_dd_opts g_opts = { 42 .io_unit_size = 4096, 43 .queue_depth = 2, 44 }; 45 46 enum dd_submit_type { 47 DD_POPULATE, 48 DD_READ, 49 DD_WRITE, 50 }; 51 52 struct dd_io { 53 uint64_t offset; 54 uint64_t length; 55 struct iocb iocb; 56 enum dd_submit_type type; 57 #ifdef SPDK_CONFIG_URING 58 int idx; 59 #endif 60 void *buf; 61 STAILQ_ENTRY(dd_io) link; 62 }; 63 64 enum dd_target_type { 65 DD_TARGET_TYPE_FILE, 66 DD_TARGET_TYPE_BDEV, 67 }; 68 69 struct dd_target { 70 enum dd_target_type type; 71 72 union { 73 struct { 74 struct spdk_bdev *bdev; 75 struct spdk_bdev_desc *desc; 76 struct spdk_io_channel *ch; 77 } bdev; 78 79 #ifdef SPDK_CONFIG_URING 80 struct { 81 int fd; 82 int idx; 83 } uring; 84 #endif 85 struct { 86 int fd; 87 } aio; 88 } u; 89 90 /* Block size of underlying device. */ 91 uint32_t block_size; 92 93 /* Position of next I/O in bytes */ 94 uint64_t pos; 95 96 /* Total size of target in bytes */ 97 uint64_t total_size; 98 99 bool open; 100 }; 101 102 struct dd_job { 103 struct dd_target input; 104 struct dd_target output; 105 106 struct dd_io *ios; 107 108 union { 109 #ifdef SPDK_CONFIG_URING 110 struct { 111 struct io_uring ring; 112 bool active; 113 struct spdk_poller *poller; 114 } uring; 115 #endif 116 struct { 117 io_context_t io_ctx; 118 struct spdk_poller *poller; 119 } aio; 120 } u; 121 122 uint32_t outstanding; 123 uint64_t copy_size; 124 STAILQ_HEAD(, dd_io) seek_queue; 125 126 struct timespec start_time; 127 uint64_t total_bytes; 128 uint64_t incremental_bytes; 129 struct spdk_poller *status_poller; 130 }; 131 132 struct dd_flags { 133 char *name; 134 int flag; 135 }; 136 137 static struct dd_flags g_flags[] = { 138 {"append", O_APPEND}, 139 {"direct", O_DIRECT}, 140 {"directory", O_DIRECTORY}, 141 {"dsync", O_DSYNC}, 142 {"noatime", O_NOATIME}, 143 {"noctty", O_NOCTTY}, 144 {"nofollow", O_NOFOLLOW}, 145 {"nonblock", O_NONBLOCK}, 146 {"sync", O_SYNC}, 147 {NULL, 0} 148 }; 149 150 static struct dd_job g_job = {}; 151 static int g_error = 0; 152 static bool g_interrupt; 153 154 static void dd_target_seek(struct dd_io *io); 155 static void _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg); 156 157 static void 158 dd_cleanup_bdev(struct dd_target io) 159 { 160 /* This can be only on the error path. 161 * To prevent the SEGV, need add checks here. 162 */ 163 if (io.u.bdev.ch) { 164 spdk_put_io_channel(io.u.bdev.ch); 165 } 166 167 spdk_bdev_close(io.u.bdev.desc); 168 } 169 170 static void 171 dd_exit(int rc) 172 { 173 if (g_job.input.type == DD_TARGET_TYPE_FILE) { 174 #ifdef SPDK_CONFIG_URING 175 if (g_opts.aio == false) { 176 close(g_job.input.u.uring.fd); 177 } else 178 #endif 179 { 180 close(g_job.input.u.aio.fd); 181 } 182 } else if (g_job.input.type == DD_TARGET_TYPE_BDEV && g_job.input.open) { 183 dd_cleanup_bdev(g_job.input); 184 } 185 186 if (g_job.output.type == DD_TARGET_TYPE_FILE) { 187 #ifdef SPDK_CONFIG_URING 188 if (g_opts.aio == false) { 189 close(g_job.output.u.uring.fd); 190 } else 191 #endif 192 { 193 close(g_job.output.u.aio.fd); 194 } 195 } else if (g_job.output.type == DD_TARGET_TYPE_BDEV && g_job.output.open) { 196 dd_cleanup_bdev(g_job.output); 197 } 198 199 if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) { 200 #ifdef SPDK_CONFIG_URING 201 if (g_opts.aio == false) { 202 spdk_poller_unregister(&g_job.u.uring.poller); 203 } else 204 #endif 205 { 206 spdk_poller_unregister(&g_job.u.aio.poller); 207 } 208 } 209 210 spdk_poller_unregister(&g_job.status_poller); 211 212 spdk_app_stop(rc); 213 } 214 215 static void 216 dd_show_progress(bool finish) 217 { 218 char *unit_str[5] = {"", "k", "M", "G", "T"}; 219 char *speed_type_str[2] = {"", "average "}; 220 char *size_unit_str = ""; 221 char *speed_unit_str = ""; 222 char *speed_type; 223 uint64_t size_unit = 1; 224 uint64_t speed_unit = 1; 225 uint64_t speed, tmp_speed; 226 int i = 0; 227 uint64_t milliseconds; 228 uint64_t size, tmp_size; 229 230 size = g_job.incremental_bytes; 231 232 g_job.incremental_bytes = 0; 233 g_job.total_bytes += size; 234 235 if (finish) { 236 struct timespec time_now; 237 238 clock_gettime(CLOCK_REALTIME, &time_now); 239 240 milliseconds = spdk_max(1, TIMESPEC_TO_MS(time_now) - TIMESPEC_TO_MS(g_job.start_time)); 241 size = g_job.total_bytes; 242 } else { 243 milliseconds = STATUS_POLLER_PERIOD_SEC * 1000; 244 } 245 246 /* Find the right unit for size displaying (B vs kB vs MB vs GB vs TB) */ 247 tmp_size = size; 248 while (tmp_size > 1024 * 10) { 249 tmp_size >>= 10; 250 size_unit <<= 10; 251 size_unit_str = unit_str[++i]; 252 if (i == 4) { 253 break; 254 } 255 } 256 257 speed_type = finish ? speed_type_str[1] : speed_type_str[0]; 258 speed = (size * 1000) / milliseconds; 259 260 i = 0; 261 262 /* Find the right unit for speed displaying (Bps vs kBps vs MBps vs GBps vs TBps) */ 263 tmp_speed = speed; 264 while (tmp_speed > 1024 * 10) { 265 tmp_speed >>= 10; 266 speed_unit <<= 10; 267 speed_unit_str = unit_str[++i]; 268 if (i == 4) { 269 break; 270 } 271 } 272 273 printf("\33[2K\rCopying: %" PRIu64 "/%" PRIu64 " [%sB] (%s%" PRIu64 " %sBps)", 274 g_job.total_bytes / size_unit, g_job.copy_size / size_unit, size_unit_str, speed_type, 275 speed / speed_unit, speed_unit_str); 276 fflush(stdout); 277 } 278 279 static int 280 dd_status_poller(void *ctx) 281 { 282 dd_show_progress(false); 283 return SPDK_POLLER_BUSY; 284 } 285 286 static void 287 dd_finalize_output(void) 288 { 289 off_t curr_offset; 290 int rc = 0; 291 292 if (g_job.outstanding > 0) { 293 return; 294 } 295 296 if (g_opts.output_file) { 297 curr_offset = lseek(g_job.output.u.aio.fd, 0, SEEK_END); 298 if (curr_offset == (off_t) -1) { 299 SPDK_ERRLOG("Could not seek output file for finalize: %s\n", strerror(errno)); 300 g_error = errno; 301 } else if ((uint64_t)curr_offset < g_job.copy_size + g_job.output.pos) { 302 rc = ftruncate(g_job.output.u.aio.fd, g_job.copy_size + g_job.output.pos); 303 if (rc != 0) { 304 SPDK_ERRLOG("Could not truncate output file for finalize: %s\n", strerror(errno)); 305 g_error = errno; 306 } 307 } 308 } 309 310 if (g_error == 0) { 311 dd_show_progress(true); 312 printf("\n\n"); 313 } 314 dd_exit(g_error); 315 } 316 317 #ifdef SPDK_CONFIG_URING 318 static void 319 dd_uring_submit(struct dd_io *io, struct dd_target *target, uint64_t length, uint64_t offset) 320 { 321 struct io_uring_sqe *sqe; 322 323 sqe = io_uring_get_sqe(&g_job.u.uring.ring); 324 if (io->type == DD_READ || io->type == DD_POPULATE) { 325 io_uring_prep_read_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx); 326 } else { 327 io_uring_prep_write_fixed(sqe, target->u.uring.idx, io->buf, length, offset, io->idx); 328 } 329 sqe->flags |= IOSQE_FIXED_FILE; 330 io_uring_sqe_set_data(sqe, io); 331 io_uring_submit(&g_job.u.uring.ring); 332 } 333 #endif 334 335 static void 336 _dd_write_bdev_done(struct spdk_bdev_io *bdev_io, 337 bool success, 338 void *cb_arg) 339 { 340 struct dd_io *io = cb_arg; 341 342 assert(g_job.outstanding > 0); 343 g_job.outstanding--; 344 spdk_bdev_free_io(bdev_io); 345 dd_target_seek(io); 346 } 347 348 static void 349 dd_target_write(struct dd_io *io) 350 { 351 struct dd_target *target = &g_job.output; 352 uint64_t length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size; 353 uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size; 354 uint64_t read_offset = io->offset - read_region_start; 355 uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size; 356 uint64_t write_offset = write_region_start + read_offset; 357 int rc = 0; 358 359 if (g_error != 0 || g_interrupt == true) { 360 if (g_job.outstanding == 0) { 361 if (g_error == 0) { 362 dd_show_progress(true); 363 printf("\n\n"); 364 } 365 dd_exit(g_error); 366 } 367 return; 368 } 369 370 g_job.incremental_bytes += io->length; 371 g_job.outstanding++; 372 io->type = DD_WRITE; 373 374 if (target->type == DD_TARGET_TYPE_FILE) { 375 #ifdef SPDK_CONFIG_URING 376 if (g_opts.aio == false) { 377 dd_uring_submit(io, target, length, write_offset); 378 } else 379 #endif 380 { 381 struct iocb *iocb = &io->iocb; 382 383 io_prep_pwrite(iocb, target->u.aio.fd, io->buf, length, write_offset); 384 iocb->data = io; 385 if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) { 386 rc = -errno; 387 } 388 } 389 } else if (target->type == DD_TARGET_TYPE_BDEV) { 390 rc = spdk_bdev_write(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length, 391 _dd_write_bdev_done, io); 392 } 393 394 if (rc != 0) { 395 SPDK_ERRLOG("%s\n", strerror(-rc)); 396 assert(g_job.outstanding > 0); 397 g_job.outstanding--; 398 g_error = rc; 399 if (g_job.outstanding == 0) { 400 dd_exit(rc); 401 } 402 return; 403 } 404 } 405 406 static void 407 _dd_read_bdev_done(struct spdk_bdev_io *bdev_io, 408 bool success, 409 void *cb_arg) 410 { 411 struct dd_io *io = cb_arg; 412 413 spdk_bdev_free_io(bdev_io); 414 415 assert(g_job.outstanding > 0); 416 g_job.outstanding--; 417 dd_target_write(io); 418 } 419 420 static void 421 dd_target_read(struct dd_io *io) 422 { 423 struct dd_target *target = &g_job.input; 424 int rc = 0; 425 426 if (g_error != 0 || g_interrupt == true) { 427 if (g_job.outstanding == 0) { 428 dd_exit(g_error); 429 } 430 return; 431 } 432 433 g_job.outstanding++; 434 io->type = DD_READ; 435 436 if (target->type == DD_TARGET_TYPE_FILE) { 437 #ifdef SPDK_CONFIG_URING 438 if (g_opts.aio == false) { 439 dd_uring_submit(io, target, io->length, io->offset); 440 } else 441 #endif 442 { 443 struct iocb *iocb = &io->iocb; 444 445 io_prep_pread(iocb, target->u.aio.fd, io->buf, io->length, io->offset); 446 iocb->data = io; 447 if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) { 448 rc = -errno; 449 } 450 } 451 } else if (target->type == DD_TARGET_TYPE_BDEV) { 452 rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, io->offset, io->length, 453 _dd_read_bdev_done, io); 454 } 455 456 if (rc != 0) { 457 SPDK_ERRLOG("%s\n", strerror(-rc)); 458 assert(g_job.outstanding > 0); 459 g_job.outstanding--; 460 g_error = rc; 461 if (g_job.outstanding == 0) { 462 dd_exit(rc); 463 } 464 return; 465 } 466 } 467 468 static void 469 _dd_target_populate_buffer_done(struct spdk_bdev_io *bdev_io, 470 bool success, 471 void *cb_arg) 472 { 473 struct dd_io *io = cb_arg; 474 475 assert(g_job.outstanding > 0); 476 g_job.outstanding--; 477 spdk_bdev_free_io(bdev_io); 478 dd_target_read(io); 479 } 480 481 static void 482 dd_target_populate_buffer(struct dd_io *io) 483 { 484 struct dd_target *target = &g_job.output; 485 uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size; 486 uint64_t read_offset = g_job.input.pos - read_region_start; 487 uint64_t write_region_start = g_opts.output_offset * g_opts.io_unit_size; 488 uint64_t write_offset = write_region_start + read_offset; 489 uint64_t length; 490 int rc = 0; 491 492 io->offset = g_job.input.pos; 493 io->length = spdk_min(io->length, g_job.copy_size - read_offset); 494 495 if (io->length == 0 || g_error != 0 || g_interrupt == true) { 496 if (g_job.outstanding == 0) { 497 if (g_error == 0) { 498 dd_show_progress(true); 499 printf("\n\n"); 500 } 501 dd_exit(g_error); 502 } 503 return; 504 } 505 506 g_job.input.pos += io->length; 507 508 if ((io->length % target->block_size) == 0) { 509 dd_target_read(io); 510 return; 511 } 512 513 /* Read whole blocks from output to combine buffers later */ 514 g_job.outstanding++; 515 io->type = DD_POPULATE; 516 517 length = SPDK_CEIL_DIV(io->length, target->block_size) * target->block_size; 518 519 if (target->type == DD_TARGET_TYPE_FILE) { 520 #ifdef SPDK_CONFIG_URING 521 if (g_opts.aio == false) { 522 dd_uring_submit(io, target, length, write_offset); 523 } else 524 #endif 525 { 526 struct iocb *iocb = &io->iocb; 527 528 io_prep_pread(iocb, target->u.aio.fd, io->buf, length, write_offset); 529 iocb->data = io; 530 if (io_submit(g_job.u.aio.io_ctx, 1, &iocb) < 0) { 531 rc = -errno; 532 } 533 } 534 } else if (target->type == DD_TARGET_TYPE_BDEV) { 535 rc = spdk_bdev_read(target->u.bdev.desc, target->u.bdev.ch, io->buf, write_offset, length, 536 _dd_target_populate_buffer_done, io); 537 } 538 539 if (rc != 0) { 540 SPDK_ERRLOG("%s\n", strerror(-rc)); 541 assert(g_job.outstanding > 0); 542 g_job.outstanding--; 543 g_error = rc; 544 if (g_job.outstanding == 0) { 545 dd_exit(rc); 546 } 547 return; 548 } 549 } 550 551 static off_t 552 dd_file_seek_data(void) 553 { 554 off_t next_data_offset = (off_t) -1; 555 556 next_data_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_DATA); 557 558 if (next_data_offset == (off_t) -1) { 559 /* NXIO with SEEK_DATA means there are no more data to read. 560 * But in case of input and output files, we may have to finalize output file 561 * inserting a hole to the end of the file. 562 */ 563 if (errno == ENXIO) { 564 dd_finalize_output(); 565 } else if (g_job.outstanding == 0) { 566 SPDK_ERRLOG("Could not seek input file for data: %s\n", strerror(errno)); 567 g_error = errno; 568 dd_exit(g_error); 569 } 570 } 571 572 return next_data_offset; 573 } 574 575 static off_t 576 dd_file_seek_hole(void) 577 { 578 off_t next_hole_offset = (off_t) -1; 579 580 next_hole_offset = lseek(g_job.input.u.aio.fd, g_job.input.pos, SEEK_HOLE); 581 582 if (next_hole_offset == (off_t) -1 && g_job.outstanding == 0) { 583 SPDK_ERRLOG("Could not seek input file for hole: %s\n", strerror(errno)); 584 g_error = errno; 585 dd_exit(g_error); 586 } 587 588 return next_hole_offset; 589 } 590 591 static void 592 _dd_bdev_seek_data_done(struct spdk_bdev_io *bdev_io, 593 bool success, 594 void *cb_arg) 595 { 596 struct dd_io *io = cb_arg; 597 uint64_t next_data_offset_blocks = UINT64_MAX; 598 struct dd_target *target = &g_job.input; 599 int rc = 0; 600 601 if (g_error != 0 || g_interrupt == true) { 602 STAILQ_REMOVE_HEAD(&g_job.seek_queue, link); 603 if (g_job.outstanding == 0) { 604 if (g_error == 0) { 605 dd_show_progress(true); 606 printf("\n\n"); 607 } 608 dd_exit(g_error); 609 } 610 return; 611 } 612 613 assert(g_job.outstanding > 0); 614 g_job.outstanding--; 615 616 next_data_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io); 617 spdk_bdev_free_io(bdev_io); 618 619 /* UINT64_MAX means there are no more data to read. 620 * But in case of input and output files, we may have to finalize output file 621 * inserting a hole to the end of the file. 622 */ 623 if (next_data_offset_blocks == UINT64_MAX) { 624 STAILQ_REMOVE_HEAD(&g_job.seek_queue, link); 625 dd_finalize_output(); 626 return; 627 } 628 629 g_job.input.pos = next_data_offset_blocks * g_job.input.block_size; 630 631 g_job.outstanding++; 632 rc = spdk_bdev_seek_hole(target->u.bdev.desc, target->u.bdev.ch, 633 g_job.input.pos / g_job.input.block_size, 634 _dd_bdev_seek_hole_done, io); 635 636 if (rc != 0) { 637 SPDK_ERRLOG("%s\n", strerror(-rc)); 638 STAILQ_REMOVE_HEAD(&g_job.seek_queue, link); 639 assert(g_job.outstanding > 0); 640 g_job.outstanding--; 641 g_error = rc; 642 if (g_job.outstanding == 0) { 643 dd_exit(rc); 644 } 645 } 646 } 647 648 static void 649 _dd_bdev_seek_hole_done(struct spdk_bdev_io *bdev_io, 650 bool success, 651 void *cb_arg) 652 { 653 struct dd_io *io = cb_arg; 654 struct dd_target *target = &g_job.input; 655 uint64_t next_hole_offset_blocks = UINT64_MAX; 656 struct dd_io *seek_io; 657 int rc = 0; 658 659 /* First seek operation is the one in progress, i.e. this one just ended */ 660 STAILQ_REMOVE_HEAD(&g_job.seek_queue, link); 661 662 if (g_error != 0 || g_interrupt == true) { 663 if (g_job.outstanding == 0) { 664 if (g_error == 0) { 665 dd_show_progress(true); 666 printf("\n\n"); 667 } 668 dd_exit(g_error); 669 } 670 return; 671 } 672 673 assert(g_job.outstanding > 0); 674 g_job.outstanding--; 675 676 next_hole_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io); 677 spdk_bdev_free_io(bdev_io); 678 679 /* UINT64_MAX means there are no more holes. */ 680 if (next_hole_offset_blocks == UINT64_MAX) { 681 io->length = g_opts.io_unit_size; 682 } else { 683 io->length = spdk_min((uint64_t)g_opts.io_unit_size, 684 next_hole_offset_blocks * g_job.input.block_size - g_job.input.pos); 685 } 686 687 dd_target_populate_buffer(io); 688 689 /* If input reading is not at the end, start following seek operation in the queue */ 690 if (!STAILQ_EMPTY(&g_job.seek_queue) && g_job.input.pos < g_job.input.total_size) { 691 seek_io = STAILQ_FIRST(&g_job.seek_queue); 692 assert(seek_io != NULL); 693 g_job.outstanding++; 694 rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch, 695 g_job.input.pos / g_job.input.block_size, 696 _dd_bdev_seek_data_done, seek_io); 697 698 if (rc != 0) { 699 SPDK_ERRLOG("%s\n", strerror(-rc)); 700 assert(g_job.outstanding > 0); 701 g_job.outstanding--; 702 g_error = rc; 703 if (g_job.outstanding == 0) { 704 dd_exit(rc); 705 } 706 } 707 } 708 } 709 710 static void 711 dd_target_seek(struct dd_io *io) 712 { 713 struct dd_target *target = &g_job.input; 714 uint64_t read_region_start = g_opts.input_offset * g_opts.io_unit_size; 715 uint64_t read_offset = g_job.input.pos - read_region_start; 716 off_t next_data_offset = (off_t) -1; 717 off_t next_hole_offset = (off_t) -1; 718 int rc = 0; 719 720 if (!g_opts.sparse) { 721 dd_target_populate_buffer(io); 722 return; 723 } 724 725 if (g_job.copy_size - read_offset == 0 || g_error != 0 || g_interrupt == true) { 726 if (g_job.outstanding == 0) { 727 if (g_error == 0) { 728 dd_show_progress(true); 729 printf("\n\n"); 730 } 731 dd_exit(g_error); 732 } 733 return; 734 } 735 736 if (target->type == DD_TARGET_TYPE_FILE) { 737 next_data_offset = dd_file_seek_data(); 738 if (next_data_offset < 0) { 739 return; 740 } else if ((uint64_t)next_data_offset > g_job.input.pos) { 741 g_job.input.pos = next_data_offset; 742 } 743 744 next_hole_offset = dd_file_seek_hole(); 745 if (next_hole_offset < 0) { 746 return; 747 } else if ((uint64_t)next_hole_offset > g_job.input.pos) { 748 io->length = spdk_min((uint64_t)g_opts.io_unit_size, 749 (uint64_t)(next_hole_offset - g_job.input.pos)); 750 } else { 751 io->length = g_opts.io_unit_size; 752 } 753 754 dd_target_populate_buffer(io); 755 } else if (target->type == DD_TARGET_TYPE_BDEV) { 756 /* Check if other seek operation is in progress */ 757 if (STAILQ_EMPTY(&g_job.seek_queue)) { 758 g_job.outstanding++; 759 rc = spdk_bdev_seek_data(target->u.bdev.desc, target->u.bdev.ch, 760 g_job.input.pos / g_job.input.block_size, 761 _dd_bdev_seek_data_done, io); 762 763 } 764 765 STAILQ_INSERT_TAIL(&g_job.seek_queue, io, link); 766 } 767 768 if (rc != 0) { 769 SPDK_ERRLOG("%s\n", strerror(-rc)); 770 assert(g_job.outstanding > 0); 771 g_job.outstanding--; 772 g_error = rc; 773 if (g_job.outstanding == 0) { 774 dd_exit(rc); 775 } 776 return; 777 } 778 } 779 780 static void 781 dd_complete_poll(struct dd_io *io) 782 { 783 assert(g_job.outstanding > 0); 784 g_job.outstanding--; 785 786 switch (io->type) { 787 case DD_POPULATE: 788 dd_target_read(io); 789 break; 790 case DD_READ: 791 dd_target_write(io); 792 break; 793 case DD_WRITE: 794 dd_target_seek(io); 795 break; 796 default: 797 assert(false); 798 break; 799 } 800 } 801 802 #ifdef SPDK_CONFIG_URING 803 static int 804 dd_uring_poll(void *ctx) 805 { 806 struct io_uring_cqe *cqe; 807 struct dd_io *io; 808 int rc = 0; 809 int i; 810 811 for (i = 0; i < (int)g_opts.queue_depth; i++) { 812 rc = io_uring_peek_cqe(&g_job.u.uring.ring, &cqe); 813 if (rc == 0) { 814 if (cqe->res == -EAGAIN) { 815 continue; 816 } else if (cqe->res < 0) { 817 SPDK_ERRLOG("%s\n", strerror(-cqe->res)); 818 g_error = cqe->res; 819 } 820 821 io = io_uring_cqe_get_data(cqe); 822 io_uring_cqe_seen(&g_job.u.uring.ring, cqe); 823 824 dd_complete_poll(io); 825 } else if (rc != - EAGAIN) { 826 SPDK_ERRLOG("%s\n", strerror(-rc)); 827 g_error = rc; 828 } 829 } 830 831 return rc; 832 } 833 #endif 834 835 static int 836 dd_aio_poll(void *ctx) 837 { 838 struct io_event events[32]; 839 int rc = 0; 840 int i; 841 struct timespec timeout; 842 struct dd_io *io; 843 844 timeout.tv_sec = 0; 845 timeout.tv_nsec = 0; 846 847 rc = io_getevents(g_job.u.aio.io_ctx, 0, 32, events, &timeout); 848 849 if (rc < 0) { 850 SPDK_ERRLOG("%s\n", strerror(-rc)); 851 dd_exit(rc); 852 } 853 854 for (i = 0; i < rc; i++) { 855 io = events[i].data; 856 if (events[i].res != io->length) { 857 g_error = -ENOSPC; 858 } 859 860 dd_complete_poll(io); 861 } 862 863 return rc; 864 } 865 866 static int 867 dd_open_file(struct dd_target *target, const char *fname, int flags, uint64_t skip_blocks, 868 bool input) 869 { 870 int *fd; 871 872 #ifdef SPDK_CONFIG_URING 873 if (g_opts.aio == false) { 874 fd = &target->u.uring.fd; 875 } else 876 #endif 877 { 878 fd = &target->u.aio.fd; 879 } 880 881 flags |= O_RDWR; 882 883 if (input == false && ((flags & O_DIRECTORY) == 0)) { 884 flags |= O_CREAT; 885 } 886 887 if (input == false && ((flags & O_APPEND) == 0)) { 888 flags |= O_TRUNC; 889 } 890 891 target->type = DD_TARGET_TYPE_FILE; 892 *fd = open(fname, flags, 0600); 893 if (*fd < 0) { 894 SPDK_ERRLOG("Could not open file %s: %s\n", fname, strerror(errno)); 895 return *fd; 896 } 897 898 target->block_size = spdk_max(spdk_fd_get_blocklen(*fd), 1); 899 900 target->total_size = spdk_fd_get_size(*fd); 901 if (target->total_size == 0) { 902 target->total_size = g_opts.io_unit_size * g_opts.io_unit_count; 903 } 904 905 if (input == true) { 906 g_opts.queue_depth = spdk_min(g_opts.queue_depth, 907 (target->total_size / g_opts.io_unit_size) - skip_blocks + 1); 908 } 909 910 if (g_opts.io_unit_count != 0) { 911 g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count); 912 } 913 914 return 0; 915 } 916 917 static void 918 dd_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, 919 void *event_ctx) 920 { 921 SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type); 922 } 923 924 static int 925 dd_open_bdev(struct dd_target *target, const char *bdev_name, uint64_t skip_blocks) 926 { 927 int rc; 928 929 target->type = DD_TARGET_TYPE_BDEV; 930 931 rc = spdk_bdev_open_ext(bdev_name, true, dd_bdev_event_cb, NULL, &target->u.bdev.desc); 932 if (rc < 0) { 933 SPDK_ERRLOG("Could not open bdev %s: %s\n", bdev_name, strerror(-rc)); 934 return rc; 935 } 936 937 target->u.bdev.bdev = spdk_bdev_desc_get_bdev(target->u.bdev.desc); 938 target->open = true; 939 940 target->u.bdev.ch = spdk_bdev_get_io_channel(target->u.bdev.desc); 941 if (target->u.bdev.ch == NULL) { 942 spdk_bdev_close(target->u.bdev.desc); 943 SPDK_ERRLOG("Could not get I/O channel: %s\n", strerror(ENOMEM)); 944 return -ENOMEM; 945 } 946 947 target->block_size = spdk_bdev_get_block_size(target->u.bdev.bdev); 948 target->total_size = spdk_bdev_get_num_blocks(target->u.bdev.bdev) * target->block_size; 949 950 g_opts.queue_depth = spdk_min(g_opts.queue_depth, 951 (target->total_size / g_opts.io_unit_size) - skip_blocks + 1); 952 953 if (g_opts.io_unit_count != 0) { 954 g_opts.queue_depth = spdk_min(g_opts.queue_depth, g_opts.io_unit_count); 955 } 956 957 return 0; 958 } 959 960 static void 961 dd_finish(void) 962 { 963 /* Interrupt operation */ 964 g_interrupt = true; 965 } 966 967 static int 968 parse_flags(char *file_flags) 969 { 970 char *input_flag; 971 int flags = 0; 972 int i; 973 bool found = false; 974 975 /* Translate input flags to file open flags */ 976 while ((input_flag = strsep(&file_flags, ","))) { 977 for (i = 0; g_flags[i].name != NULL; i++) { 978 if (!strcmp(input_flag, g_flags[i].name)) { 979 flags |= g_flags[i].flag; 980 found = true; 981 break; 982 } 983 } 984 985 if (found == false) { 986 SPDK_ERRLOG("Unknown file flag: %s\n", input_flag); 987 dd_exit(-EINVAL); 988 return 0; 989 } 990 991 found = false; 992 } 993 994 return flags; 995 } 996 997 #ifdef SPDK_CONFIG_URING 998 static bool 999 dd_is_blk(int fd) 1000 { 1001 struct stat st; 1002 1003 if (fstat(fd, &st) != 0) { 1004 return false; 1005 } 1006 1007 return S_ISBLK(st.st_mode); 1008 } 1009 1010 struct dd_uring_init_ctx { 1011 unsigned int io_uring_flags; 1012 int rc; 1013 }; 1014 1015 static void * 1016 dd_uring_init(void *arg) 1017 { 1018 struct dd_uring_init_ctx *ctx = arg; 1019 1020 ctx->rc = io_uring_queue_init(g_opts.queue_depth * 2, &g_job.u.uring.ring, ctx->io_uring_flags); 1021 return ctx; 1022 } 1023 1024 static int 1025 dd_register_files(void) 1026 { 1027 int fds[2]; 1028 unsigned count = 0; 1029 1030 if (g_opts.input_file) { 1031 fds[count] = g_job.input.u.uring.fd; 1032 g_job.input.u.uring.idx = count; 1033 count++; 1034 } 1035 1036 if (g_opts.output_file) { 1037 fds[count] = g_job.output.u.uring.fd; 1038 g_job.output.u.uring.idx = count; 1039 count++; 1040 } 1041 1042 return io_uring_register_files(&g_job.u.uring.ring, fds, count); 1043 1044 } 1045 1046 static int 1047 dd_register_buffers(void) 1048 { 1049 struct iovec *iovs; 1050 int i, rc; 1051 1052 iovs = calloc(g_opts.queue_depth, sizeof(struct iovec)); 1053 if (iovs == NULL) { 1054 return -ENOMEM; 1055 } 1056 1057 for (i = 0; i < (int)g_opts.queue_depth; i++) { 1058 iovs[i].iov_base = g_job.ios[i].buf; 1059 iovs[i].iov_len = g_opts.io_unit_size; 1060 g_job.ios[i].idx = i; 1061 } 1062 1063 rc = io_uring_register_buffers(&g_job.u.uring.ring, iovs, g_opts.queue_depth); 1064 1065 free(iovs); 1066 return rc; 1067 } 1068 #endif 1069 1070 static void 1071 dd_run(void *arg1) 1072 { 1073 uint64_t write_size; 1074 uint32_t i; 1075 int rc, flags = 0; 1076 1077 if (g_opts.input_file) { 1078 if (g_opts.input_file_flags) { 1079 flags = parse_flags(g_opts.input_file_flags); 1080 } 1081 1082 if (dd_open_file(&g_job.input, g_opts.input_file, flags, g_opts.input_offset, true) < 0) { 1083 SPDK_ERRLOG("%s: %s\n", g_opts.input_file, strerror(errno)); 1084 dd_exit(-errno); 1085 return; 1086 } 1087 } else if (g_opts.input_bdev) { 1088 rc = dd_open_bdev(&g_job.input, g_opts.input_bdev, g_opts.input_offset); 1089 if (rc < 0) { 1090 SPDK_ERRLOG("%s: %s\n", g_opts.input_bdev, strerror(-rc)); 1091 dd_exit(rc); 1092 return; 1093 } 1094 } 1095 1096 write_size = g_opts.io_unit_count * g_opts.io_unit_size; 1097 g_job.input.pos = g_opts.input_offset * g_opts.io_unit_size; 1098 1099 /* We cannot check write size for input files because /dev/zeros, /dev/random, etc would not work. 1100 * We will handle that during copying */ 1101 if (g_opts.input_bdev && g_job.input.pos > g_job.input.total_size) { 1102 SPDK_ERRLOG("--skip value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in input\n", 1103 g_opts.input_offset, g_job.input.total_size / g_opts.io_unit_size); 1104 dd_exit(-ENOSPC); 1105 return; 1106 } 1107 1108 if (g_opts.io_unit_count != 0 && g_opts.input_bdev && 1109 write_size + g_job.input.pos > g_job.input.total_size) { 1110 SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available from input\n", 1111 g_opts.io_unit_count, (g_job.input.total_size - g_job.input.pos) / g_opts.io_unit_size); 1112 dd_exit(-ENOSPC); 1113 return; 1114 } 1115 1116 if (g_opts.io_unit_count != 0) { 1117 g_job.copy_size = write_size; 1118 } else { 1119 g_job.copy_size = g_job.input.total_size - g_job.input.pos; 1120 } 1121 1122 g_job.output.pos = g_opts.output_offset * g_opts.io_unit_size; 1123 1124 if (g_opts.output_file) { 1125 flags = 0; 1126 1127 if (g_opts.output_file_flags) { 1128 flags = parse_flags(g_opts.output_file_flags); 1129 } 1130 1131 if (dd_open_file(&g_job.output, g_opts.output_file, flags, g_opts.output_offset, false) < 0) { 1132 SPDK_ERRLOG("%s: %s\n", g_opts.output_file, strerror(errno)); 1133 dd_exit(-errno); 1134 return; 1135 } 1136 } else if (g_opts.output_bdev) { 1137 rc = dd_open_bdev(&g_job.output, g_opts.output_bdev, g_opts.output_offset); 1138 if (rc < 0) { 1139 SPDK_ERRLOG("%s: %s\n", g_opts.output_bdev, strerror(-rc)); 1140 dd_exit(rc); 1141 return; 1142 } 1143 1144 if (g_job.output.pos > g_job.output.total_size) { 1145 SPDK_ERRLOG("--seek value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n", 1146 g_opts.output_offset, g_job.output.total_size / g_opts.io_unit_size); 1147 dd_exit(-ENOSPC); 1148 return; 1149 } 1150 1151 if (g_opts.io_unit_count != 0 && write_size + g_job.output.pos > g_job.output.total_size) { 1152 SPDK_ERRLOG("--count value too big (%" PRIu64 ") - only %" PRIu64 " blocks available in output\n", 1153 g_opts.io_unit_count, (g_job.output.total_size - g_job.output.pos) / g_opts.io_unit_size); 1154 dd_exit(-ENOSPC); 1155 return; 1156 } 1157 } 1158 1159 if ((g_job.output.block_size > g_opts.io_unit_size) || 1160 (g_job.input.block_size > g_opts.io_unit_size)) { 1161 SPDK_ERRLOG("--bs value cannot be less than input (%d) neither output (%d) native block size\n", 1162 g_job.input.block_size, g_job.output.block_size); 1163 dd_exit(-EINVAL); 1164 return; 1165 } 1166 1167 if (g_opts.input_bdev && g_opts.io_unit_size % g_job.input.block_size != 0) { 1168 SPDK_ERRLOG("--bs value must be a multiple of input native block size (%d)\n", 1169 g_job.input.block_size); 1170 dd_exit(-EINVAL); 1171 return; 1172 } 1173 1174 g_job.ios = calloc(g_opts.queue_depth, sizeof(struct dd_io)); 1175 if (g_job.ios == NULL) { 1176 SPDK_ERRLOG("%s\n", strerror(ENOMEM)); 1177 dd_exit(-ENOMEM); 1178 return; 1179 } 1180 1181 for (i = 0; i < g_opts.queue_depth; i++) { 1182 g_job.ios[i].buf = spdk_malloc(g_opts.io_unit_size, 0x1000, NULL, 0, SPDK_MALLOC_DMA); 1183 if (g_job.ios[i].buf == NULL) { 1184 SPDK_ERRLOG("%s - try smaller block size value\n", strerror(ENOMEM)); 1185 dd_exit(-ENOMEM); 1186 return; 1187 } 1188 g_job.ios[i].length = (uint64_t)g_opts.io_unit_size; 1189 } 1190 1191 if (g_opts.input_file || g_opts.output_file) { 1192 #ifdef SPDK_CONFIG_URING 1193 if (g_opts.aio == false) { 1194 struct dd_uring_init_ctx ctx; 1195 int flags = parse_flags(g_opts.input_file_flags) & parse_flags(g_opts.output_file_flags); 1196 1197 ctx.io_uring_flags = IORING_SETUP_SQPOLL; 1198 if ((flags & O_DIRECT) != 0 && 1199 dd_is_blk(g_job.input.u.uring.fd) && 1200 dd_is_blk(g_job.output.u.uring.fd)) { 1201 ctx.io_uring_flags = IORING_SETUP_IOPOLL; 1202 } 1203 1204 g_job.u.uring.poller = SPDK_POLLER_REGISTER(dd_uring_poll, NULL, 0); 1205 1206 /* Initialized uring kernel threads inherit parent process CPU mask, to avoid conflicting 1207 * with SPDK cores initialize uring without any affinity. */ 1208 if (spdk_call_unaffinitized(dd_uring_init, &ctx) == NULL || ctx.rc) { 1209 SPDK_ERRLOG("Failed to create io_uring: %d (%s)\n", ctx.rc, spdk_strerror(-ctx.rc)); 1210 dd_exit(ctx.rc); 1211 return; 1212 } 1213 g_job.u.uring.active = true; 1214 1215 /* Register the files */ 1216 rc = dd_register_files(); 1217 if (rc) { 1218 SPDK_ERRLOG("Failed to register files with io_uring: %d (%s)\n", rc, spdk_strerror(-rc)); 1219 dd_exit(rc); 1220 return; 1221 } 1222 1223 /* Register the buffers */ 1224 rc = dd_register_buffers(); 1225 if (rc) { 1226 SPDK_ERRLOG("Failed to register buffers with io_uring: %d (%s)\n", rc, spdk_strerror(-rc)); 1227 dd_exit(rc); 1228 return; 1229 } 1230 1231 } else 1232 #endif 1233 { 1234 g_job.u.aio.poller = SPDK_POLLER_REGISTER(dd_aio_poll, NULL, 0); 1235 io_setup(g_opts.queue_depth, &g_job.u.aio.io_ctx); 1236 } 1237 } 1238 1239 clock_gettime(CLOCK_REALTIME, &g_job.start_time); 1240 1241 g_job.status_poller = SPDK_POLLER_REGISTER(dd_status_poller, NULL, 1242 STATUS_POLLER_PERIOD_SEC * SPDK_SEC_TO_USEC); 1243 1244 STAILQ_INIT(&g_job.seek_queue); 1245 1246 for (i = 0; i < g_opts.queue_depth; i++) { 1247 dd_target_seek(&g_job.ios[i]); 1248 } 1249 1250 } 1251 1252 enum dd_cmdline_opts { 1253 DD_OPTION_IF = 0x1000, 1254 DD_OPTION_OF, 1255 DD_OPTION_IFLAGS, 1256 DD_OPTION_OFLAGS, 1257 DD_OPTION_IB, 1258 DD_OPTION_OB, 1259 DD_OPTION_SKIP, 1260 DD_OPTION_SEEK, 1261 DD_OPTION_BS, 1262 DD_OPTION_QD, 1263 DD_OPTION_COUNT, 1264 DD_OPTION_AIO, 1265 DD_OPTION_SPARSE, 1266 }; 1267 1268 static struct option g_cmdline_opts[] = { 1269 { 1270 .name = "if", 1271 .has_arg = 1, 1272 .flag = NULL, 1273 .val = DD_OPTION_IF, 1274 }, 1275 { 1276 .name = "of", 1277 .has_arg = 1, 1278 .flag = NULL, 1279 .val = DD_OPTION_OF, 1280 }, 1281 { 1282 .name = "iflag", 1283 .has_arg = 1, 1284 .flag = NULL, 1285 .val = DD_OPTION_IFLAGS, 1286 }, 1287 { 1288 .name = "oflag", 1289 .has_arg = 1, 1290 .flag = NULL, 1291 .val = DD_OPTION_OFLAGS, 1292 }, 1293 { 1294 .name = "ib", 1295 .has_arg = 1, 1296 .flag = NULL, 1297 .val = DD_OPTION_IB, 1298 }, 1299 { 1300 .name = "ob", 1301 .has_arg = 1, 1302 .flag = NULL, 1303 .val = DD_OPTION_OB, 1304 }, 1305 { 1306 .name = "skip", 1307 .has_arg = 1, 1308 .flag = NULL, 1309 .val = DD_OPTION_SKIP, 1310 }, 1311 { 1312 .name = "seek", 1313 .has_arg = 1, 1314 .flag = NULL, 1315 .val = DD_OPTION_SEEK, 1316 }, 1317 { 1318 .name = "bs", 1319 .has_arg = 1, 1320 .flag = NULL, 1321 .val = DD_OPTION_BS, 1322 }, 1323 { 1324 .name = "qd", 1325 .has_arg = 1, 1326 .flag = NULL, 1327 .val = DD_OPTION_QD, 1328 }, 1329 { 1330 .name = "count", 1331 .has_arg = 1, 1332 .flag = NULL, 1333 .val = DD_OPTION_COUNT, 1334 }, 1335 { 1336 .name = "aio", 1337 .has_arg = 0, 1338 .flag = NULL, 1339 .val = DD_OPTION_AIO, 1340 }, 1341 { 1342 .name = "sparse", 1343 .has_arg = 0, 1344 .flag = NULL, 1345 .val = DD_OPTION_SPARSE, 1346 }, 1347 { 1348 .name = NULL 1349 } 1350 }; 1351 1352 static void 1353 usage(void) 1354 { 1355 printf("[--------- DD Options ---------]\n"); 1356 printf(" --if Input file. Must specify either --if or --ib.\n"); 1357 printf(" --ib Input bdev. Must specifier either --if or --ib\n"); 1358 printf(" --of Output file. Must specify either --of or --ob.\n"); 1359 printf(" --ob Output bdev. Must specify either --of or --ob.\n"); 1360 printf(" --iflag Input file flags.\n"); 1361 printf(" --oflag Output file flags.\n"); 1362 printf(" --bs I/O unit size (default: %" PRId64 ")\n", g_opts.io_unit_size); 1363 printf(" --qd Queue depth (default: %d)\n", g_opts.queue_depth); 1364 printf(" --count I/O unit count. The number of I/O units to copy. (default: all)\n"); 1365 printf(" --skip Skip this many I/O units at start of input. (default: 0)\n"); 1366 printf(" --seek Skip this many I/O units at start of output. (default: 0)\n"); 1367 printf(" --aio Force usage of AIO. (by default io_uring is used if available)\n"); 1368 printf(" --sparse Enable hole skipping in input target\n"); 1369 printf(" Available iflag and oflag values:\n"); 1370 printf(" append - append mode\n"); 1371 printf(" direct - use direct I/O for data\n"); 1372 printf(" directory - fail unless a directory\n"); 1373 printf(" dsync - use synchronized I/O for data\n"); 1374 printf(" noatime - do not update access time\n"); 1375 printf(" noctty - do not assign controlling terminal from file\n"); 1376 printf(" nofollow - do not follow symlinks\n"); 1377 printf(" nonblock - use non-blocking I/O\n"); 1378 printf(" sync - use synchronized I/O for data and metadata\n"); 1379 } 1380 1381 static int 1382 parse_args(int argc, char *argv) 1383 { 1384 switch (argc) { 1385 case DD_OPTION_IF: 1386 g_opts.input_file = strdup(argv); 1387 break; 1388 case DD_OPTION_OF: 1389 g_opts.output_file = strdup(argv); 1390 break; 1391 case DD_OPTION_IFLAGS: 1392 g_opts.input_file_flags = strdup(argv); 1393 break; 1394 case DD_OPTION_OFLAGS: 1395 g_opts.output_file_flags = strdup(argv); 1396 break; 1397 case DD_OPTION_IB: 1398 g_opts.input_bdev = strdup(argv); 1399 break; 1400 case DD_OPTION_OB: 1401 g_opts.output_bdev = strdup(argv); 1402 break; 1403 case DD_OPTION_SKIP: 1404 g_opts.input_offset = spdk_strtol(optarg, 10); 1405 break; 1406 case DD_OPTION_SEEK: 1407 g_opts.output_offset = spdk_strtol(optarg, 10); 1408 break; 1409 case DD_OPTION_BS: 1410 g_opts.io_unit_size = spdk_strtol(optarg, 10); 1411 break; 1412 case DD_OPTION_QD: 1413 g_opts.queue_depth = spdk_strtol(optarg, 10); 1414 break; 1415 case DD_OPTION_COUNT: 1416 g_opts.io_unit_count = spdk_strtol(optarg, 10); 1417 break; 1418 case DD_OPTION_AIO: 1419 g_opts.aio = true; 1420 break; 1421 case DD_OPTION_SPARSE: 1422 g_opts.sparse = true; 1423 break; 1424 default: 1425 usage(); 1426 return 1; 1427 } 1428 return 0; 1429 } 1430 1431 static void 1432 dd_free(void) 1433 { 1434 uint32_t i; 1435 1436 free(g_opts.input_file); 1437 free(g_opts.output_file); 1438 free(g_opts.input_bdev); 1439 free(g_opts.output_bdev); 1440 free(g_opts.input_file_flags); 1441 free(g_opts.output_file_flags); 1442 1443 1444 if (g_job.input.type == DD_TARGET_TYPE_FILE || g_job.output.type == DD_TARGET_TYPE_FILE) { 1445 #ifdef SPDK_CONFIG_URING 1446 if (g_opts.aio == false) { 1447 if (g_job.u.uring.active) { 1448 io_uring_unregister_files(&g_job.u.uring.ring); 1449 io_uring_queue_exit(&g_job.u.uring.ring); 1450 } 1451 } else 1452 #endif 1453 { 1454 io_destroy(g_job.u.aio.io_ctx); 1455 } 1456 } 1457 1458 if (g_job.ios) { 1459 for (i = 0; i < g_opts.queue_depth; i++) { 1460 spdk_free(g_job.ios[i].buf); 1461 } 1462 1463 free(g_job.ios); 1464 } 1465 } 1466 1467 int 1468 main(int argc, char **argv) 1469 { 1470 struct spdk_app_opts opts = {}; 1471 int rc = 1; 1472 1473 spdk_app_opts_init(&opts, sizeof(opts)); 1474 opts.name = "spdk_dd"; 1475 opts.reactor_mask = "0x1"; 1476 opts.shutdown_cb = dd_finish; 1477 opts.rpc_addr = NULL; 1478 rc = spdk_app_parse_args(argc, argv, &opts, "", g_cmdline_opts, parse_args, usage); 1479 if (rc == SPDK_APP_PARSE_ARGS_FAIL) { 1480 SPDK_ERRLOG("Invalid arguments\n"); 1481 goto end; 1482 } else if (rc == SPDK_APP_PARSE_ARGS_HELP) { 1483 goto end; 1484 } 1485 1486 if (g_opts.input_file != NULL && g_opts.input_bdev != NULL) { 1487 SPDK_ERRLOG("You may specify either --if or --ib, but not both.\n"); 1488 rc = EINVAL; 1489 goto end; 1490 } 1491 1492 if (g_opts.output_file != NULL && g_opts.output_bdev != NULL) { 1493 SPDK_ERRLOG("You may specify either --of or --ob, but not both.\n"); 1494 rc = EINVAL; 1495 goto end; 1496 } 1497 1498 if (g_opts.input_file == NULL && g_opts.input_bdev == NULL) { 1499 SPDK_ERRLOG("You must specify either --if or --ib\n"); 1500 rc = EINVAL; 1501 goto end; 1502 } 1503 1504 if (g_opts.output_file == NULL && g_opts.output_bdev == NULL) { 1505 SPDK_ERRLOG("You must specify either --of or --ob\n"); 1506 rc = EINVAL; 1507 goto end; 1508 } 1509 1510 if (g_opts.io_unit_size <= 0) { 1511 SPDK_ERRLOG("Invalid --bs value\n"); 1512 rc = EINVAL; 1513 goto end; 1514 } 1515 1516 if (g_opts.io_unit_count < 0) { 1517 SPDK_ERRLOG("Invalid --count value\n"); 1518 rc = EINVAL; 1519 goto end; 1520 } 1521 1522 if (g_opts.output_file == NULL && g_opts.output_file_flags != NULL) { 1523 SPDK_ERRLOG("--oflags may be used only with --of\n"); 1524 rc = EINVAL; 1525 goto end; 1526 } 1527 1528 if (g_opts.input_file == NULL && g_opts.input_file_flags != NULL) { 1529 SPDK_ERRLOG("--iflags may be used only with --if\n"); 1530 rc = EINVAL; 1531 goto end; 1532 } 1533 1534 rc = spdk_app_start(&opts, dd_run, NULL); 1535 if (rc) { 1536 SPDK_ERRLOG("Error occurred while performing copy\n"); 1537 } 1538 1539 dd_free(); 1540 spdk_app_fini(); 1541 1542 end: 1543 return rc; 1544 } 1545