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/env.h" 38 #include "spdk/event.h" 39 #include "spdk/blob_bdev.h" 40 #include "spdk/blob.h" 41 #include "spdk/log.h" 42 #include "spdk/version.h" 43 44 /* 45 * This is not a public header file, but the CLI does expose 46 * some internals of blobstore for dev/debug puposes so we 47 * include it here. 48 */ 49 #include "../lib/blob/blobstore.h" 50 51 static const char *program_name = "blobcli"; 52 static const char *program_conf = "blobcli.conf"; 53 static const char *bdev_name = "Nvme0n1"; 54 55 enum cli_action_type { 56 CLI_IMPORT, 57 CLI_DUMP, 58 CLI_FILL, 59 CLI_REM_XATTR, 60 CLI_SET_XATTR, 61 CLI_SET_SUPER, 62 CLI_SHOW_BS, 63 CLI_SHOW_BLOB, 64 CLI_CREATE_BLOB, 65 CLI_LIST_BDEVS, 66 CLI_LIST_BLOBS, 67 CLI_INIT_BS 68 }; 69 #define BUFSIZE 255 70 71 /* todo, scrub this as there may be some extra junk in here picked up along the way... */ 72 struct cli_context_t { 73 struct spdk_blob_store *bs; 74 struct spdk_blob *blob; 75 spdk_blob_id blobid; 76 spdk_blob_id superid; 77 struct spdk_io_channel *channel; 78 uint8_t *buff; 79 uint64_t page_size; 80 uint64_t page_count; 81 uint64_t blob_pages; 82 uint64_t bytes_so_far; 83 FILE *fp; 84 enum cli_action_type action; 85 char key[BUFSIZE + 1]; 86 char value[BUFSIZE + 1]; 87 char file[BUFSIZE + 1]; 88 uint64_t filesize; 89 int fill_value; 90 const char *bdev_name; 91 int rc; 92 int num_clusters; 93 void (*next_func)(void *arg1, struct spdk_blob_store *bs, int bserrno); 94 }; 95 96 /* 97 * Prints usage and relevant error message. 98 */ 99 static void 100 usage(char *msg) 101 { 102 if (msg) { 103 printf("%s", msg); 104 } 105 printf("Version %s\n", SPDK_VERSION_STRING); 106 printf("Usage: %s [-c SPDK config_file] Command\n", program_name); 107 printf("\n%s is a command line tool for interacting with blobstore\n", 108 program_name); 109 printf("on the underlying device specified in the conf file passed\n"); 110 printf("in as a command line option.\n"); 111 printf("\nCommands include:\n"); 112 printf("\t-i - initialize a blobstore\n"); 113 printf("\t-l bdevs | blobs - list either available bdevs or existing blobs\n"); 114 printf("\t-n <# clusters> - create new blob\n"); 115 printf("\t-p <blobid> - set the superblob to the ID provided\n"); 116 printf("\t-s <blobid> | bs - show blob info or blobstore info\n"); 117 printf("\t-x <blobid> name value - set xattr name/value pair\n"); 118 printf("\t-r <blobid> name - remove xattr name/value pair\n"); 119 printf("\t-f <blobid> value - fill a blob with a decimal value\n"); 120 printf("\t-d <blobid> filename - dump contents of a blob to a file\n"); 121 printf("\t-m <blobid> filename - import contents of a file to a blob\n"); 122 printf("\n"); 123 } 124 125 /* 126 * Free up memory that we allocated. 127 */ 128 static void 129 cli_cleanup(struct cli_context_t *cli_context) 130 { 131 if (cli_context->buff) { 132 spdk_dma_free(cli_context->buff); 133 } 134 if (cli_context->channel) { 135 spdk_bs_free_io_channel(cli_context->channel); 136 } 137 free(cli_context); 138 } 139 140 /* 141 * Callback routine for the blobstore unload. 142 */ 143 static void 144 unload_complete(void *cb_arg, int bserrno) 145 { 146 struct cli_context_t *cli_context = cb_arg; 147 148 if (bserrno) { 149 printf("Error %d unloading the bobstore\n", bserrno); 150 cli_context->rc = bserrno; 151 } 152 153 spdk_app_stop(cli_context->rc); 154 } 155 156 /* 157 * Unload the blobstore. 158 */ 159 static void 160 unload_bs(struct cli_context_t *cli_context, char *msg, int bserrno) 161 { 162 if (bserrno) { 163 printf("%s (err %d)\n", msg, bserrno); 164 cli_context->rc = bserrno; 165 } 166 if (cli_context->bs) { 167 spdk_bs_unload(cli_context->bs, unload_complete, cli_context); 168 } else { 169 spdk_app_stop(bserrno); 170 } 171 } 172 173 /* 174 * Callback for closing a blob. 175 */ 176 static void 177 close_cb(void *arg1, int bserrno) 178 { 179 struct cli_context_t *cli_context = arg1; 180 181 if (bserrno) { 182 unload_bs(cli_context, "Error in close callback", 183 bserrno); 184 return; 185 } 186 unload_bs(cli_context, "", 0); 187 } 188 189 /* 190 * Callback function for sync'ing metadata. 191 */ 192 static void 193 sync_complete(void *arg1, int bserrno) 194 { 195 struct cli_context_t *cli_context = arg1; 196 197 if (bserrno) { 198 unload_bs(cli_context, "Error in sync callback", 199 bserrno); 200 return; 201 } 202 203 spdk_bs_md_close_blob(&cli_context->blob, close_cb, 204 cli_context); 205 } 206 207 /* 208 * Callback function for opening a blob after creating. 209 */ 210 static void 211 open_now_resize(void *cb_arg, struct spdk_blob *blob, int bserrno) 212 { 213 struct cli_context_t *cli_context = cb_arg; 214 int rc = 0; 215 uint64_t total = 0; 216 217 if (bserrno) { 218 unload_bs(cli_context, "Error in open completion", 219 bserrno); 220 return; 221 } 222 cli_context->blob = blob; 223 224 rc = spdk_bs_md_resize_blob(cli_context->blob, 225 cli_context->num_clusters); 226 if (rc) { 227 unload_bs(cli_context, "Error in blob resize", 228 bserrno); 229 return; 230 } 231 232 total = spdk_blob_get_num_clusters(cli_context->blob); 233 printf("blob now has USED clusters of %" PRIu64 "\n", 234 total); 235 236 /* 237 * Always a good idea to sync after MD changes or the changes 238 * may be lost if things aren't closed cleanly. 239 */ 240 spdk_bs_md_sync_blob(cli_context->blob, sync_complete, 241 cli_context); 242 } 243 244 /* 245 * Callback function for creating a blob. 246 */ 247 static void 248 blob_create_complete(void *arg1, spdk_blob_id blobid, int bserrno) 249 { 250 struct cli_context_t *cli_context = arg1; 251 252 if (bserrno) { 253 unload_bs(cli_context, "Error in blob create callback", 254 bserrno); 255 return; 256 } 257 258 cli_context->blobid = blobid; 259 printf("New blob id %" PRIu64 "\n", cli_context->blobid); 260 261 /* We have to open the blob before we can do things like resize. */ 262 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 263 open_now_resize, cli_context); 264 } 265 266 /* 267 * Callback for get_super where we'll continue on to show blobstore info. 268 */ 269 static void 270 show_bs(void *arg1, spdk_blob_id blobid, int bserrno) 271 { 272 struct cli_context_t *cli_context = arg1; 273 uint64_t val; 274 struct spdk_bdev *bdev = NULL; 275 276 if (bserrno && bserrno != -ENOENT) { 277 unload_bs(cli_context, "Error in get_super callback", 278 bserrno); 279 return; 280 } 281 cli_context->superid = blobid; 282 283 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 284 if (bdev == NULL) { 285 unload_bs(cli_context, "Error w/bdev in get_super callback", 286 bserrno); 287 return; 288 } 289 290 printf("Blobstore Public Info:\n"); 291 printf("\tUsing bdev Product Name: %s\n", 292 spdk_bdev_get_product_name(bdev)); 293 printf("\tAPI Version: %d\n", SPDK_BS_VERSION); 294 295 if (bserrno != -ENOENT) { 296 printf("\tsuper blob ID: %" PRIu64 "\n", cli_context->superid); 297 } else { 298 printf("\tsuper blob ID: none assigned\n"); 299 } 300 301 val = spdk_bs_get_page_size(cli_context->bs); 302 printf("\tpage size: %" PRIu64 "\n", val); 303 304 val = spdk_bs_get_cluster_size(cli_context->bs); 305 printf("\tcluster size: %" PRIu64 "\n", val); 306 307 val = spdk_bs_free_cluster_count(cli_context->bs); 308 printf("\t# free clusters: %" PRIu64 "\n", val); 309 310 /* 311 * Private info isn't accessible via the public API but 312 * may be useful for debug of blobstore based applications. 313 */ 314 printf("\nBlobstore Private Info:\n"); 315 printf("\tMetadata start (pages): %" PRIu64 "\n", 316 cli_context->bs->md_start); 317 printf("\tMetadata length (pages): %d \n", 318 cli_context->bs->md_len); 319 320 unload_bs(cli_context, "", 0); 321 } 322 323 /* 324 * Load callback where we'll get the super blobid next. 325 */ 326 static void 327 get_super_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 328 { 329 struct cli_context_t *cli_context = arg1; 330 331 if (bserrno) { 332 unload_bs(cli_context, "Error in load blob callback", 333 bserrno); 334 return; 335 } 336 cli_context->bs = bs; 337 338 spdk_bs_get_super(cli_context->bs, show_bs, cli_context); 339 } 340 341 /* 342 * Callback for load bs where we'll continue on to create a blob. 343 */ 344 static void 345 create_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 346 { 347 struct cli_context_t *cli_context = arg1; 348 349 if (bserrno) { 350 unload_bs(cli_context, "Error in load callback", 351 bserrno); 352 return; 353 } 354 cli_context->bs = bs; 355 356 spdk_bs_md_create_blob(cli_context->bs, blob_create_complete, 357 cli_context); 358 } 359 360 /* 361 * Show detailed info about a particular blob. 362 */ 363 static void 364 show_blob(struct cli_context_t *cli_context) 365 { 366 uint64_t val; 367 struct spdk_xattr_names *names; 368 const void *value; 369 size_t value_len; 370 char data[256]; 371 unsigned int i; 372 373 printf("Blob Public Info:\n"); 374 375 printf("blob ID: %" PRIu64 "\n", cli_context->blobid); 376 377 val = spdk_blob_get_num_clusters(cli_context->blob); 378 printf("# of clusters: %" PRIu64 "\n", val); 379 380 printf("# of bytes: %" PRIu64 "\n", 381 val * spdk_bs_get_cluster_size(cli_context->bs)); 382 383 val = spdk_blob_get_num_pages(cli_context->blob); 384 printf("# of pages: %" PRIu64 "\n", val); 385 386 spdk_bs_md_get_xattr_names(cli_context->blob, &names); 387 388 printf("# of xattrs: %d\n", spdk_xattr_names_get_count(names)); 389 printf("xattrs:\n"); 390 for (i = 0; i < spdk_xattr_names_get_count(names); i++) { 391 spdk_bs_md_get_xattr_value(cli_context->blob, 392 spdk_xattr_names_get_name(names, i), 393 &value, &value_len); 394 if ((value_len + 1) > sizeof(data)) { 395 printf("FYI: adjusting size of xattr due to CLI limits.\n"); 396 value_len = sizeof(data) - 1; 397 } 398 memcpy(&data, value, value_len); 399 data[value_len] = '\0'; 400 printf("\n(%d) Name:%s\n", i, 401 spdk_xattr_names_get_name(names, i)); 402 printf("(%d) Value:\n", i); 403 spdk_trace_dump(stdout, "", value, value_len); 404 } 405 406 /* 407 * Private info isn't accessible via the public API but 408 * may be useful for debug of blobstore based applications. 409 */ 410 printf("\nBlob Private Info:\n"); 411 switch (cli_context->blob->state) { 412 case SPDK_BLOB_STATE_DIRTY: 413 printf("state: DIRTY\n"); 414 break; 415 case SPDK_BLOB_STATE_CLEAN: 416 printf("state: CLEAN\n"); 417 break; 418 case SPDK_BLOB_STATE_LOADING: 419 printf("state: LOADING\n"); 420 break; 421 case SPDK_BLOB_STATE_SYNCING: 422 printf("state: SYNCING\n"); 423 break; 424 default: 425 printf("state: UNKNOWN\n"); 426 break; 427 } 428 printf("open ref count: %d\n", 429 cli_context->blob->open_ref); 430 } 431 432 /* 433 * Callback for getting the first blob. 434 */ 435 static void 436 blob_iter_cb(void *arg1, struct spdk_blob *blob, int bserrno) 437 { 438 struct cli_context_t *cli_context = arg1; 439 440 if (bserrno) { 441 if (bserrno == -ENOENT) { 442 /* this simply means there are no more blobs */ 443 unload_bs(cli_context, "", 0); 444 } else { 445 unload_bs(cli_context, "Error in blob iter callback", 446 bserrno); 447 } 448 return; 449 } 450 451 if (cli_context->action == CLI_LIST_BLOBS) { 452 /* just listing blobs */ 453 printf("Found blob with ID# %" PRIu64 "\n", 454 spdk_blob_get_id(blob)); 455 } else if (spdk_blob_get_id(blob) == cli_context->blobid) { 456 /* 457 * Found the blob we're looking for, but we need to finish 458 * iterating even after showing the info so that internally 459 * the blobstore logic will close the blob. Or we could 460 * chose to close it now, either way. 461 */ 462 cli_context->blob = blob; 463 show_blob(cli_context); 464 } 465 466 spdk_bs_md_iter_next(cli_context->bs, &blob, blob_iter_cb, 467 cli_context); 468 } 469 470 /* 471 * Callback for load bs where we'll continue on to list all blobs. 472 */ 473 static void 474 list_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 475 { 476 struct cli_context_t *cli_context = arg1; 477 478 if (bserrno) { 479 unload_bs(cli_context, "Error in load callback", 480 bserrno); 481 return; 482 } 483 cli_context->bs = bs; 484 485 if (cli_context->action == CLI_LIST_BLOBS) { 486 printf("\nList BLOBS:\n"); 487 } 488 489 spdk_bs_md_iter_first(cli_context->bs, blob_iter_cb, cli_context); 490 } 491 492 /* 493 * Callback for setting the super blob ID. 494 */ 495 static void 496 set_super_cb(void *arg1, int bserrno) 497 { 498 struct cli_context_t *cli_context = arg1; 499 500 if (bserrno) { 501 unload_bs(cli_context, "Error in set_super callback", 502 bserrno); 503 return; 504 } 505 506 printf("Super Blob ID has been set.\n"); 507 unload_bs(cli_context, "", 0); 508 } 509 510 /* 511 * Callback for load bs where we'll continue on to set the super blob. 512 */ 513 static void 514 set_super_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 515 { 516 struct cli_context_t *cli_context = arg1; 517 518 if (bserrno) { 519 unload_bs(cli_context, "Error in load callback", 520 bserrno); 521 return; 522 } 523 cli_context->bs = bs; 524 525 spdk_bs_set_super(cli_context->bs, cli_context->superid, 526 set_super_cb, cli_context); 527 } 528 529 /* 530 * Callback for set_xattr_open where we set or delete xattrs. 531 */ 532 static void 533 set_xattr(void *cb_arg, struct spdk_blob *blob, int bserrno) 534 { 535 struct cli_context_t *cli_context = cb_arg; 536 537 if (bserrno) { 538 unload_bs(cli_context, "Error in blob open callback", 539 bserrno); 540 return; 541 } 542 cli_context->blob = blob; 543 544 if (cli_context->action == CLI_SET_XATTR) { 545 spdk_blob_md_set_xattr(cli_context->blob, 546 cli_context->key, 547 cli_context->value, 548 strlen(cli_context->value) + 1); 549 printf("Xattr has been set.\n"); 550 } else { 551 spdk_blob_md_remove_xattr(cli_context->blob, 552 cli_context->key); 553 printf("Xattr has been removed.\n"); 554 } 555 556 spdk_bs_md_sync_blob(cli_context->blob, sync_complete, 557 cli_context); 558 } 559 560 /* 561 * Callback for load bs where we'll continue on to set/del an xattr. 562 */ 563 static void 564 xattr_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 565 { 566 struct cli_context_t *cli_context = arg1; 567 568 if (bserrno) { 569 unload_bs(cli_context, "Error in load callback", 570 bserrno); 571 return; 572 } 573 cli_context->bs = bs; 574 575 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 576 set_xattr, cli_context); 577 } 578 579 /* 580 * Callback function for reading a blob for dumping to a file. 581 */ 582 static void 583 read_dump_complete(void *arg1, int bserrno) 584 { 585 struct cli_context_t *cli_context = arg1; 586 uint64_t bytes_written; 587 588 if (bserrno) { 589 fclose(cli_context->fp); 590 unload_bs(cli_context, "Error in read completion", 591 bserrno); 592 return; 593 } 594 595 bytes_written = fwrite(cli_context->buff, 1, cli_context->page_size, 596 cli_context->fp); 597 if (bytes_written != cli_context->page_size) { 598 fclose(cli_context->fp); 599 unload_bs(cli_context, "Error with fwrite", 600 bserrno); 601 return; 602 } 603 604 printf("."); 605 if (++cli_context->page_count < cli_context->blob_pages) { 606 /* perform another read */ 607 spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 608 cli_context->buff, cli_context->page_count, 609 1, read_dump_complete, cli_context); 610 } else { 611 /* done reading */ 612 printf("\nFile write complete.\n"); 613 fclose(cli_context->fp); 614 spdk_bs_md_close_blob(&cli_context->blob, close_cb, 615 cli_context); 616 } 617 } 618 619 /* 620 * Callback for write completion on the import of a file to a blob. 621 */ 622 static void 623 write_imp_complete(void *arg1, int bserrno) 624 { 625 struct cli_context_t *cli_context = arg1; 626 uint64_t bytes_read; 627 628 if (bserrno) { 629 fclose(cli_context->fp); 630 unload_bs(cli_context, "Error in write completion", 631 bserrno); 632 return; 633 } 634 635 if (cli_context->bytes_so_far < cli_context->filesize) { 636 /* perform another file read */ 637 bytes_read = fread(cli_context->buff, 1, 638 cli_context->page_size, 639 cli_context->fp); 640 cli_context->bytes_so_far += bytes_read; 641 642 /* if this read is < 1 page, fill with 0s */ 643 if (bytes_read < cli_context->page_size) { 644 uint8_t *offset = cli_context->buff + bytes_read; 645 memset(offset, 0, 646 cli_context->page_size - bytes_read); 647 } 648 } else { 649 /* 650 * Done reading the file, fill the rest of the blob with 0s, 651 * yeah we're memsetting the same page over and over here 652 */ 653 memset(cli_context->buff, 0, cli_context->page_size); 654 } 655 if (++cli_context->page_count < cli_context->blob_pages) { 656 printf("."); 657 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 658 cli_context->buff, cli_context->page_count, 659 1, write_imp_complete, cli_context); 660 } else { 661 /* done writing */ 662 printf("\nBlob import complete.\n"); 663 fclose(cli_context->fp); 664 spdk_bs_md_close_blob(&cli_context->blob, close_cb, 665 cli_context); 666 } 667 } 668 669 /* 670 * Callback for open blobs where we'll continue on dump a blob to a file or 671 * import a file to a blob. For dump, the resulting file will always be the 672 * full size of the blob. For import, the blob will fill with the file 673 * contents first and then 0 out the rest of the blob. 674 */ 675 static void 676 dmpimp_open_cb(void *cb_arg, struct spdk_blob *blob, int bserrno) 677 { 678 struct cli_context_t *cli_context = cb_arg; 679 680 if (bserrno) { 681 unload_bs(cli_context, "Error in blob open callback", 682 bserrno); 683 return; 684 } 685 cli_context->blob = blob; 686 cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 687 cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 688 if (cli_context->channel == NULL) { 689 unload_bs(cli_context, "Error in allocating channel", 690 -ENOMEM); 691 return; 692 } 693 694 /* 695 * We'll transfer just one page at a time to keep the buffer 696 * small. This could be bigger of course. 697 */ 698 cli_context->buff = spdk_dma_malloc(cli_context->page_size, 699 0x1000, NULL); 700 if (cli_context->buff == NULL) { 701 unload_bs(cli_context, "Error in allocating memory", 702 -ENOMEM); 703 return; 704 } 705 printf("Working"); 706 cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 707 cli_context->page_count = 0; 708 if (cli_context->action == CLI_DUMP) { 709 cli_context->fp = fopen(cli_context->file, "w"); 710 711 /* read a page of data from the blob */ 712 spdk_bs_io_read_blob(cli_context->blob, cli_context->channel, 713 cli_context->buff, cli_context->page_count, 714 1, read_dump_complete, cli_context); 715 } else { 716 cli_context->fp = fopen(cli_context->file, "r"); 717 718 /* get the filesize then rewind read a page of data from file */ 719 fseek(cli_context->fp, 0L, SEEK_END); 720 cli_context->filesize = ftell(cli_context->fp); 721 rewind(cli_context->fp); 722 cli_context->bytes_so_far = fread(cli_context->buff, 1, 723 cli_context->page_size, 724 cli_context->fp); 725 726 /* if the file is < a page, fill the rest with 0s */ 727 if (cli_context->filesize < cli_context->page_size) { 728 uint8_t *offset = 729 cli_context->buff + cli_context->filesize; 730 731 memset(offset, 0, 732 cli_context->page_size - cli_context->filesize); 733 } 734 735 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 736 cli_context->buff, cli_context->page_count, 737 1, write_imp_complete, cli_context); 738 } 739 } 740 741 /* 742 * Callback for load bs where we'll continue on dump a blob to a file or 743 * import a file to a blob. 744 */ 745 static void 746 dmpimp_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 747 { 748 struct cli_context_t *cli_context = arg1; 749 750 if (bserrno) { 751 unload_bs(cli_context, "Error in load callback", 752 bserrno); 753 return; 754 } 755 cli_context->bs = bs; 756 757 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 758 dmpimp_open_cb, cli_context); 759 } 760 761 /* 762 * Callback function for writing a specific pattern to page 0. 763 */ 764 static void 765 write_complete(void *arg1, int bserrno) 766 { 767 struct cli_context_t *cli_context = arg1; 768 769 if (bserrno) { 770 unload_bs(cli_context, "Error in write completion", 771 bserrno); 772 return; 773 } 774 printf("."); 775 if (++cli_context->page_count < cli_context->blob_pages) { 776 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 777 cli_context->buff, cli_context->page_count, 778 1, write_complete, cli_context); 779 } else { 780 /* done writing */ 781 printf("\nBlob fill complete.\n"); 782 spdk_bs_md_close_blob(&cli_context->blob, close_cb, 783 cli_context); 784 } 785 786 } 787 788 /* 789 * function to fill a blob with a value. 790 */ 791 static void 792 fill_blob(void *cb_arg, struct spdk_blob *blob, int bserrno) 793 { 794 struct cli_context_t *cli_context = cb_arg; 795 796 if (bserrno) { 797 unload_bs(cli_context, "Error in blob open callback", 798 bserrno); 799 return; 800 } 801 cli_context->blob = blob; 802 cli_context->page_count = 0; 803 cli_context->blob_pages = spdk_blob_get_num_pages(cli_context->blob); 804 cli_context->buff = spdk_dma_malloc(cli_context->page_size, 805 0x1000, NULL); 806 if (cli_context->buff == NULL) { 807 unload_bs(cli_context, "Error in allocating memory", 808 -ENOMEM); 809 return; 810 } 811 812 memset(cli_context->buff, cli_context->fill_value, 813 cli_context->page_size); 814 printf("\n"); 815 spdk_bs_io_write_blob(cli_context->blob, cli_context->channel, 816 cli_context->buff, 817 0, 1, write_complete, cli_context); 818 } 819 820 /* 821 * Callback for load bs where we'll continue on to fill a blob. 822 */ 823 static void 824 fill_load_cb(void *arg1, struct spdk_blob_store *bs, int bserrno) 825 { 826 struct cli_context_t *cli_context = arg1; 827 828 if (bserrno) { 829 unload_bs(cli_context, "Error in load callback", 830 bserrno); 831 return; 832 } 833 cli_context->bs = bs; 834 cli_context->page_size = spdk_bs_get_page_size(cli_context->bs); 835 cli_context->channel = spdk_bs_alloc_io_channel(cli_context->bs); 836 if (cli_context->channel == NULL) { 837 unload_bs(cli_context, "Error in allocating channel", 838 -ENOMEM); 839 return; 840 } 841 842 spdk_bs_md_open_blob(cli_context->bs, cli_context->blobid, 843 fill_blob, cli_context); 844 } 845 846 /* 847 * Multiple actions require us to open the bs first. A function pointer 848 * setup earlier will direct the callback accordingly. 849 */ 850 static void 851 load_bs(struct cli_context_t *cli_context) 852 { 853 struct spdk_bdev *bdev = NULL; 854 struct spdk_bs_dev *bs_dev = NULL; 855 856 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 857 if (bdev == NULL) { 858 printf("Could not find a bdev\n"); 859 spdk_app_stop(-1); 860 return; 861 } 862 863 bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 864 if (bs_dev == NULL) { 865 printf("Could not create blob bdev!!\n"); 866 spdk_app_stop(-1); 867 return; 868 } 869 870 spdk_bs_load(bs_dev, cli_context->next_func, cli_context); 871 } 872 873 /* 874 * Lists all the blobs on this blobstore. 875 */ 876 static void 877 list_bdevs(void) 878 { 879 struct spdk_bdev *bdev = NULL; 880 881 printf("\nList bdevs:\n"); 882 883 bdev = spdk_bdev_first(); 884 if (bdev == NULL) { 885 printf("Could not find a bdev\n"); 886 } 887 while (bdev) { 888 printf("\tbdev Name: %s\n", spdk_bdev_get_name(bdev)); 889 printf("\tbdev Product Name: %s\n", 890 spdk_bdev_get_product_name(bdev)); 891 bdev = spdk_bdev_next(bdev); 892 } 893 894 printf("\n"); 895 spdk_app_stop(0); 896 } 897 898 /* 899 * Callback function for initializing a blob. 900 */ 901 static void 902 bs_init_complete(void *cb_arg, struct spdk_blob_store *bs, 903 int bserrno) 904 { 905 struct cli_context_t *cli_context = cb_arg; 906 907 if (bserrno) { 908 unload_bs(cli_context, "Error in bs init callback", 909 bserrno); 910 return; 911 } 912 cli_context->bs = bs; 913 printf("blobstore init'd: (%p)\n", cli_context->bs); 914 915 unload_bs(cli_context, "", 0); 916 } 917 918 /* 919 * Initialize a new blobstore. 920 */ 921 static void 922 init_bs(struct cli_context_t *cli_context) 923 { 924 struct spdk_bdev *bdev = NULL; 925 struct spdk_bs_dev *bs_dev = NULL; 926 927 bdev = spdk_bdev_get_by_name(cli_context->bdev_name); 928 if (bdev == NULL) { 929 printf("Could not find a bdev\n"); 930 spdk_app_stop(-1); 931 return; 932 } 933 printf("Blobstore using bdev Product Name: %s\n", 934 spdk_bdev_get_product_name(bdev)); 935 936 bs_dev = spdk_bdev_create_bs_dev(bdev, NULL, NULL); 937 if (bs_dev == NULL) { 938 printf("Could not create blob bdev!!\n"); 939 spdk_app_stop(-1); 940 return; 941 } 942 943 spdk_bs_init(bs_dev, NULL, bs_init_complete, 944 cli_context); 945 } 946 947 /* 948 * This is the function we pass into the SPDK framework that gets 949 * called first. 950 */ 951 static void 952 cli_start(void *arg1, void *arg2) 953 { 954 struct cli_context_t *cli_context = arg1; 955 956 printf("\n"); 957 958 /* 959 * Decide what to do next based on cmd line parsing that 960 * happened earlier, in many cases we setup a function pointer 961 * to be used as a callback following a generic action like 962 * loading the blobstore. 963 */ 964 switch (cli_context->action) { 965 case CLI_SET_SUPER: 966 cli_context->next_func = &set_super_load_cb; 967 load_bs(cli_context); 968 break; 969 case CLI_SHOW_BS: 970 cli_context->next_func = &get_super_load_cb; 971 load_bs(cli_context); 972 break; 973 case CLI_CREATE_BLOB: 974 cli_context->next_func = &create_load_cb; 975 load_bs(cli_context); 976 break; 977 case CLI_SET_XATTR: 978 case CLI_REM_XATTR: 979 cli_context->next_func = &xattr_load_cb; 980 load_bs(cli_context); 981 break; 982 case CLI_SHOW_BLOB: 983 case CLI_LIST_BLOBS: 984 cli_context->next_func = &list_load_cb; 985 load_bs(cli_context); 986 break; 987 case CLI_DUMP: 988 case CLI_IMPORT: 989 cli_context->next_func = &dmpimp_load_cb; 990 load_bs(cli_context); 991 break; 992 case CLI_FILL: 993 cli_context->next_func = &fill_load_cb; 994 load_bs(cli_context); 995 break; 996 case CLI_INIT_BS: 997 init_bs(cli_context); 998 break; 999 case CLI_LIST_BDEVS: 1000 list_bdevs(); 1001 break; 1002 default: 1003 /* should never get here */ 1004 spdk_app_stop(-1); 1005 break; 1006 } 1007 } 1008 1009 int 1010 main(int argc, char **argv) 1011 { 1012 struct spdk_app_opts opts = {}; 1013 struct cli_context_t *cli_context = NULL; 1014 const char *config_file = NULL; 1015 int rc = 0; 1016 int op; 1017 bool cmd_chosen = false; 1018 1019 if (argc < 2) { 1020 usage("ERROR: Invalid option\n"); 1021 exit(1); 1022 } 1023 1024 cli_context = calloc(1, sizeof(struct cli_context_t)); 1025 if (cli_context == NULL) { 1026 printf("ERROR: could not allocate context structure\n"); 1027 exit(-1); 1028 } 1029 cli_context->bdev_name = bdev_name; 1030 1031 while ((op = getopt(argc, argv, "c:d:f:il:m:n:p:r:s:x:")) != -1) { 1032 switch (op) { 1033 case 'c': 1034 config_file = optarg; 1035 break; 1036 case 'd': 1037 cli_context->action = CLI_DUMP; 1038 cli_context->blobid = atoll(optarg); 1039 break; 1040 case 'f': 1041 cli_context->action = CLI_FILL; 1042 cli_context->blobid = atoll(optarg); 1043 break; 1044 case 'i': 1045 cli_context->action = CLI_INIT_BS; 1046 break; 1047 case 'r': 1048 cli_context->action = CLI_REM_XATTR; 1049 cli_context->blobid = atoll(optarg); 1050 break; 1051 case 'l': 1052 if (strcmp("bdevs", optarg) == 0) { 1053 cli_context->action = CLI_LIST_BDEVS; 1054 } else if (strcmp("blobs", optarg) == 0) { 1055 cli_context->action = CLI_LIST_BLOBS; 1056 } else { 1057 usage("ERROR: invalid option for list\n"); 1058 cli_cleanup(cli_context); 1059 exit(-1); 1060 } 1061 break; 1062 case 'm': 1063 cli_context->action = CLI_IMPORT; 1064 cli_context->blobid = atoll(optarg); 1065 break; 1066 case 'n': 1067 cli_context->num_clusters = atoi(optarg); 1068 if (cli_context->num_clusters > 0) { 1069 cli_context->action = CLI_CREATE_BLOB; 1070 } else { 1071 usage("ERROR: invalid option for new\n"); 1072 cli_cleanup(cli_context); 1073 exit(-1); 1074 } 1075 break; 1076 case 'p': 1077 cli_context->action = CLI_SET_SUPER; 1078 cli_context->superid = atoll(optarg); 1079 break; 1080 case 's': 1081 if (strcmp("bs", optarg) == 0) { 1082 cli_context->action = CLI_SHOW_BS; 1083 } else { 1084 cli_context->action = CLI_SHOW_BLOB; 1085 cli_context->blobid = atoll(optarg); 1086 } 1087 break; 1088 case 'x': 1089 cli_context->action = CLI_SET_XATTR; 1090 cli_context->blobid = atoll(optarg); 1091 break; 1092 default: 1093 usage("ERROR: Invalid option\n"); 1094 cli_cleanup(cli_context); 1095 exit(1); 1096 } 1097 /* config file is the only option that can be combined */ 1098 if (op != 'c') { 1099 if (cmd_chosen) { 1100 usage("Error: Please choose only one command\n"); 1101 cli_cleanup(cli_context); 1102 exit(1); 1103 } else { 1104 cmd_chosen = true; 1105 } 1106 } 1107 } 1108 1109 if (cmd_chosen == false) { 1110 usage("Error: Please choose a command.\n"); 1111 exit(1); 1112 } 1113 1114 /* if they don't supply a conf name, use the default */ 1115 if (!config_file) { 1116 config_file = program_conf; 1117 } 1118 1119 /* if the config file doesn't exist, tell them how to make one */ 1120 if (access(config_file, F_OK) == -1) { 1121 printf("Error: No config file found.\n"); 1122 printf("To create a config file named 'blobcli.conf' for your NVMe device:\n"); 1123 printf(" <path to spdk>/scripts/gen_nvme.sh > blobcli.conf\n"); 1124 printf("and then re-run the cli tool.\n"); 1125 exit(1); 1126 } 1127 1128 /* a few options require some extra paramters */ 1129 if (cli_context->action == CLI_SET_XATTR || 1130 cli_context->action == CLI_REM_XATTR) { 1131 snprintf(cli_context->key, BUFSIZE, "%s", argv[3]); 1132 snprintf(cli_context->value, BUFSIZE, "%s", argv[4]); 1133 } 1134 1135 if (cli_context->action == CLI_DUMP || 1136 cli_context->action == CLI_IMPORT) { 1137 snprintf(cli_context->file, BUFSIZE, "%s", argv[3]); 1138 } 1139 1140 if (cli_context->action == CLI_FILL) { 1141 cli_context->fill_value = atoi(argv[3]); 1142 } 1143 1144 /* Set default values in opts struct along with name and conf file. */ 1145 spdk_app_opts_init(&opts); 1146 opts.name = "blobcli"; 1147 opts.config_file = config_file; 1148 1149 /* 1150 * spdk_app_start() will block running cli_start() until 1151 * spdk_app_stop() is called by someone (not simply when 1152 * cli_start() returns) 1153 */ 1154 rc = spdk_app_start(&opts, cli_start, cli_context, NULL); 1155 if (rc) { 1156 printf("ERROR!\n"); 1157 } 1158 1159 /* Free up memory that we allocated */ 1160 cli_cleanup(cli_context); 1161 1162 /* Gracefully close out all of the SPDK subsystems. */ 1163 spdk_app_fini(); 1164 return rc; 1165 } 1166