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