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