Print this page
8479 nvmeadm doesn't handle namespaces with EUI64
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>


  37 #include <stdio.h>
  38 #include <stdlib.h>
  39 #include <strings.h>
  40 #include <ctype.h>
  41 #include <err.h>
  42 #include <sys/sunddi.h>
  43 #include <libdevinfo.h>
  44 
  45 #include <sys/nvme.h>
  46 
  47 #include "nvmeadm.h"
  48 
  49 typedef struct nvme_process_arg nvme_process_arg_t;
  50 typedef struct nvme_feature nvme_feature_t;
  51 typedef struct nvmeadm_cmd nvmeadm_cmd_t;
  52 
  53 struct nvme_process_arg {
  54         int npa_argc;
  55         char **npa_argv;
  56         char *npa_name;
  57         uint32_t npa_nsid;

  58         boolean_t npa_isns;
  59         const nvmeadm_cmd_t *npa_cmd;
  60         di_node_t npa_node;
  61         di_minor_t npa_minor;
  62         char *npa_path;
  63         char *npa_dsk;
  64         nvme_identify_ctrl_t *npa_idctl;
  65         nvme_identify_nsid_t *npa_idns;
  66         nvme_version_t *npa_version;
  67 };
  68 
  69 struct nvme_feature {
  70         char *f_name;
  71         char *f_short;
  72         uint8_t f_feature;
  73         size_t f_bufsize;
  74         uint_t f_getflags;
  75         int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *);
  76         void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *);
  77 };


 104 static int do_get_logpage(int, const nvme_process_arg_t *);
 105 static int do_get_feat_common(int, const nvme_feature_t *,
 106     nvme_identify_ctrl_t *);
 107 static int do_get_feat_intr_vect(int, const nvme_feature_t *,
 108     nvme_identify_ctrl_t *);
 109 static int do_get_features(int, const nvme_process_arg_t *);
 110 static int do_format(int, const nvme_process_arg_t *);
 111 static int do_secure_erase(int, const nvme_process_arg_t *);
 112 static int do_attach_detach(int, const nvme_process_arg_t *);
 113 
 114 static void usage_list(const char *);
 115 static void usage_identify(const char *);
 116 static void usage_get_logpage(const char *);
 117 static void usage_get_features(const char *);
 118 static void usage_format(const char *);
 119 static void usage_secure_erase(const char *);
 120 static void usage_attach_detach(const char *);
 121 
 122 int verbose;
 123 int debug;
 124 int found;
 125 static int exitcode;
 126 
 127 static const nvmeadm_cmd_t nvmeadm_cmds[] = {
 128         {
 129                 "list",
 130                 "list controllers and namespaces",
 131                 NULL,
 132                 do_list, usage_list, B_TRUE
 133         },
 134         {
 135                 "identify",
 136                 "identify controllers and/or namespaces",
 137                 NULL,
 138                 do_identify, usage_identify, B_TRUE
 139         },
 140         {
 141                 "get-logpage",
 142                 "get a log page from controllers and/or namespaces",
 143                 NULL,
 144                 do_get_logpage, usage_get_logpage, B_TRUE


 296         /*
 297          * Make sure we're not running commands on multiple controllers that
 298          * aren't allowed to do that.
 299          */
 300         if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL &&
 301             cmd->c_multi == B_FALSE) {
 302                 warnx("%s not allowed on multiple controllers",
 303                     cmd->c_name);
 304                 usage(cmd);
 305                 exit(-1);
 306         }
 307 
 308         /*
 309          * Get controller/namespace arguments and run command.
 310          */
 311         npa.npa_name = strtok_r(argv[optind], ",", &lasts);
 312         do {
 313                 if (npa.npa_name != NULL) {
 314                         tmp = strchr(npa.npa_name, '/');
 315                         if (tmp != NULL) {
 316                                 unsigned long nsid;
 317                                 *tmp++ = '\0';
 318                                 errno = 0;
 319                                 nsid = strtoul(tmp, NULL, 10);
 320                                 if (nsid >= UINT32_MAX || errno != 0) {
 321                                         warn("invalid namespace %s", tmp);
 322                                         exitcode--;
 323                                         continue;
 324                                 }
 325                                 if (nsid == 0) {
 326                                         warnx("invalid namespace %s", tmp);
 327                                         exitcode--;
 328                                         continue;
 329                                 }
 330                                 npa.npa_nsid = nsid;
 331                                 npa.npa_isns = B_TRUE;
 332                         }
 333                 }
 334 
 335                 if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL)
 336                         err(-1, "failed to initialize libdevinfo");
 337                 nvme_walk(&npa, node);
 338                 di_fini(node);
 339 
 340                 if (found == 0) {
 341                         if (npa.npa_name != NULL) {
 342                                 warnx("%s%.*s%.*d: no such controller or "
 343                                     "namespace", npa.npa_name,
 344                                     npa.npa_nsid > 0 ? -1 : 0, "/",
 345                                     npa.npa_nsid > 0 ? -1 : 0, npa.npa_nsid);
 346                         } else {
 347                                 warnx("no controllers found");
 348                         }
 349                         exitcode--;
 350                 }
 351                 found = 0;
 352                 npa.npa_name = strtok_r(NULL, ",", &lasts);
 353         } while (npa.npa_name != NULL);
 354 
 355         exit(exitcode);
 356 }
 357 
 358 static void
 359 usage(const nvmeadm_cmd_t *cmd)
 360 {
 361         (void) fprintf(stderr, "usage:\n");
 362         (void) fprintf(stderr, "  %s -h %s\n", getprogname(),
 363             cmd != NULL ? cmd->c_name : "[<command>]");
 364         (void) fprintf(stderr, "  %s [-dv] ", getprogname());
 365 
 366         if (cmd != NULL) {
 367                 cmd->c_usage(cmd->c_name);
 368         } else {
 369                 (void) fprintf(stderr,
 370                     "<command> <ctl>[/<ns>][,...] [<args>]\n");
 371                 (void) fprintf(stderr,
 372                     "\n  Manage NVMe controllers and namespaces.\n");
 373                 (void) fprintf(stderr, "\ncommands:\n");
 374 
 375                 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
 376                         (void) fprintf(stderr, "  %-15s - %s\n",
 377                             cmd->c_name, cmd->c_desc);
 378         }
 379         (void) fprintf(stderr, "\nflags:\n"
 380             "  -h  print usage information\n"
 381             "  -d  print information useful for debugging %s\n"
 382             "  -v  print verbose information\n", getprogname());
 383         if (cmd != NULL && cmd->c_flagdesc != NULL)
 384                 (void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
 385 }
 386 
 387 static boolean_t
 388 nvme_match(nvme_process_arg_t *npa)
 389 {
 390         char *name;
 391         uint32_t nsid = 0;
 392 
 393         if (npa->npa_name == NULL)
 394                 return (B_TRUE);
 395 
 396         if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
 397             di_instance(npa->npa_node)) < 0)
 398                 err(-1, "nvme_match()");
 399 
 400         if (strcmp(name, npa->npa_name) != 0) {
 401                 free(name);
 402                 return (B_FALSE);
 403         }
 404 
 405         free(name);
 406 
 407         if (npa->npa_isns) {
 408                 if (npa->npa_nsid == 0)
 409                         return (B_TRUE);
 410                 nsid = strtoul(di_minor_name(npa->npa_minor), NULL, 10);
 411         }
 412 
 413         if (npa->npa_isns && npa->npa_nsid != nsid)


 414                 return (B_FALSE);

 415 
 416         return (B_TRUE);
 417 }
 418 
 419 char *
 420 nvme_dskname(const nvme_process_arg_t *npa)
 421 {
 422         char *path = NULL;
 423         di_node_t child;
 424         di_dim_t dim;
 425         char *addr;
 426 
 427         dim = di_dim_init();
 428 
 429         for (child = di_child_node(npa->npa_node);
 430             child != DI_NODE_NIL;
 431             child = di_sibling_node(child)) {
 432                 addr = di_bus_addr(child);
 433                 if (addr == NULL)
 434                         continue;


 458 
 459         di_dim_fini(dim);
 460         return (path);
 461 }
 462 
 463 static int
 464 nvme_process(di_node_t node, di_minor_t minor, void *arg)
 465 {
 466         nvme_process_arg_t *npa = arg;
 467         int fd;
 468 
 469         npa->npa_node = node;
 470         npa->npa_minor = minor;
 471 
 472         if (!nvme_match(npa))
 473                 return (DI_WALK_CONTINUE);
 474 
 475         if ((fd = nvme_open(minor)) < 0)
 476                 return (DI_WALK_CONTINUE);
 477 
 478         found++;
 479 
 480         npa->npa_path = di_devfs_path(node);
 481         if (npa->npa_path == NULL)
 482                 goto out;
 483 
 484         npa->npa_version = nvme_version(fd);
 485         if (npa->npa_version == NULL)
 486                 goto out;
 487 
 488         npa->npa_idctl = nvme_identify_ctrl(fd);
 489         if (npa->npa_idctl == NULL)
 490                 goto out;
 491 
 492         npa->npa_idns = nvme_identify_nsid(fd);
 493         if (npa->npa_idns == NULL)
 494                 goto out;
 495 
 496         if (npa->npa_isns)
 497                 npa->npa_dsk = nvme_dskname(npa);
 498 


 572         ns_npa.npa_cmd = &cmd;
 573 
 574         nvme_walk(&ns_npa, npa->npa_node);
 575 
 576         free(name);
 577 
 578         return (exitcode);
 579 }
 580 
 581 static void
 582 usage_identify(const char *c_name)
 583 {
 584         (void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n"
 585             "  Print detailed information about the specified NVMe "
 586             "controllers and/or name-\n  spaces.\n", c_name);
 587 }
 588 
 589 static int
 590 do_identify(int fd, const nvme_process_arg_t *npa)
 591 {
 592         if (npa->npa_nsid == 0) {
 593                 nvme_capabilities_t *cap;
 594 
 595                 cap = nvme_capabilities(fd);
 596                 if (cap == NULL)
 597                         return (-1);
 598 
 599                 (void) printf("%s: ", npa->npa_name);
 600                 nvme_print_identify_ctrl(npa->npa_idctl, cap,
 601                     npa->npa_version);
 602 
 603                 free(cap);
 604         } else {
 605                 (void) printf("%s/%s: ", npa->npa_name,
 606                     di_minor_name(npa->npa_minor));
 607                 nvme_print_identify_nsid(npa->npa_idns,
 608                     npa->npa_version);
 609         }
 610 
 611         return (0);
 612 }
 613 
 614 static void
 615 usage_get_logpage(const char *c_name)
 616 {
 617         (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n"
 618             "  Print the specified log page of the specified NVMe "
 619             "controllers and/or name-\n  spaces. Supported log pages "
 620             "are error, health, and firmware.\n", c_name);
 621 }
 622 
 623 static int
 624 do_get_logpage_error(int fd, const nvme_process_arg_t *npa)
 625 {
 626         int nlog = npa->npa_idctl->id_elpe + 1;
 627         size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog;
 628         nvme_error_log_entry_t *elog;
 629 
 630         if (npa->npa_nsid != 0)
 631                 errx(-1, "Error Log not available on a per-namespace basis");
 632 
 633         elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize);
 634 
 635         if (elog == NULL)
 636                 return (-1);
 637 
 638         nlog = bufsize / sizeof (nvme_error_log_entry_t);
 639 
 640         (void) printf("%s: ", npa->npa_name);
 641         nvme_print_error_log(nlog, elog);
 642 
 643         free(elog);
 644 
 645         return (0);
 646 }
 647 
 648 static int
 649 do_get_logpage_health(int fd, const nvme_process_arg_t *npa)
 650 {
 651         size_t bufsize = sizeof (nvme_health_log_t);
 652         nvme_health_log_t *hlog;
 653 
 654         if (npa->npa_nsid != 0) {
 655                 if (npa->npa_idctl->id_lpa.lp_smart == 0)
 656                         errx(-1, "SMART/Health information not available "
 657                             "on a per-namespace basis on this controller");
 658         }
 659 
 660         hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
 661 
 662         if (hlog == NULL)
 663                 return (-1);
 664 
 665         (void) printf("%s: ", npa->npa_name);
 666         nvme_print_health_log(hlog, npa->npa_idctl);
 667 
 668         free(hlog);
 669 
 670         return (0);
 671 }
 672 
 673 static int
 674 do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa)
 675 {
 676         size_t bufsize = sizeof (nvme_fwslot_log_t);
 677         nvme_fwslot_log_t *fwlog;
 678 
 679         if (npa->npa_nsid != 0)
 680                 errx(-1, "Firmware Slot information not available on a "
 681                     "per-namespace basis");
 682 
 683         fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize);
 684 
 685         if (fwlog == NULL)
 686                 return (-1);
 687 
 688         (void) printf("%s: ", npa->npa_name);
 689         nvme_print_fwslot_log(fwlog);
 690 
 691         free(fwlog);
 692 
 693         return (0);
 694 }
 695 
 696 static int
 697 do_get_logpage(int fd, const nvme_process_arg_t *npa)
 698 {
 699         int ret = 0;


 788 
 789         return (0);
 790 }
 791 
 792 static int
 793 do_get_features(int fd, const nvme_process_arg_t *npa)
 794 {
 795         const nvme_feature_t *feat;
 796         char *f, *flist, *lasts;
 797         boolean_t header_printed = B_FALSE;
 798 
 799         if (npa->npa_argc > 1)
 800                 errx(-1, "unexpected arguments");
 801 
 802         /*
 803          * No feature list given, print all supported features.
 804          */
 805         if (npa->npa_argc == 0) {
 806                 (void) printf("%s: Get Features\n", npa->npa_name);
 807                 for (feat = &features[0]; feat->f_feature != 0; feat++) {
 808                         if ((npa->npa_nsid != 0 &&
 809                             (feat->f_getflags & NVMEADM_NS) == 0) ||
 810                             (npa->npa_nsid == 0 &&
 811                             (feat->f_getflags & NVMEADM_CTRL) == 0))
 812                                 continue;
 813 
 814                         (void) feat->f_get(fd, feat, npa->npa_idctl);
 815                 }
 816 
 817                 return (0);
 818         }
 819 
 820         /*
 821          * Process feature list.
 822          */
 823         flist = strdup(npa->npa_argv[0]);
 824         if (flist == NULL)
 825                 err(-1, "do_get_features");
 826 
 827         for (f = strtok_r(flist, ",", &lasts);
 828             f != NULL;
 829             f = strtok_r(NULL, ",", &lasts)) {
 830                 while (isspace(*f))
 831                         f++;
 832 
 833                 for (feat = &features[0]; feat->f_feature != 0; feat++) {
 834                         if (strncasecmp(feat->f_name, f, strlen(f)) == 0 ||
 835                             strncasecmp(feat->f_short, f, strlen(f)) == 0)
 836                                 break;
 837                 }
 838 
 839                 if (feat->f_feature == 0) {
 840                         warnx("unknown feature %s", f);
 841                         continue;
 842                 }
 843 
 844                 if ((npa->npa_nsid != 0 &&
 845                     (feat->f_getflags & NVMEADM_NS) == 0) ||
 846                     (npa->npa_nsid == 0 &&
 847                     (feat->f_getflags & NVMEADM_CTRL) == 0)) {
 848                         warnx("feature %s %s supported for namespaces",
 849                             feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ?
 850                             "only" : "not");
 851                         continue;
 852                 }
 853 
 854                 if (!header_printed) {
 855                         (void) printf("%s: Get Features\n", npa->npa_name);
 856                         header_printed = B_TRUE;
 857                 }
 858 
 859                 if (feat->f_get(fd, feat, npa->npa_idctl) != 0) {
 860                         warnx("unsupported feature: %s", feat->f_name);
 861                         continue;
 862                 }
 863         }
 864 
 865         free(flist);
 866         return (0);




  37 #include <stdio.h>
  38 #include <stdlib.h>
  39 #include <strings.h>
  40 #include <ctype.h>
  41 #include <err.h>
  42 #include <sys/sunddi.h>
  43 #include <libdevinfo.h>
  44 
  45 #include <sys/nvme.h>
  46 
  47 #include "nvmeadm.h"
  48 
  49 typedef struct nvme_process_arg nvme_process_arg_t;
  50 typedef struct nvme_feature nvme_feature_t;
  51 typedef struct nvmeadm_cmd nvmeadm_cmd_t;
  52 
  53 struct nvme_process_arg {
  54         int npa_argc;
  55         char **npa_argv;
  56         char *npa_name;
  57         char *npa_nsid;
  58         int npa_found;
  59         boolean_t npa_isns;
  60         const nvmeadm_cmd_t *npa_cmd;
  61         di_node_t npa_node;
  62         di_minor_t npa_minor;
  63         char *npa_path;
  64         char *npa_dsk;
  65         nvme_identify_ctrl_t *npa_idctl;
  66         nvme_identify_nsid_t *npa_idns;
  67         nvme_version_t *npa_version;
  68 };
  69 
  70 struct nvme_feature {
  71         char *f_name;
  72         char *f_short;
  73         uint8_t f_feature;
  74         size_t f_bufsize;
  75         uint_t f_getflags;
  76         int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *);
  77         void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *);
  78 };


 105 static int do_get_logpage(int, const nvme_process_arg_t *);
 106 static int do_get_feat_common(int, const nvme_feature_t *,
 107     nvme_identify_ctrl_t *);
 108 static int do_get_feat_intr_vect(int, const nvme_feature_t *,
 109     nvme_identify_ctrl_t *);
 110 static int do_get_features(int, const nvme_process_arg_t *);
 111 static int do_format(int, const nvme_process_arg_t *);
 112 static int do_secure_erase(int, const nvme_process_arg_t *);
 113 static int do_attach_detach(int, const nvme_process_arg_t *);
 114 
 115 static void usage_list(const char *);
 116 static void usage_identify(const char *);
 117 static void usage_get_logpage(const char *);
 118 static void usage_get_features(const char *);
 119 static void usage_format(const char *);
 120 static void usage_secure_erase(const char *);
 121 static void usage_attach_detach(const char *);
 122 
 123 int verbose;
 124 int debug;

 125 static int exitcode;
 126 
 127 static const nvmeadm_cmd_t nvmeadm_cmds[] = {
 128         {
 129                 "list",
 130                 "list controllers and namespaces",
 131                 NULL,
 132                 do_list, usage_list, B_TRUE
 133         },
 134         {
 135                 "identify",
 136                 "identify controllers and/or namespaces",
 137                 NULL,
 138                 do_identify, usage_identify, B_TRUE
 139         },
 140         {
 141                 "get-logpage",
 142                 "get a log page from controllers and/or namespaces",
 143                 NULL,
 144                 do_get_logpage, usage_get_logpage, B_TRUE


 296         /*
 297          * Make sure we're not running commands on multiple controllers that
 298          * aren't allowed to do that.
 299          */
 300         if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL &&
 301             cmd->c_multi == B_FALSE) {
 302                 warnx("%s not allowed on multiple controllers",
 303                     cmd->c_name);
 304                 usage(cmd);
 305                 exit(-1);
 306         }
 307 
 308         /*
 309          * Get controller/namespace arguments and run command.
 310          */
 311         npa.npa_name = strtok_r(argv[optind], ",", &lasts);
 312         do {
 313                 if (npa.npa_name != NULL) {
 314                         tmp = strchr(npa.npa_name, '/');
 315                         if (tmp != NULL) {

 316                                 *tmp++ = '\0';
 317                                 npa.npa_nsid = tmp;












 318                                 npa.npa_isns = B_TRUE;
 319                         }
 320                 }
 321 
 322                 if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL)
 323                         err(-1, "failed to initialize libdevinfo");
 324                 nvme_walk(&npa, node);
 325                 di_fini(node);
 326 
 327                 if (npa.npa_found == 0) {
 328                         if (npa.npa_name != NULL) {
 329                                 warnx("%s%.*s%.*s: no such controller or "
 330                                     "namespace", npa.npa_name,
 331                                     npa.npa_isns ? -1 : 0, "/",
 332                                     npa.npa_isns ? -1 : 0, npa.npa_nsid);
 333                         } else {
 334                                 warnx("no controllers found");
 335                         }
 336                         exitcode--;
 337                 }
 338                 npa.npa_found = 0;
 339                 npa.npa_name = strtok_r(NULL, ",", &lasts);
 340         } while (npa.npa_name != NULL);
 341 
 342         exit(exitcode);
 343 }
 344 
 345 static void
 346 usage(const nvmeadm_cmd_t *cmd)
 347 {
 348         (void) fprintf(stderr, "usage:\n");
 349         (void) fprintf(stderr, "  %s -h %s\n", getprogname(),
 350             cmd != NULL ? cmd->c_name : "[<command>]");
 351         (void) fprintf(stderr, "  %s [-dv] ", getprogname());
 352 
 353         if (cmd != NULL) {
 354                 cmd->c_usage(cmd->c_name);
 355         } else {
 356                 (void) fprintf(stderr,
 357                     "<command> <ctl>[/<ns>][,...] [<args>]\n");
 358                 (void) fprintf(stderr,
 359                     "\n  Manage NVMe controllers and namespaces.\n");
 360                 (void) fprintf(stderr, "\ncommands:\n");
 361 
 362                 for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++)
 363                         (void) fprintf(stderr, "  %-15s - %s\n",
 364                             cmd->c_name, cmd->c_desc);
 365         }
 366         (void) fprintf(stderr, "\nflags:\n"
 367             "  -h  print usage information\n"
 368             "  -d  print information useful for debugging %s\n"
 369             "  -v  print verbose information\n", getprogname());
 370         if (cmd != NULL && cmd->c_flagdesc != NULL)
 371                 (void) fprintf(stderr, "%s\n", cmd->c_flagdesc);
 372 }
 373 
 374 static boolean_t
 375 nvme_match(nvme_process_arg_t *npa)
 376 {
 377         char *name;
 378         char *nsid = NULL;
 379 
 380         if (npa->npa_name == NULL)
 381                 return (B_TRUE);
 382 
 383         if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node),
 384             di_instance(npa->npa_node)) < 0)
 385                 err(-1, "nvme_match()");
 386 
 387         if (strcmp(name, npa->npa_name) != 0) {
 388                 free(name);
 389                 return (B_FALSE);
 390         }
 391 
 392         free(name);
 393 
 394         if (npa->npa_isns) {
 395                 if (npa->npa_nsid == NULL)
 396                         return (B_TRUE);


 397 
 398                 nsid = di_minor_name(npa->npa_minor);
 399 
 400                 if (nsid == NULL || strcmp(npa->npa_nsid, nsid) != 0)
 401                         return (B_FALSE);
 402         }
 403 
 404         return (B_TRUE);
 405 }
 406 
 407 char *
 408 nvme_dskname(const nvme_process_arg_t *npa)
 409 {
 410         char *path = NULL;
 411         di_node_t child;
 412         di_dim_t dim;
 413         char *addr;
 414 
 415         dim = di_dim_init();
 416 
 417         for (child = di_child_node(npa->npa_node);
 418             child != DI_NODE_NIL;
 419             child = di_sibling_node(child)) {
 420                 addr = di_bus_addr(child);
 421                 if (addr == NULL)
 422                         continue;


 446 
 447         di_dim_fini(dim);
 448         return (path);
 449 }
 450 
 451 static int
 452 nvme_process(di_node_t node, di_minor_t minor, void *arg)
 453 {
 454         nvme_process_arg_t *npa = arg;
 455         int fd;
 456 
 457         npa->npa_node = node;
 458         npa->npa_minor = minor;
 459 
 460         if (!nvme_match(npa))
 461                 return (DI_WALK_CONTINUE);
 462 
 463         if ((fd = nvme_open(minor)) < 0)
 464                 return (DI_WALK_CONTINUE);
 465 
 466         npa->npa_found++;
 467 
 468         npa->npa_path = di_devfs_path(node);
 469         if (npa->npa_path == NULL)
 470                 goto out;
 471 
 472         npa->npa_version = nvme_version(fd);
 473         if (npa->npa_version == NULL)
 474                 goto out;
 475 
 476         npa->npa_idctl = nvme_identify_ctrl(fd);
 477         if (npa->npa_idctl == NULL)
 478                 goto out;
 479 
 480         npa->npa_idns = nvme_identify_nsid(fd);
 481         if (npa->npa_idns == NULL)
 482                 goto out;
 483 
 484         if (npa->npa_isns)
 485                 npa->npa_dsk = nvme_dskname(npa);
 486 


 560         ns_npa.npa_cmd = &cmd;
 561 
 562         nvme_walk(&ns_npa, npa->npa_node);
 563 
 564         free(name);
 565 
 566         return (exitcode);
 567 }
 568 
 569 static void
 570 usage_identify(const char *c_name)
 571 {
 572         (void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n"
 573             "  Print detailed information about the specified NVMe "
 574             "controllers and/or name-\n  spaces.\n", c_name);
 575 }
 576 
 577 static int
 578 do_identify(int fd, const nvme_process_arg_t *npa)
 579 {
 580         if (!npa->npa_isns) {
 581                 nvme_capabilities_t *cap;
 582 
 583                 cap = nvme_capabilities(fd);
 584                 if (cap == NULL)
 585                         return (-1);
 586 
 587                 (void) printf("%s: ", npa->npa_name);
 588                 nvme_print_identify_ctrl(npa->npa_idctl, cap,
 589                     npa->npa_version);
 590 
 591                 free(cap);
 592         } else {
 593                 (void) printf("%s/%s: ", npa->npa_name,
 594                     di_minor_name(npa->npa_minor));
 595                 nvme_print_identify_nsid(npa->npa_idns,
 596                     npa->npa_version);
 597         }
 598 
 599         return (0);
 600 }
 601 
 602 static void
 603 usage_get_logpage(const char *c_name)
 604 {
 605         (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n"
 606             "  Print the specified log page of the specified NVMe "
 607             "controllers and/or name-\n  spaces. Supported log pages "
 608             "are error, health, and firmware.\n", c_name);
 609 }
 610 
 611 static int
 612 do_get_logpage_error(int fd, const nvme_process_arg_t *npa)
 613 {
 614         int nlog = npa->npa_idctl->id_elpe + 1;
 615         size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog;
 616         nvme_error_log_entry_t *elog;
 617 
 618         if (npa->npa_isns)
 619                 errx(-1, "Error Log not available on a per-namespace basis");
 620 
 621         elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize);
 622 
 623         if (elog == NULL)
 624                 return (-1);
 625 
 626         nlog = bufsize / sizeof (nvme_error_log_entry_t);
 627 
 628         (void) printf("%s: ", npa->npa_name);
 629         nvme_print_error_log(nlog, elog);
 630 
 631         free(elog);
 632 
 633         return (0);
 634 }
 635 
 636 static int
 637 do_get_logpage_health(int fd, const nvme_process_arg_t *npa)
 638 {
 639         size_t bufsize = sizeof (nvme_health_log_t);
 640         nvme_health_log_t *hlog;
 641 
 642         if (npa->npa_isns) {
 643                 if (npa->npa_idctl->id_lpa.lp_smart == 0)
 644                         errx(-1, "SMART/Health information not available "
 645                             "on a per-namespace basis on this controller");
 646         }
 647 
 648         hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize);
 649 
 650         if (hlog == NULL)
 651                 return (-1);
 652 
 653         (void) printf("%s: ", npa->npa_name);
 654         nvme_print_health_log(hlog, npa->npa_idctl);
 655 
 656         free(hlog);
 657 
 658         return (0);
 659 }
 660 
 661 static int
 662 do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa)
 663 {
 664         size_t bufsize = sizeof (nvme_fwslot_log_t);
 665         nvme_fwslot_log_t *fwlog;
 666 
 667         if (npa->npa_isns)
 668                 errx(-1, "Firmware Slot information not available on a "
 669                     "per-namespace basis");
 670 
 671         fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize);
 672 
 673         if (fwlog == NULL)
 674                 return (-1);
 675 
 676         (void) printf("%s: ", npa->npa_name);
 677         nvme_print_fwslot_log(fwlog);
 678 
 679         free(fwlog);
 680 
 681         return (0);
 682 }
 683 
 684 static int
 685 do_get_logpage(int fd, const nvme_process_arg_t *npa)
 686 {
 687         int ret = 0;


 776 
 777         return (0);
 778 }
 779 
 780 static int
 781 do_get_features(int fd, const nvme_process_arg_t *npa)
 782 {
 783         const nvme_feature_t *feat;
 784         char *f, *flist, *lasts;
 785         boolean_t header_printed = B_FALSE;
 786 
 787         if (npa->npa_argc > 1)
 788                 errx(-1, "unexpected arguments");
 789 
 790         /*
 791          * No feature list given, print all supported features.
 792          */
 793         if (npa->npa_argc == 0) {
 794                 (void) printf("%s: Get Features\n", npa->npa_name);
 795                 for (feat = &features[0]; feat->f_feature != 0; feat++) {
 796                         if ((npa->npa_isns &&
 797                             (feat->f_getflags & NVMEADM_NS) == 0) ||
 798                             (!npa->npa_isns &&
 799                             (feat->f_getflags & NVMEADM_CTRL) == 0))
 800                                 continue;
 801 
 802                         (void) feat->f_get(fd, feat, npa->npa_idctl);
 803                 }
 804 
 805                 return (0);
 806         }
 807 
 808         /*
 809          * Process feature list.
 810          */
 811         flist = strdup(npa->npa_argv[0]);
 812         if (flist == NULL)
 813                 err(-1, "do_get_features");
 814 
 815         for (f = strtok_r(flist, ",", &lasts);
 816             f != NULL;
 817             f = strtok_r(NULL, ",", &lasts)) {
 818                 while (isspace(*f))
 819                         f++;
 820 
 821                 for (feat = &features[0]; feat->f_feature != 0; feat++) {
 822                         if (strncasecmp(feat->f_name, f, strlen(f)) == 0 ||
 823                             strncasecmp(feat->f_short, f, strlen(f)) == 0)
 824                                 break;
 825                 }
 826 
 827                 if (feat->f_feature == 0) {
 828                         warnx("unknown feature %s", f);
 829                         continue;
 830                 }
 831 
 832                 if ((npa->npa_isns &&
 833                     (feat->f_getflags & NVMEADM_NS) == 0) ||
 834                     (!npa->npa_isns &&
 835                     (feat->f_getflags & NVMEADM_CTRL) == 0)) {
 836                         warnx("feature %s %s supported for namespaces",
 837                             feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ?
 838                             "only" : "not");
 839                         continue;
 840                 }
 841 
 842                 if (!header_printed) {
 843                         (void) printf("%s: Get Features\n", npa->npa_name);
 844                         header_printed = B_TRUE;
 845                 }
 846 
 847                 if (feat->f_get(fd, feat, npa->npa_idctl) != 0) {
 848                         warnx("unsupported feature: %s", feat->f_name);
 849                         continue;
 850                 }
 851         }
 852 
 853         free(flist);
 854         return (0);