1*ecee5a1fSHans Rosenfeld /* 2*ecee5a1fSHans Rosenfeld * This file and its contents are supplied under the terms of the 3*ecee5a1fSHans Rosenfeld * Common Development and Distribution License ("CDDL"), version 1.0. 4*ecee5a1fSHans Rosenfeld * You may only use this file in accordance with the terms of version 5*ecee5a1fSHans Rosenfeld * 1.0 of the CDDL. 6*ecee5a1fSHans Rosenfeld * 7*ecee5a1fSHans Rosenfeld * A full copy of the text of the CDDL should have accompanied this 8*ecee5a1fSHans Rosenfeld * source. A copy of the CDDL is also available via the Internet at 9*ecee5a1fSHans Rosenfeld * http://www.illumos.org/license/CDDL. 10*ecee5a1fSHans Rosenfeld */ 11*ecee5a1fSHans Rosenfeld 12*ecee5a1fSHans Rosenfeld /* 13*ecee5a1fSHans Rosenfeld * Copyright 2016 Nexenta Systems, Inc. 14*ecee5a1fSHans Rosenfeld */ 15*ecee5a1fSHans Rosenfeld 16*ecee5a1fSHans Rosenfeld /* 17*ecee5a1fSHans Rosenfeld * nvmeadm -- NVMe administration utility 18*ecee5a1fSHans Rosenfeld * 19*ecee5a1fSHans Rosenfeld * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args] 20*ecee5a1fSHans Rosenfeld * commands: list 21*ecee5a1fSHans Rosenfeld * identify 22*ecee5a1fSHans Rosenfeld * get-logpage <logpage name> 23*ecee5a1fSHans Rosenfeld * get-features <feature>[,...] 24*ecee5a1fSHans Rosenfeld * format ... 25*ecee5a1fSHans Rosenfeld * secure-erase ... 26*ecee5a1fSHans Rosenfeld * detach ... 27*ecee5a1fSHans Rosenfeld * attach ... 28*ecee5a1fSHans Rosenfeld * get-param ... 29*ecee5a1fSHans Rosenfeld * set-param ... 30*ecee5a1fSHans Rosenfeld * load-firmware ... 31*ecee5a1fSHans Rosenfeld * activate-firmware ... 32*ecee5a1fSHans Rosenfeld * write-uncorrectable ... 33*ecee5a1fSHans Rosenfeld * compare ... 34*ecee5a1fSHans Rosenfeld * compare-and-write ... 35*ecee5a1fSHans Rosenfeld */ 36*ecee5a1fSHans Rosenfeld 37*ecee5a1fSHans Rosenfeld #include <stdio.h> 38*ecee5a1fSHans Rosenfeld #include <stdlib.h> 39*ecee5a1fSHans Rosenfeld #include <strings.h> 40*ecee5a1fSHans Rosenfeld #include <ctype.h> 41*ecee5a1fSHans Rosenfeld #include <err.h> 42*ecee5a1fSHans Rosenfeld #include <sys/sunddi.h> 43*ecee5a1fSHans Rosenfeld #include <libdevinfo.h> 44*ecee5a1fSHans Rosenfeld 45*ecee5a1fSHans Rosenfeld #include <sys/nvme.h> 46*ecee5a1fSHans Rosenfeld 47*ecee5a1fSHans Rosenfeld #include "nvmeadm.h" 48*ecee5a1fSHans Rosenfeld 49*ecee5a1fSHans Rosenfeld typedef struct nvme_process_arg nvme_process_arg_t; 50*ecee5a1fSHans Rosenfeld typedef struct nvme_feature nvme_feature_t; 51*ecee5a1fSHans Rosenfeld typedef struct nvmeadm_cmd nvmeadm_cmd_t; 52*ecee5a1fSHans Rosenfeld 53*ecee5a1fSHans Rosenfeld struct nvme_process_arg { 54*ecee5a1fSHans Rosenfeld int npa_argc; 55*ecee5a1fSHans Rosenfeld char **npa_argv; 56*ecee5a1fSHans Rosenfeld char *npa_name; 57*ecee5a1fSHans Rosenfeld uint32_t npa_nsid; 58*ecee5a1fSHans Rosenfeld boolean_t npa_isns; 59*ecee5a1fSHans Rosenfeld const nvmeadm_cmd_t *npa_cmd; 60*ecee5a1fSHans Rosenfeld di_node_t npa_node; 61*ecee5a1fSHans Rosenfeld di_minor_t npa_minor; 62*ecee5a1fSHans Rosenfeld char *npa_path; 63*ecee5a1fSHans Rosenfeld char *npa_dsk; 64*ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *npa_idctl; 65*ecee5a1fSHans Rosenfeld nvme_identify_nsid_t *npa_idns; 66*ecee5a1fSHans Rosenfeld nvme_version_t *npa_version; 67*ecee5a1fSHans Rosenfeld }; 68*ecee5a1fSHans Rosenfeld 69*ecee5a1fSHans Rosenfeld struct nvme_feature { 70*ecee5a1fSHans Rosenfeld char *f_name; 71*ecee5a1fSHans Rosenfeld char *f_short; 72*ecee5a1fSHans Rosenfeld uint8_t f_feature; 73*ecee5a1fSHans Rosenfeld size_t f_bufsize; 74*ecee5a1fSHans Rosenfeld uint_t f_getflags; 75*ecee5a1fSHans Rosenfeld int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *); 76*ecee5a1fSHans Rosenfeld void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *); 77*ecee5a1fSHans Rosenfeld }; 78*ecee5a1fSHans Rosenfeld 79*ecee5a1fSHans Rosenfeld #define NVMEADM_CTRL 1 80*ecee5a1fSHans Rosenfeld #define NVMEADM_NS 2 81*ecee5a1fSHans Rosenfeld #define NVMEADM_BOTH (NVMEADM_CTRL | NVMEADM_NS) 82*ecee5a1fSHans Rosenfeld 83*ecee5a1fSHans Rosenfeld struct nvmeadm_cmd { 84*ecee5a1fSHans Rosenfeld char *c_name; 85*ecee5a1fSHans Rosenfeld char *c_desc; 86*ecee5a1fSHans Rosenfeld char *c_flagdesc; 87*ecee5a1fSHans Rosenfeld int (*c_func)(int, const nvme_process_arg_t *); 88*ecee5a1fSHans Rosenfeld void (*c_usage)(const char *); 89*ecee5a1fSHans Rosenfeld boolean_t c_multi; 90*ecee5a1fSHans Rosenfeld }; 91*ecee5a1fSHans Rosenfeld 92*ecee5a1fSHans Rosenfeld 93*ecee5a1fSHans Rosenfeld static void usage(const nvmeadm_cmd_t *); 94*ecee5a1fSHans Rosenfeld static void nvme_walk(nvme_process_arg_t *, di_node_t); 95*ecee5a1fSHans Rosenfeld static boolean_t nvme_match(nvme_process_arg_t *); 96*ecee5a1fSHans Rosenfeld 97*ecee5a1fSHans Rosenfeld static int nvme_process(di_node_t, di_minor_t, void *); 98*ecee5a1fSHans Rosenfeld 99*ecee5a1fSHans Rosenfeld static int do_list(int, const nvme_process_arg_t *); 100*ecee5a1fSHans Rosenfeld static int do_identify(int, const nvme_process_arg_t *); 101*ecee5a1fSHans Rosenfeld static int do_get_logpage_error(int, const nvme_process_arg_t *); 102*ecee5a1fSHans Rosenfeld static int do_get_logpage_health(int, const nvme_process_arg_t *); 103*ecee5a1fSHans Rosenfeld static int do_get_logpage_fwslot(int, const nvme_process_arg_t *); 104*ecee5a1fSHans Rosenfeld static int do_get_logpage(int, const nvme_process_arg_t *); 105*ecee5a1fSHans Rosenfeld static int do_get_feat_common(int, const nvme_feature_t *, 106*ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *); 107*ecee5a1fSHans Rosenfeld static int do_get_feat_intr_vect(int, const nvme_feature_t *, 108*ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *); 109*ecee5a1fSHans Rosenfeld static int do_get_features(int, const nvme_process_arg_t *); 110*ecee5a1fSHans Rosenfeld static int do_format(int, const nvme_process_arg_t *); 111*ecee5a1fSHans Rosenfeld static int do_secure_erase(int, const nvme_process_arg_t *); 112*ecee5a1fSHans Rosenfeld static int do_attach_detach(int, const nvme_process_arg_t *); 113*ecee5a1fSHans Rosenfeld 114*ecee5a1fSHans Rosenfeld static void usage_list(const char *); 115*ecee5a1fSHans Rosenfeld static void usage_identify(const char *); 116*ecee5a1fSHans Rosenfeld static void usage_get_logpage(const char *); 117*ecee5a1fSHans Rosenfeld static void usage_get_features(const char *); 118*ecee5a1fSHans Rosenfeld static void usage_format(const char *); 119*ecee5a1fSHans Rosenfeld static void usage_secure_erase(const char *); 120*ecee5a1fSHans Rosenfeld static void usage_attach_detach(const char *); 121*ecee5a1fSHans Rosenfeld 122*ecee5a1fSHans Rosenfeld int verbose; 123*ecee5a1fSHans Rosenfeld int debug; 124*ecee5a1fSHans Rosenfeld int found; 125*ecee5a1fSHans Rosenfeld static int exitcode; 126*ecee5a1fSHans Rosenfeld 127*ecee5a1fSHans Rosenfeld static const nvmeadm_cmd_t nvmeadm_cmds[] = { 128*ecee5a1fSHans Rosenfeld { 129*ecee5a1fSHans Rosenfeld "list", 130*ecee5a1fSHans Rosenfeld "list controllers and namespaces", 131*ecee5a1fSHans Rosenfeld NULL, 132*ecee5a1fSHans Rosenfeld do_list, usage_list, B_TRUE 133*ecee5a1fSHans Rosenfeld }, 134*ecee5a1fSHans Rosenfeld { 135*ecee5a1fSHans Rosenfeld "identify", 136*ecee5a1fSHans Rosenfeld "identify controllers and/or namespaces", 137*ecee5a1fSHans Rosenfeld NULL, 138*ecee5a1fSHans Rosenfeld do_identify, usage_identify, B_TRUE 139*ecee5a1fSHans Rosenfeld }, 140*ecee5a1fSHans Rosenfeld { 141*ecee5a1fSHans Rosenfeld "get-logpage", 142*ecee5a1fSHans Rosenfeld "get a log page from controllers and/or namespaces", 143*ecee5a1fSHans Rosenfeld NULL, 144*ecee5a1fSHans Rosenfeld do_get_logpage, usage_get_logpage, B_TRUE 145*ecee5a1fSHans Rosenfeld }, 146*ecee5a1fSHans Rosenfeld { 147*ecee5a1fSHans Rosenfeld "get-features", 148*ecee5a1fSHans Rosenfeld "get features from controllers and/or namespaces", 149*ecee5a1fSHans Rosenfeld NULL, 150*ecee5a1fSHans Rosenfeld do_get_features, usage_get_features, B_TRUE 151*ecee5a1fSHans Rosenfeld }, 152*ecee5a1fSHans Rosenfeld { 153*ecee5a1fSHans Rosenfeld "format", 154*ecee5a1fSHans Rosenfeld "format namespace(s) of a controller", 155*ecee5a1fSHans Rosenfeld NULL, 156*ecee5a1fSHans Rosenfeld do_format, usage_format, B_FALSE 157*ecee5a1fSHans Rosenfeld }, 158*ecee5a1fSHans Rosenfeld { 159*ecee5a1fSHans Rosenfeld "secure-erase", 160*ecee5a1fSHans Rosenfeld "secure erase namespace(s) of a controller", 161*ecee5a1fSHans Rosenfeld " -c Do a cryptographic erase.", 162*ecee5a1fSHans Rosenfeld do_secure_erase, usage_secure_erase, B_FALSE 163*ecee5a1fSHans Rosenfeld }, 164*ecee5a1fSHans Rosenfeld { 165*ecee5a1fSHans Rosenfeld "detach", 166*ecee5a1fSHans Rosenfeld "detach blkdev(7d) from namespace(s) of a controller", 167*ecee5a1fSHans Rosenfeld NULL, 168*ecee5a1fSHans Rosenfeld do_attach_detach, usage_attach_detach, B_FALSE 169*ecee5a1fSHans Rosenfeld }, 170*ecee5a1fSHans Rosenfeld { 171*ecee5a1fSHans Rosenfeld "attach", 172*ecee5a1fSHans Rosenfeld "attach blkdev(7d) to namespace(s) of a controller", 173*ecee5a1fSHans Rosenfeld NULL, 174*ecee5a1fSHans Rosenfeld do_attach_detach, usage_attach_detach, B_FALSE 175*ecee5a1fSHans Rosenfeld }, 176*ecee5a1fSHans Rosenfeld { 177*ecee5a1fSHans Rosenfeld NULL, NULL, NULL, 178*ecee5a1fSHans Rosenfeld NULL, NULL, B_FALSE 179*ecee5a1fSHans Rosenfeld } 180*ecee5a1fSHans Rosenfeld }; 181*ecee5a1fSHans Rosenfeld 182*ecee5a1fSHans Rosenfeld static const nvme_feature_t features[] = { 183*ecee5a1fSHans Rosenfeld { "Arbitration", "", 184*ecee5a1fSHans Rosenfeld NVME_FEAT_ARBITRATION, 0, NVMEADM_CTRL, 185*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_arbitration }, 186*ecee5a1fSHans Rosenfeld { "Power Management", "", 187*ecee5a1fSHans Rosenfeld NVME_FEAT_POWER_MGMT, 0, NVMEADM_CTRL, 188*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_power_mgmt }, 189*ecee5a1fSHans Rosenfeld { "LBA Range Type", "range", 190*ecee5a1fSHans Rosenfeld NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_NS, 191*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_lba_range }, 192*ecee5a1fSHans Rosenfeld { "Temperature Threshold", "", 193*ecee5a1fSHans Rosenfeld NVME_FEAT_TEMPERATURE, 0, NVMEADM_CTRL, 194*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_temperature }, 195*ecee5a1fSHans Rosenfeld { "Error Recovery", "", 196*ecee5a1fSHans Rosenfeld NVME_FEAT_ERROR, 0, NVMEADM_CTRL, 197*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_error }, 198*ecee5a1fSHans Rosenfeld { "Volatile Write Cache", "cache", 199*ecee5a1fSHans Rosenfeld NVME_FEAT_WRITE_CACHE, 0, NVMEADM_CTRL, 200*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_write_cache }, 201*ecee5a1fSHans Rosenfeld { "Number of Queues", "queues", 202*ecee5a1fSHans Rosenfeld NVME_FEAT_NQUEUES, 0, NVMEADM_CTRL, 203*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_nqueues }, 204*ecee5a1fSHans Rosenfeld { "Interrupt Coalescing", "coalescing", 205*ecee5a1fSHans Rosenfeld NVME_FEAT_INTR_COAL, 0, NVMEADM_CTRL, 206*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_intr_coal }, 207*ecee5a1fSHans Rosenfeld { "Interrupt Vector Configuration", "vector", 208*ecee5a1fSHans Rosenfeld NVME_FEAT_INTR_VECT, 0, NVMEADM_CTRL, 209*ecee5a1fSHans Rosenfeld do_get_feat_intr_vect, nvme_print_feat_intr_vect }, 210*ecee5a1fSHans Rosenfeld { "Write Atomicity", "atomicity", 211*ecee5a1fSHans Rosenfeld NVME_FEAT_WRITE_ATOM, 0, NVMEADM_CTRL, 212*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_write_atom }, 213*ecee5a1fSHans Rosenfeld { "Asynchronous Event Configuration", "event", 214*ecee5a1fSHans Rosenfeld NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_CTRL, 215*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_async_event }, 216*ecee5a1fSHans Rosenfeld { "Autonomous Power State Transition", "", 217*ecee5a1fSHans Rosenfeld NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_CTRL, 218*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_auto_pst }, 219*ecee5a1fSHans Rosenfeld { "Software Progress Marker", "progress", 220*ecee5a1fSHans Rosenfeld NVME_FEAT_PROGRESS, 0, NVMEADM_CTRL, 221*ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_progress }, 222*ecee5a1fSHans Rosenfeld { NULL, NULL, 0, 0, B_FALSE, NULL } 223*ecee5a1fSHans Rosenfeld }; 224*ecee5a1fSHans Rosenfeld 225*ecee5a1fSHans Rosenfeld 226*ecee5a1fSHans Rosenfeld int 227*ecee5a1fSHans Rosenfeld main(int argc, char **argv) 228*ecee5a1fSHans Rosenfeld { 229*ecee5a1fSHans Rosenfeld int c; 230*ecee5a1fSHans Rosenfeld extern int optind; 231*ecee5a1fSHans Rosenfeld const nvmeadm_cmd_t *cmd; 232*ecee5a1fSHans Rosenfeld di_node_t node; 233*ecee5a1fSHans Rosenfeld nvme_process_arg_t npa = { 0 }; 234*ecee5a1fSHans Rosenfeld int help = 0; 235*ecee5a1fSHans Rosenfeld char *tmp, *lasts = NULL; 236*ecee5a1fSHans Rosenfeld 237*ecee5a1fSHans Rosenfeld while ((c = getopt(argc, argv, "dhv")) != -1) { 238*ecee5a1fSHans Rosenfeld switch (c) { 239*ecee5a1fSHans Rosenfeld case 'd': 240*ecee5a1fSHans Rosenfeld debug++; 241*ecee5a1fSHans Rosenfeld break; 242*ecee5a1fSHans Rosenfeld case 'v': 243*ecee5a1fSHans Rosenfeld verbose++; 244*ecee5a1fSHans Rosenfeld break; 245*ecee5a1fSHans Rosenfeld case 'h': 246*ecee5a1fSHans Rosenfeld help++; 247*ecee5a1fSHans Rosenfeld break; 248*ecee5a1fSHans Rosenfeld case '?': 249*ecee5a1fSHans Rosenfeld usage(NULL); 250*ecee5a1fSHans Rosenfeld exit(-1); 251*ecee5a1fSHans Rosenfeld } 252*ecee5a1fSHans Rosenfeld } 253*ecee5a1fSHans Rosenfeld 254*ecee5a1fSHans Rosenfeld if (optind == argc) { 255*ecee5a1fSHans Rosenfeld usage(NULL); 256*ecee5a1fSHans Rosenfeld if (help) 257*ecee5a1fSHans Rosenfeld exit(0); 258*ecee5a1fSHans Rosenfeld else 259*ecee5a1fSHans Rosenfeld exit(-1); 260*ecee5a1fSHans Rosenfeld } 261*ecee5a1fSHans Rosenfeld 262*ecee5a1fSHans Rosenfeld /* Look up the specified command in the command table. */ 263*ecee5a1fSHans Rosenfeld for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) 264*ecee5a1fSHans Rosenfeld if (strcmp(cmd->c_name, argv[optind]) == 0) 265*ecee5a1fSHans Rosenfeld break; 266*ecee5a1fSHans Rosenfeld 267*ecee5a1fSHans Rosenfeld if (cmd->c_name == NULL) { 268*ecee5a1fSHans Rosenfeld usage(NULL); 269*ecee5a1fSHans Rosenfeld exit(-1); 270*ecee5a1fSHans Rosenfeld } 271*ecee5a1fSHans Rosenfeld 272*ecee5a1fSHans Rosenfeld if (help) { 273*ecee5a1fSHans Rosenfeld usage(cmd); 274*ecee5a1fSHans Rosenfeld exit(0); 275*ecee5a1fSHans Rosenfeld } 276*ecee5a1fSHans Rosenfeld 277*ecee5a1fSHans Rosenfeld npa.npa_cmd = cmd; 278*ecee5a1fSHans Rosenfeld 279*ecee5a1fSHans Rosenfeld optind++; 280*ecee5a1fSHans Rosenfeld 281*ecee5a1fSHans Rosenfeld /* 282*ecee5a1fSHans Rosenfeld * All commands but "list" require a ctl/ns argument. 283*ecee5a1fSHans Rosenfeld */ 284*ecee5a1fSHans Rosenfeld if ((optind == argc || (strncmp(argv[optind], "nvme", 4) != 0)) && 285*ecee5a1fSHans Rosenfeld cmd->c_func != do_list) { 286*ecee5a1fSHans Rosenfeld warnx("missing controller/namespace name"); 287*ecee5a1fSHans Rosenfeld usage(cmd); 288*ecee5a1fSHans Rosenfeld exit(-1); 289*ecee5a1fSHans Rosenfeld } 290*ecee5a1fSHans Rosenfeld 291*ecee5a1fSHans Rosenfeld 292*ecee5a1fSHans Rosenfeld /* Store the remaining arguments for use by the command. */ 293*ecee5a1fSHans Rosenfeld npa.npa_argc = argc - optind - 1; 294*ecee5a1fSHans Rosenfeld npa.npa_argv = &argv[optind + 1]; 295*ecee5a1fSHans Rosenfeld 296*ecee5a1fSHans Rosenfeld /* 297*ecee5a1fSHans Rosenfeld * Make sure we're not running commands on multiple controllers that 298*ecee5a1fSHans Rosenfeld * aren't allowed to do that. 299*ecee5a1fSHans Rosenfeld */ 300*ecee5a1fSHans Rosenfeld if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL && 301*ecee5a1fSHans Rosenfeld cmd->c_multi == B_FALSE) { 302*ecee5a1fSHans Rosenfeld warnx("%s not allowed on multiple controllers", 303*ecee5a1fSHans Rosenfeld cmd->c_name); 304*ecee5a1fSHans Rosenfeld usage(cmd); 305*ecee5a1fSHans Rosenfeld exit(-1); 306*ecee5a1fSHans Rosenfeld } 307*ecee5a1fSHans Rosenfeld 308*ecee5a1fSHans Rosenfeld /* 309*ecee5a1fSHans Rosenfeld * Get controller/namespace arguments and run command. 310*ecee5a1fSHans Rosenfeld */ 311*ecee5a1fSHans Rosenfeld npa.npa_name = strtok_r(argv[optind], ",", &lasts); 312*ecee5a1fSHans Rosenfeld do { 313*ecee5a1fSHans Rosenfeld if (npa.npa_name != NULL) { 314*ecee5a1fSHans Rosenfeld tmp = strchr(npa.npa_name, '/'); 315*ecee5a1fSHans Rosenfeld if (tmp != NULL) { 316*ecee5a1fSHans Rosenfeld unsigned long nsid; 317*ecee5a1fSHans Rosenfeld *tmp++ = '\0'; 318*ecee5a1fSHans Rosenfeld errno = 0; 319*ecee5a1fSHans Rosenfeld nsid = strtoul(tmp, NULL, 10); 320*ecee5a1fSHans Rosenfeld if (nsid >= UINT32_MAX || errno != 0) { 321*ecee5a1fSHans Rosenfeld warn("invalid namespace %s", tmp); 322*ecee5a1fSHans Rosenfeld exitcode--; 323*ecee5a1fSHans Rosenfeld continue; 324*ecee5a1fSHans Rosenfeld } 325*ecee5a1fSHans Rosenfeld if (nsid == 0) { 326*ecee5a1fSHans Rosenfeld warnx("invalid namespace %s", tmp); 327*ecee5a1fSHans Rosenfeld exitcode--; 328*ecee5a1fSHans Rosenfeld continue; 329*ecee5a1fSHans Rosenfeld } 330*ecee5a1fSHans Rosenfeld npa.npa_nsid = nsid; 331*ecee5a1fSHans Rosenfeld npa.npa_isns = B_TRUE; 332*ecee5a1fSHans Rosenfeld } 333*ecee5a1fSHans Rosenfeld } 334*ecee5a1fSHans Rosenfeld 335*ecee5a1fSHans Rosenfeld if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL) 336*ecee5a1fSHans Rosenfeld err(-1, "failed to initialize libdevinfo"); 337*ecee5a1fSHans Rosenfeld nvme_walk(&npa, node); 338*ecee5a1fSHans Rosenfeld di_fini(node); 339*ecee5a1fSHans Rosenfeld 340*ecee5a1fSHans Rosenfeld if (found == 0) { 341*ecee5a1fSHans Rosenfeld if (npa.npa_name != NULL) { 342*ecee5a1fSHans Rosenfeld warnx("%s%.*s%.*d: no such controller or " 343*ecee5a1fSHans Rosenfeld "namespace", npa.npa_name, 344*ecee5a1fSHans Rosenfeld npa.npa_nsid > 0 ? -1 : 0, "/", 345*ecee5a1fSHans Rosenfeld npa.npa_nsid > 0 ? -1 : 0, npa.npa_nsid); 346*ecee5a1fSHans Rosenfeld } else { 347*ecee5a1fSHans Rosenfeld warnx("no controllers found"); 348*ecee5a1fSHans Rosenfeld } 349*ecee5a1fSHans Rosenfeld exitcode--; 350*ecee5a1fSHans Rosenfeld } 351*ecee5a1fSHans Rosenfeld found = 0; 352*ecee5a1fSHans Rosenfeld npa.npa_name = strtok_r(NULL, ",", &lasts); 353*ecee5a1fSHans Rosenfeld } while (npa.npa_name != NULL); 354*ecee5a1fSHans Rosenfeld 355*ecee5a1fSHans Rosenfeld exit(exitcode); 356*ecee5a1fSHans Rosenfeld } 357*ecee5a1fSHans Rosenfeld 358*ecee5a1fSHans Rosenfeld static void 359*ecee5a1fSHans Rosenfeld usage(const nvmeadm_cmd_t *cmd) 360*ecee5a1fSHans Rosenfeld { 361*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "usage:\n"); 362*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %s -h %s\n", getprogname(), 363*ecee5a1fSHans Rosenfeld cmd != NULL ? cmd->c_name : "[<command>]"); 364*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %s [-dv] ", getprogname()); 365*ecee5a1fSHans Rosenfeld 366*ecee5a1fSHans Rosenfeld if (cmd != NULL) { 367*ecee5a1fSHans Rosenfeld cmd->c_usage(cmd->c_name); 368*ecee5a1fSHans Rosenfeld } else { 369*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, 370*ecee5a1fSHans Rosenfeld "<command> <ctl>[/<ns>][,...] [<args>]\n"); 371*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, 372*ecee5a1fSHans Rosenfeld "\n Manage NVMe controllers and namespaces.\n"); 373*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "\ncommands:\n"); 374*ecee5a1fSHans Rosenfeld 375*ecee5a1fSHans Rosenfeld for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) 376*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %-15s - %s\n", 377*ecee5a1fSHans Rosenfeld cmd->c_name, cmd->c_desc); 378*ecee5a1fSHans Rosenfeld } 379*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "\nflags:\n" 380*ecee5a1fSHans Rosenfeld " -h print usage information\n" 381*ecee5a1fSHans Rosenfeld " -d print information useful for debugging %s\n" 382*ecee5a1fSHans Rosenfeld " -v print verbose information\n", getprogname()); 383*ecee5a1fSHans Rosenfeld if (cmd != NULL && cmd->c_flagdesc != NULL) 384*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s\n", cmd->c_flagdesc); 385*ecee5a1fSHans Rosenfeld } 386*ecee5a1fSHans Rosenfeld 387*ecee5a1fSHans Rosenfeld static boolean_t 388*ecee5a1fSHans Rosenfeld nvme_match(nvme_process_arg_t *npa) 389*ecee5a1fSHans Rosenfeld { 390*ecee5a1fSHans Rosenfeld char *name; 391*ecee5a1fSHans Rosenfeld uint32_t nsid = 0; 392*ecee5a1fSHans Rosenfeld 393*ecee5a1fSHans Rosenfeld if (npa->npa_name == NULL) 394*ecee5a1fSHans Rosenfeld return (B_TRUE); 395*ecee5a1fSHans Rosenfeld 396*ecee5a1fSHans Rosenfeld if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 397*ecee5a1fSHans Rosenfeld di_instance(npa->npa_node)) < 0) 398*ecee5a1fSHans Rosenfeld err(-1, "nvme_match()"); 399*ecee5a1fSHans Rosenfeld 400*ecee5a1fSHans Rosenfeld if (strcmp(name, npa->npa_name) != 0) { 401*ecee5a1fSHans Rosenfeld free(name); 402*ecee5a1fSHans Rosenfeld return (B_FALSE); 403*ecee5a1fSHans Rosenfeld } 404*ecee5a1fSHans Rosenfeld 405*ecee5a1fSHans Rosenfeld free(name); 406*ecee5a1fSHans Rosenfeld 407*ecee5a1fSHans Rosenfeld if (npa->npa_isns) { 408*ecee5a1fSHans Rosenfeld if (npa->npa_nsid == 0) 409*ecee5a1fSHans Rosenfeld return (B_TRUE); 410*ecee5a1fSHans Rosenfeld nsid = strtoul(di_minor_name(npa->npa_minor), NULL, 10); 411*ecee5a1fSHans Rosenfeld } 412*ecee5a1fSHans Rosenfeld 413*ecee5a1fSHans Rosenfeld if (npa->npa_isns && npa->npa_nsid != nsid) 414*ecee5a1fSHans Rosenfeld return (B_FALSE); 415*ecee5a1fSHans Rosenfeld 416*ecee5a1fSHans Rosenfeld return (B_TRUE); 417*ecee5a1fSHans Rosenfeld } 418*ecee5a1fSHans Rosenfeld 419*ecee5a1fSHans Rosenfeld char * 420*ecee5a1fSHans Rosenfeld nvme_dskname(const nvme_process_arg_t *npa) 421*ecee5a1fSHans Rosenfeld { 422*ecee5a1fSHans Rosenfeld char *path = NULL; 423*ecee5a1fSHans Rosenfeld di_node_t child; 424*ecee5a1fSHans Rosenfeld di_dim_t dim; 425*ecee5a1fSHans Rosenfeld char *addr; 426*ecee5a1fSHans Rosenfeld 427*ecee5a1fSHans Rosenfeld dim = di_dim_init(); 428*ecee5a1fSHans Rosenfeld 429*ecee5a1fSHans Rosenfeld for (child = di_child_node(npa->npa_node); 430*ecee5a1fSHans Rosenfeld child != DI_NODE_NIL; 431*ecee5a1fSHans Rosenfeld child = di_sibling_node(child)) { 432*ecee5a1fSHans Rosenfeld addr = di_bus_addr(child); 433*ecee5a1fSHans Rosenfeld if (addr == NULL) 434*ecee5a1fSHans Rosenfeld continue; 435*ecee5a1fSHans Rosenfeld 436*ecee5a1fSHans Rosenfeld if (addr[0] == 'w') 437*ecee5a1fSHans Rosenfeld addr++; 438*ecee5a1fSHans Rosenfeld 439*ecee5a1fSHans Rosenfeld if (strncasecmp(addr, di_minor_name(npa->npa_minor), 440*ecee5a1fSHans Rosenfeld strchrnul(addr, ',') - addr) != 0) 441*ecee5a1fSHans Rosenfeld continue; 442*ecee5a1fSHans Rosenfeld 443*ecee5a1fSHans Rosenfeld path = di_dim_path_dev(dim, di_driver_name(child), 444*ecee5a1fSHans Rosenfeld di_instance(child), "c"); 445*ecee5a1fSHans Rosenfeld 446*ecee5a1fSHans Rosenfeld if (path != NULL) { 447*ecee5a1fSHans Rosenfeld path[strlen(path) - 2] = '\0'; 448*ecee5a1fSHans Rosenfeld path = strrchr(path, '/') + 1; 449*ecee5a1fSHans Rosenfeld if (path != NULL) { 450*ecee5a1fSHans Rosenfeld path = strdup(path); 451*ecee5a1fSHans Rosenfeld if (path == NULL) 452*ecee5a1fSHans Rosenfeld err(-1, "nvme_dskname"); 453*ecee5a1fSHans Rosenfeld } 454*ecee5a1fSHans Rosenfeld } 455*ecee5a1fSHans Rosenfeld 456*ecee5a1fSHans Rosenfeld break; 457*ecee5a1fSHans Rosenfeld } 458*ecee5a1fSHans Rosenfeld 459*ecee5a1fSHans Rosenfeld di_dim_fini(dim); 460*ecee5a1fSHans Rosenfeld return (path); 461*ecee5a1fSHans Rosenfeld } 462*ecee5a1fSHans Rosenfeld 463*ecee5a1fSHans Rosenfeld static int 464*ecee5a1fSHans Rosenfeld nvme_process(di_node_t node, di_minor_t minor, void *arg) 465*ecee5a1fSHans Rosenfeld { 466*ecee5a1fSHans Rosenfeld nvme_process_arg_t *npa = arg; 467*ecee5a1fSHans Rosenfeld int fd; 468*ecee5a1fSHans Rosenfeld 469*ecee5a1fSHans Rosenfeld npa->npa_node = node; 470*ecee5a1fSHans Rosenfeld npa->npa_minor = minor; 471*ecee5a1fSHans Rosenfeld 472*ecee5a1fSHans Rosenfeld if (!nvme_match(npa)) 473*ecee5a1fSHans Rosenfeld return (DI_WALK_CONTINUE); 474*ecee5a1fSHans Rosenfeld 475*ecee5a1fSHans Rosenfeld if ((fd = nvme_open(minor)) < 0) 476*ecee5a1fSHans Rosenfeld return (DI_WALK_CONTINUE); 477*ecee5a1fSHans Rosenfeld 478*ecee5a1fSHans Rosenfeld found++; 479*ecee5a1fSHans Rosenfeld 480*ecee5a1fSHans Rosenfeld npa->npa_path = di_devfs_path(node); 481*ecee5a1fSHans Rosenfeld if (npa->npa_path == NULL) 482*ecee5a1fSHans Rosenfeld goto out; 483*ecee5a1fSHans Rosenfeld 484*ecee5a1fSHans Rosenfeld npa->npa_version = nvme_version(fd); 485*ecee5a1fSHans Rosenfeld if (npa->npa_version == NULL) 486*ecee5a1fSHans Rosenfeld goto out; 487*ecee5a1fSHans Rosenfeld 488*ecee5a1fSHans Rosenfeld npa->npa_idctl = nvme_identify_ctrl(fd); 489*ecee5a1fSHans Rosenfeld if (npa->npa_idctl == NULL) 490*ecee5a1fSHans Rosenfeld goto out; 491*ecee5a1fSHans Rosenfeld 492*ecee5a1fSHans Rosenfeld npa->npa_idns = nvme_identify_nsid(fd); 493*ecee5a1fSHans Rosenfeld if (npa->npa_idns == NULL) 494*ecee5a1fSHans Rosenfeld goto out; 495*ecee5a1fSHans Rosenfeld 496*ecee5a1fSHans Rosenfeld if (npa->npa_isns) 497*ecee5a1fSHans Rosenfeld npa->npa_dsk = nvme_dskname(npa); 498*ecee5a1fSHans Rosenfeld 499*ecee5a1fSHans Rosenfeld exitcode += npa->npa_cmd->c_func(fd, npa); 500*ecee5a1fSHans Rosenfeld 501*ecee5a1fSHans Rosenfeld out: 502*ecee5a1fSHans Rosenfeld di_devfs_path_free(npa->npa_path); 503*ecee5a1fSHans Rosenfeld free(npa->npa_dsk); 504*ecee5a1fSHans Rosenfeld free(npa->npa_version); 505*ecee5a1fSHans Rosenfeld free(npa->npa_idctl); 506*ecee5a1fSHans Rosenfeld free(npa->npa_idns); 507*ecee5a1fSHans Rosenfeld 508*ecee5a1fSHans Rosenfeld npa->npa_version = NULL; 509*ecee5a1fSHans Rosenfeld npa->npa_idctl = NULL; 510*ecee5a1fSHans Rosenfeld npa->npa_idns = NULL; 511*ecee5a1fSHans Rosenfeld 512*ecee5a1fSHans Rosenfeld nvme_close(fd); 513*ecee5a1fSHans Rosenfeld 514*ecee5a1fSHans Rosenfeld return (DI_WALK_CONTINUE); 515*ecee5a1fSHans Rosenfeld } 516*ecee5a1fSHans Rosenfeld 517*ecee5a1fSHans Rosenfeld static void 518*ecee5a1fSHans Rosenfeld nvme_walk(nvme_process_arg_t *npa, di_node_t node) 519*ecee5a1fSHans Rosenfeld { 520*ecee5a1fSHans Rosenfeld char *minor_nodetype = DDI_NT_NVME_NEXUS; 521*ecee5a1fSHans Rosenfeld 522*ecee5a1fSHans Rosenfeld if (npa->npa_isns) 523*ecee5a1fSHans Rosenfeld minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT; 524*ecee5a1fSHans Rosenfeld 525*ecee5a1fSHans Rosenfeld (void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process); 526*ecee5a1fSHans Rosenfeld } 527*ecee5a1fSHans Rosenfeld 528*ecee5a1fSHans Rosenfeld static void 529*ecee5a1fSHans Rosenfeld usage_list(const char *c_name) 530*ecee5a1fSHans Rosenfeld { 531*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s [<ctl>[/<ns>][,...]\n\n" 532*ecee5a1fSHans Rosenfeld " List NVMe controllers and their namespaces. If no " 533*ecee5a1fSHans Rosenfeld "controllers and/or name-\n spaces are specified, all " 534*ecee5a1fSHans Rosenfeld "controllers and namespaces in the system will be\n " 535*ecee5a1fSHans Rosenfeld "listed.\n", c_name); 536*ecee5a1fSHans Rosenfeld } 537*ecee5a1fSHans Rosenfeld 538*ecee5a1fSHans Rosenfeld static int 539*ecee5a1fSHans Rosenfeld do_list_nsid(int fd, const nvme_process_arg_t *npa) 540*ecee5a1fSHans Rosenfeld { 541*ecee5a1fSHans Rosenfeld _NOTE(ARGUNUSED(fd)); 542*ecee5a1fSHans Rosenfeld 543*ecee5a1fSHans Rosenfeld (void) printf(" %s/%s (%s): ", npa->npa_name, 544*ecee5a1fSHans Rosenfeld di_minor_name(npa->npa_minor), 545*ecee5a1fSHans Rosenfeld npa->npa_dsk != NULL ? npa->npa_dsk : "unattached"); 546*ecee5a1fSHans Rosenfeld nvme_print_nsid_summary(npa->npa_idns); 547*ecee5a1fSHans Rosenfeld 548*ecee5a1fSHans Rosenfeld return (0); 549*ecee5a1fSHans Rosenfeld } 550*ecee5a1fSHans Rosenfeld 551*ecee5a1fSHans Rosenfeld static int 552*ecee5a1fSHans Rosenfeld do_list(int fd, const nvme_process_arg_t *npa) 553*ecee5a1fSHans Rosenfeld { 554*ecee5a1fSHans Rosenfeld _NOTE(ARGUNUSED(fd)); 555*ecee5a1fSHans Rosenfeld 556*ecee5a1fSHans Rosenfeld nvme_process_arg_t ns_npa = { 0 }; 557*ecee5a1fSHans Rosenfeld nvmeadm_cmd_t cmd = { 0 }; 558*ecee5a1fSHans Rosenfeld char *name; 559*ecee5a1fSHans Rosenfeld 560*ecee5a1fSHans Rosenfeld if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 561*ecee5a1fSHans Rosenfeld di_instance(npa->npa_node)) < 0) 562*ecee5a1fSHans Rosenfeld err(-1, "do_list()"); 563*ecee5a1fSHans Rosenfeld 564*ecee5a1fSHans Rosenfeld (void) printf("%s: ", name); 565*ecee5a1fSHans Rosenfeld nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version); 566*ecee5a1fSHans Rosenfeld 567*ecee5a1fSHans Rosenfeld ns_npa.npa_name = name; 568*ecee5a1fSHans Rosenfeld ns_npa.npa_isns = B_TRUE; 569*ecee5a1fSHans Rosenfeld ns_npa.npa_nsid = npa->npa_nsid; 570*ecee5a1fSHans Rosenfeld cmd = *(npa->npa_cmd); 571*ecee5a1fSHans Rosenfeld cmd.c_func = do_list_nsid; 572*ecee5a1fSHans Rosenfeld ns_npa.npa_cmd = &cmd; 573*ecee5a1fSHans Rosenfeld 574*ecee5a1fSHans Rosenfeld nvme_walk(&ns_npa, npa->npa_node); 575*ecee5a1fSHans Rosenfeld 576*ecee5a1fSHans Rosenfeld free(name); 577*ecee5a1fSHans Rosenfeld 578*ecee5a1fSHans Rosenfeld return (exitcode); 579*ecee5a1fSHans Rosenfeld } 580*ecee5a1fSHans Rosenfeld 581*ecee5a1fSHans Rosenfeld static void 582*ecee5a1fSHans Rosenfeld usage_identify(const char *c_name) 583*ecee5a1fSHans Rosenfeld { 584*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n" 585*ecee5a1fSHans Rosenfeld " Print detailed information about the specified NVMe " 586*ecee5a1fSHans Rosenfeld "controllers and/or name-\n spaces.\n", c_name); 587*ecee5a1fSHans Rosenfeld } 588*ecee5a1fSHans Rosenfeld 589*ecee5a1fSHans Rosenfeld static int 590*ecee5a1fSHans Rosenfeld do_identify(int fd, const nvme_process_arg_t *npa) 591*ecee5a1fSHans Rosenfeld { 592*ecee5a1fSHans Rosenfeld if (npa->npa_nsid == 0) { 593*ecee5a1fSHans Rosenfeld nvme_capabilities_t *cap; 594*ecee5a1fSHans Rosenfeld 595*ecee5a1fSHans Rosenfeld cap = nvme_capabilities(fd); 596*ecee5a1fSHans Rosenfeld if (cap == NULL) 597*ecee5a1fSHans Rosenfeld return (-1); 598*ecee5a1fSHans Rosenfeld 599*ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 600*ecee5a1fSHans Rosenfeld nvme_print_identify_ctrl(npa->npa_idctl, cap, 601*ecee5a1fSHans Rosenfeld npa->npa_version); 602*ecee5a1fSHans Rosenfeld 603*ecee5a1fSHans Rosenfeld free(cap); 604*ecee5a1fSHans Rosenfeld } else { 605*ecee5a1fSHans Rosenfeld (void) printf("%s/%s: ", npa->npa_name, 606*ecee5a1fSHans Rosenfeld di_minor_name(npa->npa_minor)); 607*ecee5a1fSHans Rosenfeld nvme_print_identify_nsid(npa->npa_idns, 608*ecee5a1fSHans Rosenfeld npa->npa_version); 609*ecee5a1fSHans Rosenfeld } 610*ecee5a1fSHans Rosenfeld 611*ecee5a1fSHans Rosenfeld return (0); 612*ecee5a1fSHans Rosenfeld } 613*ecee5a1fSHans Rosenfeld 614*ecee5a1fSHans Rosenfeld static void 615*ecee5a1fSHans Rosenfeld usage_get_logpage(const char *c_name) 616*ecee5a1fSHans Rosenfeld { 617*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n" 618*ecee5a1fSHans Rosenfeld " Print the specified log page of the specified NVMe " 619*ecee5a1fSHans Rosenfeld "controllers and/or name-\n spaces. Supported log pages " 620*ecee5a1fSHans Rosenfeld "are error, health, and firmware.\n", c_name); 621*ecee5a1fSHans Rosenfeld } 622*ecee5a1fSHans Rosenfeld 623*ecee5a1fSHans Rosenfeld static int 624*ecee5a1fSHans Rosenfeld do_get_logpage_error(int fd, const nvme_process_arg_t *npa) 625*ecee5a1fSHans Rosenfeld { 626*ecee5a1fSHans Rosenfeld int nlog = npa->npa_idctl->id_elpe + 1; 627*ecee5a1fSHans Rosenfeld size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog; 628*ecee5a1fSHans Rosenfeld nvme_error_log_entry_t *elog; 629*ecee5a1fSHans Rosenfeld 630*ecee5a1fSHans Rosenfeld if (npa->npa_nsid != 0) 631*ecee5a1fSHans Rosenfeld errx(-1, "Error Log not available on a per-namespace basis"); 632*ecee5a1fSHans Rosenfeld 633*ecee5a1fSHans Rosenfeld elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize); 634*ecee5a1fSHans Rosenfeld 635*ecee5a1fSHans Rosenfeld if (elog == NULL) 636*ecee5a1fSHans Rosenfeld return (-1); 637*ecee5a1fSHans Rosenfeld 638*ecee5a1fSHans Rosenfeld nlog = bufsize / sizeof (nvme_error_log_entry_t); 639*ecee5a1fSHans Rosenfeld 640*ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 641*ecee5a1fSHans Rosenfeld nvme_print_error_log(nlog, elog); 642*ecee5a1fSHans Rosenfeld 643*ecee5a1fSHans Rosenfeld free(elog); 644*ecee5a1fSHans Rosenfeld 645*ecee5a1fSHans Rosenfeld return (0); 646*ecee5a1fSHans Rosenfeld } 647*ecee5a1fSHans Rosenfeld 648*ecee5a1fSHans Rosenfeld static int 649*ecee5a1fSHans Rosenfeld do_get_logpage_health(int fd, const nvme_process_arg_t *npa) 650*ecee5a1fSHans Rosenfeld { 651*ecee5a1fSHans Rosenfeld size_t bufsize = sizeof (nvme_health_log_t); 652*ecee5a1fSHans Rosenfeld nvme_health_log_t *hlog; 653*ecee5a1fSHans Rosenfeld 654*ecee5a1fSHans Rosenfeld if (npa->npa_nsid != 0) { 655*ecee5a1fSHans Rosenfeld if (npa->npa_idctl->id_lpa.lp_smart == 0) 656*ecee5a1fSHans Rosenfeld errx(-1, "SMART/Health information not available " 657*ecee5a1fSHans Rosenfeld "on a per-namespace basis on this controller"); 658*ecee5a1fSHans Rosenfeld } 659*ecee5a1fSHans Rosenfeld 660*ecee5a1fSHans Rosenfeld hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize); 661*ecee5a1fSHans Rosenfeld 662*ecee5a1fSHans Rosenfeld if (hlog == NULL) 663*ecee5a1fSHans Rosenfeld return (-1); 664*ecee5a1fSHans Rosenfeld 665*ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 666*ecee5a1fSHans Rosenfeld nvme_print_health_log(hlog, npa->npa_idctl); 667*ecee5a1fSHans Rosenfeld 668*ecee5a1fSHans Rosenfeld free(hlog); 669*ecee5a1fSHans Rosenfeld 670*ecee5a1fSHans Rosenfeld return (0); 671*ecee5a1fSHans Rosenfeld } 672*ecee5a1fSHans Rosenfeld 673*ecee5a1fSHans Rosenfeld static int 674*ecee5a1fSHans Rosenfeld do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa) 675*ecee5a1fSHans Rosenfeld { 676*ecee5a1fSHans Rosenfeld size_t bufsize = sizeof (nvme_fwslot_log_t); 677*ecee5a1fSHans Rosenfeld nvme_fwslot_log_t *fwlog; 678*ecee5a1fSHans Rosenfeld 679*ecee5a1fSHans Rosenfeld if (npa->npa_nsid != 0) 680*ecee5a1fSHans Rosenfeld errx(-1, "Firmware Slot information not available on a " 681*ecee5a1fSHans Rosenfeld "per-namespace basis"); 682*ecee5a1fSHans Rosenfeld 683*ecee5a1fSHans Rosenfeld fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize); 684*ecee5a1fSHans Rosenfeld 685*ecee5a1fSHans Rosenfeld if (fwlog == NULL) 686*ecee5a1fSHans Rosenfeld return (-1); 687*ecee5a1fSHans Rosenfeld 688*ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 689*ecee5a1fSHans Rosenfeld nvme_print_fwslot_log(fwlog); 690*ecee5a1fSHans Rosenfeld 691*ecee5a1fSHans Rosenfeld free(fwlog); 692*ecee5a1fSHans Rosenfeld 693*ecee5a1fSHans Rosenfeld return (0); 694*ecee5a1fSHans Rosenfeld } 695*ecee5a1fSHans Rosenfeld 696*ecee5a1fSHans Rosenfeld static int 697*ecee5a1fSHans Rosenfeld do_get_logpage(int fd, const nvme_process_arg_t *npa) 698*ecee5a1fSHans Rosenfeld { 699*ecee5a1fSHans Rosenfeld int ret = 0; 700*ecee5a1fSHans Rosenfeld int (*func)(int, const nvme_process_arg_t *); 701*ecee5a1fSHans Rosenfeld 702*ecee5a1fSHans Rosenfeld if (npa->npa_argc < 1) { 703*ecee5a1fSHans Rosenfeld warnx("missing logpage name"); 704*ecee5a1fSHans Rosenfeld usage(npa->npa_cmd); 705*ecee5a1fSHans Rosenfeld exit(-1); 706*ecee5a1fSHans Rosenfeld } 707*ecee5a1fSHans Rosenfeld 708*ecee5a1fSHans Rosenfeld if (strcmp(npa->npa_argv[0], "error") == 0) 709*ecee5a1fSHans Rosenfeld func = do_get_logpage_error; 710*ecee5a1fSHans Rosenfeld else if (strcmp(npa->npa_argv[0], "health") == 0) 711*ecee5a1fSHans Rosenfeld func = do_get_logpage_health; 712*ecee5a1fSHans Rosenfeld else if (strcmp(npa->npa_argv[0], "firmware") == 0) 713*ecee5a1fSHans Rosenfeld func = do_get_logpage_fwslot; 714*ecee5a1fSHans Rosenfeld else 715*ecee5a1fSHans Rosenfeld errx(-1, "invalid log page: %s", npa->npa_argv[0]); 716*ecee5a1fSHans Rosenfeld 717*ecee5a1fSHans Rosenfeld ret = func(fd, npa); 718*ecee5a1fSHans Rosenfeld return (ret); 719*ecee5a1fSHans Rosenfeld } 720*ecee5a1fSHans Rosenfeld 721*ecee5a1fSHans Rosenfeld static void 722*ecee5a1fSHans Rosenfeld usage_get_features(const char *c_name) 723*ecee5a1fSHans Rosenfeld { 724*ecee5a1fSHans Rosenfeld const nvme_feature_t *feat; 725*ecee5a1fSHans Rosenfeld 726*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n" 727*ecee5a1fSHans Rosenfeld " Print the specified features of the specified NVMe controllers " 728*ecee5a1fSHans Rosenfeld "and/or\n namespaces. Supported features are:\n\n", c_name); 729*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %-35s %-14s %s\n", 730*ecee5a1fSHans Rosenfeld "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE"); 731*ecee5a1fSHans Rosenfeld for (feat = &features[0]; feat->f_feature != 0; feat++) { 732*ecee5a1fSHans Rosenfeld char *type; 733*ecee5a1fSHans Rosenfeld 734*ecee5a1fSHans Rosenfeld if ((feat->f_getflags & NVMEADM_BOTH) == NVMEADM_BOTH) 735*ecee5a1fSHans Rosenfeld type = "both"; 736*ecee5a1fSHans Rosenfeld else if ((feat->f_getflags & NVMEADM_CTRL) != 0) 737*ecee5a1fSHans Rosenfeld type = "controller only"; 738*ecee5a1fSHans Rosenfeld else 739*ecee5a1fSHans Rosenfeld type = "namespace only"; 740*ecee5a1fSHans Rosenfeld 741*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %-35s %-14s %s\n", 742*ecee5a1fSHans Rosenfeld feat->f_name, feat->f_short, type); 743*ecee5a1fSHans Rosenfeld } 744*ecee5a1fSHans Rosenfeld 745*ecee5a1fSHans Rosenfeld } 746*ecee5a1fSHans Rosenfeld 747*ecee5a1fSHans Rosenfeld static int 748*ecee5a1fSHans Rosenfeld do_get_feat_common(int fd, const nvme_feature_t *feat, 749*ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *idctl) 750*ecee5a1fSHans Rosenfeld { 751*ecee5a1fSHans Rosenfeld void *buf = NULL; 752*ecee5a1fSHans Rosenfeld size_t bufsize = feat->f_bufsize; 753*ecee5a1fSHans Rosenfeld uint64_t res; 754*ecee5a1fSHans Rosenfeld 755*ecee5a1fSHans Rosenfeld if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf) 756*ecee5a1fSHans Rosenfeld == B_FALSE) 757*ecee5a1fSHans Rosenfeld return (EINVAL); 758*ecee5a1fSHans Rosenfeld 759*ecee5a1fSHans Rosenfeld nvme_print(2, feat->f_name, -1, NULL); 760*ecee5a1fSHans Rosenfeld feat->f_print(res, buf, bufsize, idctl); 761*ecee5a1fSHans Rosenfeld free(buf); 762*ecee5a1fSHans Rosenfeld 763*ecee5a1fSHans Rosenfeld return (0); 764*ecee5a1fSHans Rosenfeld } 765*ecee5a1fSHans Rosenfeld 766*ecee5a1fSHans Rosenfeld static int 767*ecee5a1fSHans Rosenfeld do_get_feat_intr_vect(int fd, const nvme_feature_t *feat, 768*ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *idctl) 769*ecee5a1fSHans Rosenfeld { 770*ecee5a1fSHans Rosenfeld uint64_t res; 771*ecee5a1fSHans Rosenfeld uint64_t arg; 772*ecee5a1fSHans Rosenfeld int intr_cnt; 773*ecee5a1fSHans Rosenfeld 774*ecee5a1fSHans Rosenfeld intr_cnt = nvme_intr_cnt(fd); 775*ecee5a1fSHans Rosenfeld 776*ecee5a1fSHans Rosenfeld if (intr_cnt == -1) 777*ecee5a1fSHans Rosenfeld return (EINVAL); 778*ecee5a1fSHans Rosenfeld 779*ecee5a1fSHans Rosenfeld nvme_print(2, feat->f_name, -1, NULL); 780*ecee5a1fSHans Rosenfeld 781*ecee5a1fSHans Rosenfeld for (arg = 0; arg < intr_cnt; arg++) { 782*ecee5a1fSHans Rosenfeld if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL) 783*ecee5a1fSHans Rosenfeld == B_FALSE) 784*ecee5a1fSHans Rosenfeld return (EINVAL); 785*ecee5a1fSHans Rosenfeld 786*ecee5a1fSHans Rosenfeld feat->f_print(res, NULL, 0, idctl); 787*ecee5a1fSHans Rosenfeld } 788*ecee5a1fSHans Rosenfeld 789*ecee5a1fSHans Rosenfeld return (0); 790*ecee5a1fSHans Rosenfeld } 791*ecee5a1fSHans Rosenfeld 792*ecee5a1fSHans Rosenfeld static int 793*ecee5a1fSHans Rosenfeld do_get_features(int fd, const nvme_process_arg_t *npa) 794*ecee5a1fSHans Rosenfeld { 795*ecee5a1fSHans Rosenfeld const nvme_feature_t *feat; 796*ecee5a1fSHans Rosenfeld char *f, *flist, *lasts; 797*ecee5a1fSHans Rosenfeld boolean_t header_printed = B_FALSE; 798*ecee5a1fSHans Rosenfeld 799*ecee5a1fSHans Rosenfeld if (npa->npa_argc > 1) 800*ecee5a1fSHans Rosenfeld errx(-1, "unexpected arguments"); 801*ecee5a1fSHans Rosenfeld 802*ecee5a1fSHans Rosenfeld /* 803*ecee5a1fSHans Rosenfeld * No feature list given, print all supported features. 804*ecee5a1fSHans Rosenfeld */ 805*ecee5a1fSHans Rosenfeld if (npa->npa_argc == 0) { 806*ecee5a1fSHans Rosenfeld (void) printf("%s: Get Features\n", npa->npa_name); 807*ecee5a1fSHans Rosenfeld for (feat = &features[0]; feat->f_feature != 0; feat++) { 808*ecee5a1fSHans Rosenfeld if ((npa->npa_nsid != 0 && 809*ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_NS) == 0) || 810*ecee5a1fSHans Rosenfeld (npa->npa_nsid == 0 && 811*ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_CTRL) == 0)) 812*ecee5a1fSHans Rosenfeld continue; 813*ecee5a1fSHans Rosenfeld 814*ecee5a1fSHans Rosenfeld (void) feat->f_get(fd, feat, npa->npa_idctl); 815*ecee5a1fSHans Rosenfeld } 816*ecee5a1fSHans Rosenfeld 817*ecee5a1fSHans Rosenfeld return (0); 818*ecee5a1fSHans Rosenfeld } 819*ecee5a1fSHans Rosenfeld 820*ecee5a1fSHans Rosenfeld /* 821*ecee5a1fSHans Rosenfeld * Process feature list. 822*ecee5a1fSHans Rosenfeld */ 823*ecee5a1fSHans Rosenfeld flist = strdup(npa->npa_argv[0]); 824*ecee5a1fSHans Rosenfeld if (flist == NULL) 825*ecee5a1fSHans Rosenfeld err(-1, "do_get_features"); 826*ecee5a1fSHans Rosenfeld 827*ecee5a1fSHans Rosenfeld for (f = strtok_r(flist, ",", &lasts); 828*ecee5a1fSHans Rosenfeld f != NULL; 829*ecee5a1fSHans Rosenfeld f = strtok_r(NULL, ",", &lasts)) { 830*ecee5a1fSHans Rosenfeld while (isspace(*f)) 831*ecee5a1fSHans Rosenfeld f++; 832*ecee5a1fSHans Rosenfeld 833*ecee5a1fSHans Rosenfeld for (feat = &features[0]; feat->f_feature != 0; feat++) { 834*ecee5a1fSHans Rosenfeld if (strncasecmp(feat->f_name, f, strlen(f)) == 0 || 835*ecee5a1fSHans Rosenfeld strncasecmp(feat->f_short, f, strlen(f)) == 0) 836*ecee5a1fSHans Rosenfeld break; 837*ecee5a1fSHans Rosenfeld } 838*ecee5a1fSHans Rosenfeld 839*ecee5a1fSHans Rosenfeld if (feat->f_feature == 0) { 840*ecee5a1fSHans Rosenfeld warnx("unknown feature %s", f); 841*ecee5a1fSHans Rosenfeld continue; 842*ecee5a1fSHans Rosenfeld } 843*ecee5a1fSHans Rosenfeld 844*ecee5a1fSHans Rosenfeld if ((npa->npa_nsid != 0 && 845*ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_NS) == 0) || 846*ecee5a1fSHans Rosenfeld (npa->npa_nsid == 0 && 847*ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_CTRL) == 0)) { 848*ecee5a1fSHans Rosenfeld warnx("feature %s %s supported for namespaces", 849*ecee5a1fSHans Rosenfeld feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ? 850*ecee5a1fSHans Rosenfeld "only" : "not"); 851*ecee5a1fSHans Rosenfeld continue; 852*ecee5a1fSHans Rosenfeld } 853*ecee5a1fSHans Rosenfeld 854*ecee5a1fSHans Rosenfeld if (!header_printed) { 855*ecee5a1fSHans Rosenfeld (void) printf("%s: Get Features\n", npa->npa_name); 856*ecee5a1fSHans Rosenfeld header_printed = B_TRUE; 857*ecee5a1fSHans Rosenfeld } 858*ecee5a1fSHans Rosenfeld 859*ecee5a1fSHans Rosenfeld if (feat->f_get(fd, feat, npa->npa_idctl) != 0) { 860*ecee5a1fSHans Rosenfeld warnx("unsupported feature: %s", feat->f_name); 861*ecee5a1fSHans Rosenfeld continue; 862*ecee5a1fSHans Rosenfeld } 863*ecee5a1fSHans Rosenfeld } 864*ecee5a1fSHans Rosenfeld 865*ecee5a1fSHans Rosenfeld free(flist); 866*ecee5a1fSHans Rosenfeld return (0); 867*ecee5a1fSHans Rosenfeld } 868*ecee5a1fSHans Rosenfeld 869*ecee5a1fSHans Rosenfeld static int 870*ecee5a1fSHans Rosenfeld do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf, 871*ecee5a1fSHans Rosenfeld unsigned long ses) 872*ecee5a1fSHans Rosenfeld { 873*ecee5a1fSHans Rosenfeld nvme_process_arg_t ns_npa = { 0 }; 874*ecee5a1fSHans Rosenfeld nvmeadm_cmd_t cmd = { 0 }; 875*ecee5a1fSHans Rosenfeld 876*ecee5a1fSHans Rosenfeld cmd = *(npa->npa_cmd); 877*ecee5a1fSHans Rosenfeld cmd.c_func = do_attach_detach; 878*ecee5a1fSHans Rosenfeld cmd.c_name = "detach"; 879*ecee5a1fSHans Rosenfeld ns_npa = *npa; 880*ecee5a1fSHans Rosenfeld ns_npa.npa_cmd = &cmd; 881*ecee5a1fSHans Rosenfeld 882*ecee5a1fSHans Rosenfeld if (do_attach_detach(fd, &ns_npa) != 0) 883*ecee5a1fSHans Rosenfeld return (exitcode); 884*ecee5a1fSHans Rosenfeld if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) { 885*ecee5a1fSHans Rosenfeld warn("%s failed", npa->npa_cmd->c_name); 886*ecee5a1fSHans Rosenfeld exitcode += -1; 887*ecee5a1fSHans Rosenfeld } 888*ecee5a1fSHans Rosenfeld cmd.c_name = "attach"; 889*ecee5a1fSHans Rosenfeld exitcode += do_attach_detach(fd, &ns_npa); 890*ecee5a1fSHans Rosenfeld 891*ecee5a1fSHans Rosenfeld return (exitcode); 892*ecee5a1fSHans Rosenfeld } 893*ecee5a1fSHans Rosenfeld 894*ecee5a1fSHans Rosenfeld static void 895*ecee5a1fSHans Rosenfeld usage_format(const char *c_name) 896*ecee5a1fSHans Rosenfeld { 897*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n" 898*ecee5a1fSHans Rosenfeld " Format one or all namespaces of the specified NVMe " 899*ecee5a1fSHans Rosenfeld "controller. Supported LBA\n formats can be queried with " 900*ecee5a1fSHans Rosenfeld "the \"%s identify\" command on the namespace\n to be " 901*ecee5a1fSHans Rosenfeld "formatted.\n", c_name, getprogname()); 902*ecee5a1fSHans Rosenfeld } 903*ecee5a1fSHans Rosenfeld 904*ecee5a1fSHans Rosenfeld static int 905*ecee5a1fSHans Rosenfeld do_format(int fd, const nvme_process_arg_t *npa) 906*ecee5a1fSHans Rosenfeld { 907*ecee5a1fSHans Rosenfeld unsigned long lbaf; 908*ecee5a1fSHans Rosenfeld 909*ecee5a1fSHans Rosenfeld if (npa->npa_idctl->id_oacs.oa_format == 0) 910*ecee5a1fSHans Rosenfeld errx(-1, "%s not supported", npa->npa_cmd->c_name); 911*ecee5a1fSHans Rosenfeld 912*ecee5a1fSHans Rosenfeld if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0) 913*ecee5a1fSHans Rosenfeld errx(-1, "%s not supported on individual namespace", 914*ecee5a1fSHans Rosenfeld npa->npa_cmd->c_name); 915*ecee5a1fSHans Rosenfeld 916*ecee5a1fSHans Rosenfeld 917*ecee5a1fSHans Rosenfeld if (npa->npa_argc > 0) { 918*ecee5a1fSHans Rosenfeld errno = 0; 919*ecee5a1fSHans Rosenfeld lbaf = strtoul(npa->npa_argv[0], NULL, 10); 920*ecee5a1fSHans Rosenfeld 921*ecee5a1fSHans Rosenfeld if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF) 922*ecee5a1fSHans Rosenfeld errx(-1, "invalid LBA format %d", lbaf + 1); 923*ecee5a1fSHans Rosenfeld 924*ecee5a1fSHans Rosenfeld if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0) 925*ecee5a1fSHans Rosenfeld errx(-1, "LBA formats with metadata not supported"); 926*ecee5a1fSHans Rosenfeld } else { 927*ecee5a1fSHans Rosenfeld lbaf = npa->npa_idns->id_flbas.lba_format; 928*ecee5a1fSHans Rosenfeld } 929*ecee5a1fSHans Rosenfeld 930*ecee5a1fSHans Rosenfeld return (do_format_common(fd, npa, lbaf, 0)); 931*ecee5a1fSHans Rosenfeld } 932*ecee5a1fSHans Rosenfeld 933*ecee5a1fSHans Rosenfeld static void 934*ecee5a1fSHans Rosenfeld usage_secure_erase(const char *c_name) 935*ecee5a1fSHans Rosenfeld { 936*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>] [-c]\n\n" 937*ecee5a1fSHans Rosenfeld " Secure-Erase one or all namespaces of the specified " 938*ecee5a1fSHans Rosenfeld "NVMe controller.\n", c_name); 939*ecee5a1fSHans Rosenfeld } 940*ecee5a1fSHans Rosenfeld 941*ecee5a1fSHans Rosenfeld static int 942*ecee5a1fSHans Rosenfeld do_secure_erase(int fd, const nvme_process_arg_t *npa) 943*ecee5a1fSHans Rosenfeld { 944*ecee5a1fSHans Rosenfeld unsigned long lbaf; 945*ecee5a1fSHans Rosenfeld uint8_t ses = NVME_FRMT_SES_USER; 946*ecee5a1fSHans Rosenfeld 947*ecee5a1fSHans Rosenfeld if (npa->npa_idctl->id_oacs.oa_format == 0) 948*ecee5a1fSHans Rosenfeld errx(-1, "%s not supported", npa->npa_cmd->c_name); 949*ecee5a1fSHans Rosenfeld 950*ecee5a1fSHans Rosenfeld if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0) 951*ecee5a1fSHans Rosenfeld errx(-1, "%s not supported on individual namespace", 952*ecee5a1fSHans Rosenfeld npa->npa_cmd->c_name); 953*ecee5a1fSHans Rosenfeld 954*ecee5a1fSHans Rosenfeld if (npa->npa_argc > 0) { 955*ecee5a1fSHans Rosenfeld if (strcmp(npa->npa_argv[0], "-c") == 0) 956*ecee5a1fSHans Rosenfeld ses = NVME_FRMT_SES_CRYPTO; 957*ecee5a1fSHans Rosenfeld else 958*ecee5a1fSHans Rosenfeld usage(npa->npa_cmd); 959*ecee5a1fSHans Rosenfeld } 960*ecee5a1fSHans Rosenfeld 961*ecee5a1fSHans Rosenfeld if (ses == NVME_FRMT_SES_CRYPTO && 962*ecee5a1fSHans Rosenfeld npa->npa_idctl->id_fna.fn_crypt_erase == 0) 963*ecee5a1fSHans Rosenfeld errx(-1, "cryptographic %s not supported", 964*ecee5a1fSHans Rosenfeld npa->npa_cmd->c_name); 965*ecee5a1fSHans Rosenfeld 966*ecee5a1fSHans Rosenfeld lbaf = npa->npa_idns->id_flbas.lba_format; 967*ecee5a1fSHans Rosenfeld 968*ecee5a1fSHans Rosenfeld return (do_format_common(fd, npa, lbaf, ses)); 969*ecee5a1fSHans Rosenfeld } 970*ecee5a1fSHans Rosenfeld 971*ecee5a1fSHans Rosenfeld static void 972*ecee5a1fSHans Rosenfeld usage_attach_detach(const char *c_name) 973*ecee5a1fSHans Rosenfeld { 974*ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n" 975*ecee5a1fSHans Rosenfeld " %c%s blkdev(7d) %s one or all namespaces of the " 976*ecee5a1fSHans Rosenfeld "specified NVMe controller.\n", 977*ecee5a1fSHans Rosenfeld c_name, toupper(c_name[0]), &c_name[1], 978*ecee5a1fSHans Rosenfeld c_name[0] == 'd' ? "from" : "to"); 979*ecee5a1fSHans Rosenfeld } 980*ecee5a1fSHans Rosenfeld 981*ecee5a1fSHans Rosenfeld static int 982*ecee5a1fSHans Rosenfeld do_attach_detach(int fd, const nvme_process_arg_t *npa) 983*ecee5a1fSHans Rosenfeld { 984*ecee5a1fSHans Rosenfeld char *c_name = npa->npa_cmd->c_name; 985*ecee5a1fSHans Rosenfeld 986*ecee5a1fSHans Rosenfeld if (!npa->npa_isns) { 987*ecee5a1fSHans Rosenfeld nvme_process_arg_t ns_npa = { 0 }; 988*ecee5a1fSHans Rosenfeld 989*ecee5a1fSHans Rosenfeld ns_npa.npa_name = npa->npa_name; 990*ecee5a1fSHans Rosenfeld ns_npa.npa_isns = B_TRUE; 991*ecee5a1fSHans Rosenfeld ns_npa.npa_cmd = npa->npa_cmd; 992*ecee5a1fSHans Rosenfeld 993*ecee5a1fSHans Rosenfeld nvme_walk(&ns_npa, npa->npa_node); 994*ecee5a1fSHans Rosenfeld 995*ecee5a1fSHans Rosenfeld return (exitcode); 996*ecee5a1fSHans Rosenfeld } else { 997*ecee5a1fSHans Rosenfeld if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd) 998*ecee5a1fSHans Rosenfeld == B_FALSE) { 999*ecee5a1fSHans Rosenfeld warn("%s failed", c_name); 1000*ecee5a1fSHans Rosenfeld return (-1); 1001*ecee5a1fSHans Rosenfeld } 1002*ecee5a1fSHans Rosenfeld } 1003*ecee5a1fSHans Rosenfeld 1004*ecee5a1fSHans Rosenfeld return (0); 1005*ecee5a1fSHans Rosenfeld } 1006