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