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