Index: head/sbin/nvmecontrol/firmware.c =================================================================== --- head/sbin/nvmecontrol/firmware.c (revision 360122) +++ head/sbin/nvmecontrol/firmware.c (revision 360123) @@ -1,363 +1,363 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* Tables for command line parsing */ static cmd_fn_t firmware; #define NONE 0xffffffffu static struct options { bool activate; uint32_t slot; const char *fw_img; const char *dev; } opt = { .activate = false, .slot = NONE, .fw_img = NULL, .dev = NULL, }; static const struct opts firmware_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("activate", 'a', arg_none, opt, activate, "Attempt to activate firmware"), OPT("slot", 's', arg_uint32, opt, slot, "Slot to activate and/or download firmware to"), OPT("firmware", 'f', arg_path, opt, fw_img, "Firmware image to download"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args firmware_args[] = { - { arg_string, &opt.dev, "controller-id" }, + { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd firmware_cmd = { .name = "firmware", .fn = firmware, .descr = "Download firmware image to controller", .ctx_size = sizeof(opt), .opts = firmware_opts, .args = firmware_args, }; CMD_COMMAND(firmware_cmd); /* End of tables for command line parsing */ static int slot_has_valid_firmware(int fd, int slot) { struct nvme_firmware_page fw; int has_fw = false; read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &fw, sizeof(fw)); if (fw.revision[slot-1] != 0LLU) has_fw = true; return (has_fw); } static void read_image_file(const char *path, void **buf, int32_t *size) { struct stat sb; int32_t filesize; int fd; *size = 0; *buf = NULL; if ((fd = open(path, O_RDONLY)) < 0) err(1, "unable to open '%s'", path); if (fstat(fd, &sb) < 0) err(1, "unable to stat '%s'", path); /* * The NVMe spec does not explicitly state a maximum firmware image * size, although one can be inferred from the dword size limitation * for the size and offset fields in the Firmware Image Download * command. * * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the * size and offsets are specified in terms of dwords (not bytes), but * realistically INT32_MAX is sufficient here and simplifies matters * a bit. */ if (sb.st_size > INT32_MAX) errx(1, "size of file '%s' is too large (%jd bytes)", path, (intmax_t)sb.st_size); filesize = (int32_t)sb.st_size; if ((*buf = malloc(filesize)) == NULL) errx(1, "unable to malloc %d bytes", filesize); if ((*size = read(fd, *buf, filesize)) < 0) err(1, "error reading '%s'", path); /* XXX assuming no short reads */ if (*size != filesize) errx(1, "error reading '%s' (read %d bytes, requested %d bytes)", path, *size, filesize); close(fd); } static void update_firmware(int fd, uint8_t *payload, int32_t payload_size) { struct nvme_pt_command pt; int32_t off, resid, size; void *chunk; off = 0; resid = payload_size; if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL) errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); while (resid > 0) { size = (resid >= NVME_MAX_XFER_SIZE) ? NVME_MAX_XFER_SIZE : resid; memcpy(chunk, payload + off, size); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD; pt.cmd.cdw10 = htole32((size / sizeof(uint32_t)) - 1); pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); pt.buf = chunk; pt.len = size; pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "firmware download request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "firmware download request returned error"); resid -= size; off += size; } free(chunk); } static int activate_firmware(int fd, int slot, int activate_action) { struct nvme_pt_command pt; uint16_t sct, sc; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE; pt.cmd.cdw10 = htole32((activate_action << 3) | slot); pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "firmware activate request failed"); sct = NVME_STATUS_GET_SCT(pt.cpl.status); sc = NVME_STATUS_GET_SC(pt.cpl.status); if (sct == NVME_SCT_COMMAND_SPECIFIC && sc == NVME_SC_FIRMWARE_REQUIRES_RESET) return 1; if (nvme_completion_is_error(&pt.cpl)) errx(1, "firmware activate request returned error"); return 0; } static void firmware(const struct cmd *f, int argc, char *argv[]) { int fd = -1; int activate_action, reboot_required; char prompt[64]; void *buf = NULL; + char *path; int32_t size = 0, nsid; uint16_t oacs_fw; uint8_t fw_slot1_ro, fw_num_slots; struct nvme_controller_data cdata; if (arg_parse(argc, argv, f)) return; if (opt.slot == 0) { fprintf(stderr, "0 is not a valid slot number. " "Slot numbers start at 1.\n"); arg_help(argc, argv, f); } else if (opt.slot > 7 && opt.slot != NONE) { fprintf(stderr, "Slot number %s specified which is " "greater than max allowed slot number of " "7.\n", optarg); arg_help(argc, argv, f); } if (!opt.activate && opt.fw_img == NULL) { fprintf(stderr, "Neither a replace ([-f path_to_firmware]) nor " "activate ([-a]) firmware image action\n" "was specified.\n"); arg_help(argc, argv, f); } if (opt.activate && opt.fw_img == NULL && opt.slot == 0) { fprintf(stderr, "Slot number to activate not specified.\n"); arg_help(argc, argv, f); } open_dev(opt.dev, &fd, 1, 1); - - /* Check that a controller (and not a namespace) was specified. */ - get_nsid(fd, NULL, &nsid); + get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); - arg_help(argc, argv, f); + open_dev(path, &fd, 1, 1); } + free(path); read_controller_data(fd, &cdata); oacs_fw = (cdata.oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; if (oacs_fw == 0) errx(1, "controller does not support firmware activate/download"); fw_slot1_ro = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) & NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK; if (opt.fw_img && opt.slot == 1 && fw_slot1_ro) errx(1, "slot %d is marked as read only", opt.slot); fw_num_slots = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; if (opt.slot > fw_num_slots) errx(1, "slot %d specified but controller only supports %d slots", opt.slot, fw_num_slots); if (opt.activate && opt.fw_img == NULL && !slot_has_valid_firmware(fd, opt.slot)) errx(1, "slot %d does not contain valid firmware,\n" "try 'nvmecontrol logpage -p 3 %s' to get a list " "of available images\n", opt.slot, opt.dev); if (opt.fw_img) read_image_file(opt.fw_img, &buf, &size); if (opt.fw_img != NULL&& opt.activate) printf("You are about to download and activate " "firmware image (%s) to controller %s.\n" "This may damage your controller and/or " "overwrite an existing firmware image.\n", opt.fw_img, opt.dev); else if (opt.activate) printf("You are about to activate a new firmware " "image on controller %s.\n" "This may damage your controller.\n", opt.dev); else if (opt.fw_img != NULL) printf("You are about to download firmware image " "(%s) to controller %s.\n" "This may damage your controller and/or " "overwrite an existing firmware image.\n", opt.fw_img, opt.dev); printf("Are you sure you want to continue? (yes/no) "); while (1) { fgets(prompt, sizeof(prompt), stdin); if (strncasecmp(prompt, "yes", 3) == 0) break; if (strncasecmp(prompt, "no", 2) == 0) exit(1); printf("Please answer \"yes\" or \"no\". "); } if (opt.fw_img != NULL) { update_firmware(fd, buf, size); if (opt.activate) activate_action = NVME_AA_REPLACE_ACTIVATE; else activate_action = NVME_AA_REPLACE_NO_ACTIVATE; } else { activate_action = NVME_AA_ACTIVATE; } reboot_required = activate_firmware(fd, opt.slot, activate_action); if (opt.activate) { if (reboot_required) { printf("New firmware image activated but requires " "conventional reset (i.e. reboot) to " "complete activation.\n"); } else { printf("New firmware image activated and will take " "effect after next controller reset.\n" "Controller reset can be initiated via " "'nvmecontrol reset %s'\n", opt.dev); } } close(fd); exit(0); } Index: head/sbin/nvmecontrol/ns.c =================================================================== --- head/sbin/nvmecontrol/ns.c (revision 360122) +++ head/sbin/nvmecontrol/ns.c (revision 360123) @@ -1,884 +1,968 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Netflix, Inc. * Copyright (C) 2018-2019 Alexander Motin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* Tables for command line parsing */ static cmd_fn_t ns; static cmd_fn_t nsactive; static cmd_fn_t nsallocated; static cmd_fn_t nscontrollers; static cmd_fn_t nscreate; static cmd_fn_t nsdelete; static cmd_fn_t nsattach; static cmd_fn_t nsdetach; static cmd_fn_t nsattached; static cmd_fn_t nsidentify; #define NONE 0xffffffffu #define NONE64 0xffffffffffffffffull #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } #define OPT_END { NULL, 0, arg_none, NULL, NULL } static struct cmd ns_cmd = { .name = "ns", .fn = ns, .descr = "Namespace management commands", .ctx_size = 0, .opts = NULL, .args = NULL, }; CMD_COMMAND(ns_cmd); static struct active_options { const char *dev; } active_opt = { .dev = NULL, }; static const struct args active_args[] = { - { arg_string, &active_opt.dev, "controller-id" }, + { arg_string, &active_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd active_cmd = { .name = "active", .fn = nsactive, .descr = "List active (attached) namespaces", .ctx_size = sizeof(active_opt), .opts = NULL, .args = active_args, }; CMD_SUBCOMMAND(ns_cmd, active_cmd); static struct cmd allocated_cmd = { .name = "allocated", .fn = nsallocated, .descr = "List allocated (created) namespaces", .ctx_size = sizeof(active_opt), .opts = NULL, .args = active_args, }; CMD_SUBCOMMAND(ns_cmd, allocated_cmd); static struct controllers_options { const char *dev; } controllers_opt = { .dev = NULL, }; static const struct args controllers_args[] = { - { arg_string, &controllers_opt.dev, "controller-id" }, + { arg_string, &controllers_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd controllers_cmd = { .name = "controllers", .fn = nscontrollers, .descr = "List all controllers in NVM subsystem", .ctx_size = sizeof(controllers_opt), .opts = NULL, .args = controllers_args, }; CMD_SUBCOMMAND(ns_cmd, controllers_cmd); static struct create_options { uint64_t nsze; uint64_t cap; uint32_t lbaf; uint32_t mset; uint32_t nmic; uint32_t pi; uint32_t pil; uint32_t flbas; uint32_t dps; // uint32_t block_size; const char *dev; } create_opt = { .nsze = NONE64, .cap = NONE64, .lbaf = NONE, .mset = NONE, .nmic = NONE, .pi = NONE, .pil = NONE, .flbas = NONE, .dps = NONE, .dev = NULL, // .block_size = NONE, }; static const struct opts create_opts[] = { OPT("nsze", 's', arg_uint64, create_opt, nsze, "The namespace size"), OPT("ncap", 'c', arg_uint64, create_opt, cap, "The capacity of the namespace (<= ns size)"), OPT("lbaf", 'f', arg_uint32, create_opt, lbaf, "The FMT field of the FLBAS"), OPT("mset", 'm', arg_uint32, create_opt, mset, "The MSET field of the FLBAS"), OPT("nmic", 'n', arg_uint32, create_opt, nmic, "Namespace multipath and sharing capabilities"), OPT("pi", 'p', arg_uint32, create_opt, pi, "PI field of FLBAS"), OPT("pil", 'l', arg_uint32, create_opt, pil, "PIL field of FLBAS"), OPT("flbas", 'L', arg_uint32, create_opt, flbas, "Namespace formatted logical block size setting"), OPT("dps", 'd', arg_uint32, create_opt, dps, "Data protection settings"), // OPT("block-size", 'b', arg_uint32, create_opt, block_size, // "Blocksize of the namespace"), OPT_END }; static const struct args create_args[] = { - { arg_string, &create_opt.dev, "controller-id" }, + { arg_string, &create_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd create_cmd = { .name = "create", .fn = nscreate, .descr = "Create a namespace", .ctx_size = sizeof(create_opt), .opts = create_opts, .args = create_args, }; CMD_SUBCOMMAND(ns_cmd, create_cmd); static struct delete_options { uint32_t nsid; const char *dev; } delete_opt = { .nsid = NONE, .dev = NULL, }; static const struct opts delete_opts[] = { OPT("namespace-id", 'n', arg_uint32, delete_opt, nsid, "The namespace ID to delete"), OPT_END }; static const struct args delete_args[] = { - { arg_string, &delete_opt.dev, "controller-id" }, + { arg_string, &delete_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd delete_cmd = { .name = "delete", .fn = nsdelete, .descr = "Delete a namespace", .ctx_size = sizeof(delete_opt), .opts = delete_opts, .args = delete_args, }; CMD_SUBCOMMAND(ns_cmd, delete_cmd); static struct attach_options { uint32_t nsid; uint32_t ctrlrid; const char *dev; } attach_opt = { .nsid = NONE, .ctrlrid = NONE - 1, .dev = NULL, }; static const struct opts attach_opts[] = { OPT("namespace-id", 'n', arg_uint32, attach_opt, nsid, "The namespace ID to attach"), OPT("controller", 'c', arg_uint32, attach_opt, ctrlrid, "The controller ID to attach"), OPT_END }; static const struct args attach_args[] = { - { arg_string, &attach_opt.dev, "controller-id" }, + { arg_string, &attach_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd attach_cmd = { .name = "attach", .fn = nsattach, .descr = "Attach a controller to a namespace", .ctx_size = sizeof(attach_opt), .opts = attach_opts, .args = attach_args, }; CMD_SUBCOMMAND(ns_cmd, attach_cmd); static struct attached_options { uint32_t nsid; const char *dev; } attached_opt = { .nsid = NONE, .dev = NULL, }; static const struct opts attached_opts[] = { OPT("namespace-id", 'n', arg_uint32, attached_opt, nsid, "The namespace ID to request attached controllers"), OPT_END }; static const struct args attached_args[] = { - { arg_string, &attached_opt.dev, "controller-id" }, + { arg_string, &attached_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd attached_cmd = { .name = "attached", .fn = nsattached, .descr = "List controllers attached to a namespace", .ctx_size = sizeof(attached_opt), .opts = attached_opts, .args = attached_args, }; CMD_SUBCOMMAND(ns_cmd, attached_cmd); static struct detach_options { uint32_t nsid; uint32_t ctrlrid; const char *dev; } detach_opt = { .nsid = NONE, .ctrlrid = NONE - 1, .dev = NULL, }; static const struct opts detach_opts[] = { OPT("namespace-id", 'n', arg_uint32, detach_opt, nsid, "The namespace ID to detach"), OPT("controller", 'c', arg_uint32, detach_opt, ctrlrid, "The controller ID to detach"), OPT_END }; static const struct args detach_args[] = { - { arg_string, &detach_opt.dev, "controller-id" }, + { arg_string, &detach_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd detach_cmd = { .name = "detach", .fn = nsdetach, .descr = "Detach a controller from a namespace", .ctx_size = sizeof(detach_opt), .opts = detach_opts, .args = detach_args, }; CMD_SUBCOMMAND(ns_cmd, detach_cmd); static struct identify_options { bool hex; bool verbose; const char *dev; uint32_t nsid; } identify_opt = { .hex = false, .verbose = false, .dev = NULL, .nsid = NONE, }; static const struct opts identify_opts[] = { OPT("hex", 'x', arg_none, identify_opt, hex, "Print identiy information in hex"), OPT("verbose", 'v', arg_none, identify_opt, verbose, "More verbosity: print entire identify table"), OPT("nsid", 'n', arg_uint32, identify_opt, nsid, "The namespace ID to print IDENTIFY for"), { NULL, 0, arg_none, NULL, NULL } }; static const struct args identify_args[] = { - { arg_string, &identify_opt.dev, "controller-id" }, + { arg_string, &identify_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd identify_cmd = { .name = "identify", .fn = nsidentify, .descr = "Print IDENTIFY for allocated namespace", .ctx_size = sizeof(identify_opt), .opts = identify_opts, .args = identify_args, }; CMD_SUBCOMMAND(ns_cmd, identify_cmd); /* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */ struct ns_result_str { uint16_t res; const char * str; }; static struct ns_result_str ns_result[] = { { 0x2, "Invalid Field"}, { 0xa, "Invalid Format"}, { 0xb, "Invalid Namespace or format"}, { 0x15, "Namespace insufficent capacity"}, { 0x16, "Namespace ID unavaliable"}, { 0x18, "Namespace already attached"}, { 0x19, "Namespace is private"}, { 0x1a, "Namespace is not attached"}, { 0x1b, "Thin provisioning not supported"}, { 0x1c, "Controller list invalid"}, { 0x24, "ANA Group Identifier Invalid"}, { 0x25, "ANA Attach Failed"}, { 0xFFFF, "Unknown"} }; static const char * get_res_str(uint16_t res) { struct ns_result_str *t = ns_result; while (t->res != 0xFFFF) { if (t->res == res) return (t->str); t++; } return t->str; } static void nsactive(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; + struct nvme_controller_data cd; int fd, i; + char *path; + uint32_t nsid; uint32_t list[1024]; if (arg_parse(argc, argv, f)) return; open_dev(active_opt.dev, &fd, 0, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 0, 1); + } + free(path); + read_controller_data(fd, &cd); + /* Check that controller can execute this command. */ + if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & + NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + errx(1, "controller does not support namespace management"); + memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(0); pt.cmd.cdw10 = htole32(0x02); pt.buf = list; pt.len = sizeof(list); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); printf("Active namespaces:\n"); for (i = 0; list[i] != 0; i++) printf("%10d\n", le32toh(list[i])); exit(0); } static void nsallocated(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, i; + char *path; + uint32_t nsid; uint32_t list[1024]; if (arg_parse(argc, argv, f)) return; open_dev(active_opt.dev, &fd, 0, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 0, 1); + } + free(path); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(0); pt.cmd.cdw10 = htole32(0x10); pt.buf = list; pt.len = sizeof(list); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); printf("Allocated namespaces:\n"); for (i = 0; list[i] != 0; i++) printf("%10d\n", le32toh(list[i])); exit(0); } static void nscontrollers(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, i, n; + char *path; + uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; open_dev(controllers_opt.dev, &fd, 0, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 0, 1); + } + free(path); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(0x13); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); n = le16toh(clist[0]); printf("NVM subsystem includes %d controller(s):\n", n); for (i = 0; i < n; i++) printf(" 0x%04x\n", le16toh(clist[i + 1])); exit(0); } /* * NS MGMT Command specific status values: * 0xa = Invalid Format * 0x15 = Namespace Insuffience capacity * 0x16 = Namespace ID unavailable (number namespaces exceeded) * 0xb = Thin Provisioning Not supported */ static void nscreate(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; struct nvme_namespace_data nsdata; int fd, result; + char *path; + uint32_t nsid; if (arg_parse(argc, argv, f)) return; if (create_opt.cap == NONE64) create_opt.cap = create_opt.nsze; if (create_opt.nsze == NONE64) { fprintf(stderr, "Size not specified\n"); arg_help(argc, argv, f); } open_dev(create_opt.dev, &fd, 1, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } + free(path); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); /* Allow namespaces sharing if Multi-Path I/O is supported. */ if (create_opt.nmic == NONE) { create_opt.nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK << NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0; } memset(&nsdata, 0, sizeof(nsdata)); nsdata.nsze = create_opt.nsze; nsdata.ncap = create_opt.cap; if (create_opt.flbas == NONE) nsdata.flbas = ((create_opt.lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK) << NVME_NS_DATA_FLBAS_FORMAT_SHIFT) | ((create_opt.mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK) << NVME_NS_DATA_FLBAS_EXTENDED_SHIFT); else nsdata.flbas = create_opt.flbas; if (create_opt.dps == NONE) nsdata.dps = ((create_opt.pi & NVME_NS_DATA_DPS_MD_START_MASK) << NVME_NS_DATA_DPS_MD_START_SHIFT) | ((create_opt.pil & NVME_NS_DATA_DPS_PIT_MASK) << NVME_NS_DATA_DPS_PIT_SHIFT); else nsdata.dps = create_opt.dps; nsdata.nmic = create_opt.nmic; nvme_namespace_data_swapbytes(&nsdata); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; pt.cmd.cdw10 = htole32(0); /* create */ pt.buf = &nsdata; pt.len = sizeof(struct nvme_namespace_data); pt.is_read = 0; /* passthrough writes data to ctrlr */ if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", argv[optind], result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace creation failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } printf("namespace %d created\n", pt.cpl.cdw0); exit(0); } static void nsdelete(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, result; + char *path; + uint32_t nsid; char buf[2]; if (arg_parse(argc, argv, f)) return; - if (delete_opt.nsid == NONE) { - fprintf(stderr, - "No NSID specified"); - arg_help(argc, argv, f); - } open_dev(delete_opt.dev, &fd, 1, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } else if (delete_opt.nsid == NONE) { + close(fd); + fprintf(stderr, "No NSID specified"); + arg_help(argc, argv, f); + } + if (delete_opt.nsid != NONE) + nsid = delete_opt.nsid; + free(path); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; pt.cmd.cdw10 = htole32(1); /* delete */ pt.buf = buf; pt.len = sizeof(buf); pt.is_read = 1; - pt.cmd.nsid = delete_opt.nsid; + pt.cmd.nsid = nsid; if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", delete_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace deletion failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } - printf("namespace %d deleted\n", delete_opt.nsid); + printf("namespace %d deleted\n", nsid); exit(0); } /* * Attach and Detach use Dword 10, and a controller list (section 4.9) * This struct is 4096 bytes in size. * 0h = attach * 1h = detach * * Result values for both attach/detach: * * Completion 18h = Already attached * 19h = NS is private and already attached to a controller * 1Ah = Not attached, request could not be completed * 1Ch = Controller list invalid. * * 0x2 Invalid Field can occur if ctrlrid d.n.e in system. */ static void nsattach(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, result; + char *path; + uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; - if (attach_opt.nsid == NONE) { - fprintf(stderr, "No valid NSID specified\n"); + open_dev(attach_opt.dev, &fd, 1, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } else if (attach_opt.nsid == NONE) { + close(fd); + fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } - open_dev(attach_opt.dev, &fd, 1, 1); + if (attach_opt.nsid != NONE) + nsid = attach_opt.nsid; read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); if (attach_opt.ctrlrid == NONE) { /* Get full list of controllers to attach to. */ memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(0x13); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } else { /* By default attach to this controller. */ if (attach_opt.ctrlrid == NONE - 1) attach_opt.ctrlrid = cd.ctrlr_id; memset(&clist, 0, sizeof(clist)); clist[0] = htole16(1); clist[1] = htole16(attach_opt.ctrlrid); } memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; pt.cmd.cdw10 = htole32(0); /* attach */ - pt.cmd.nsid = attach_opt.nsid; + pt.cmd.nsid = nsid; pt.buf = &clist; pt.len = sizeof(clist); if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", attach_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace attach failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } - printf("namespace %d attached\n", attach_opt.nsid); + printf("namespace %d attached\n", nsid); exit(0); } static void nsdetach(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, result; + char *path; + uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; - if (detach_opt.nsid == NONE) { - fprintf(stderr, "No valid NSID specified\n"); + open_dev(detach_opt.dev, &fd, 1, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } else if (detach_opt.nsid == NONE) { + close(fd); + fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } - open_dev(detach_opt.dev, &fd, 1, 1); + if (detach_opt.nsid != NONE) + nsid = detach_opt.nsid; read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); if (detach_opt.ctrlrid == NONE) { /* Get list of controllers this namespace attached to. */ memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; - pt.cmd.nsid = htole32(detach_opt.nsid); + pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(0x12); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); if (clist[0] == 0) { detach_opt.ctrlrid = cd.ctrlr_id; memset(&clist, 0, sizeof(clist)); clist[0] = htole16(1); clist[1] = htole16(detach_opt.ctrlrid); } } else { /* By default detach from this controller. */ if (detach_opt.ctrlrid == NONE - 1) detach_opt.ctrlrid = cd.ctrlr_id; memset(&clist, 0, sizeof(clist)); clist[0] = htole16(1); clist[1] = htole16(detach_opt.ctrlrid); } memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; pt.cmd.cdw10 = htole32(1); /* detach */ - pt.cmd.nsid = detach_opt.nsid; + pt.cmd.nsid = nsid; pt.buf = &clist; pt.len = sizeof(clist); if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", argv[optind], result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace detach failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } - printf("namespace %d detached\n", detach_opt.nsid); + printf("namespace %d detached\n", nsid); exit(0); } static void nsattached(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, i, n; + char *path; + uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; - if (attached_opt.nsid == NONE) { - fprintf(stderr, "No valid NSID specified\n"); + open_dev(attached_opt.dev, &fd, 0, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } else if (attached_opt.nsid == NONE) { + close(fd); + fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } - open_dev(attached_opt.dev, &fd, 0, 1); + if (attached_opt.nsid != NONE) + nsid = attached_opt.nsid; read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; - pt.cmd.nsid = htole32(attached_opt.nsid); + pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(0x12); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); n = le16toh(clist[0]); printf("Attached %d controller(s):\n", n); for (i = 0; i < n; i++) printf(" 0x%04x\n", le16toh(clist[i + 1])); exit(0); } static void nsidentify(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; struct nvme_namespace_data nsdata; uint8_t *data; int fd; + char *path; + uint32_t nsid; u_int i; if (arg_parse(argc, argv, f)) return; - if (identify_opt.nsid == NONE) { - fprintf(stderr, "No valid NSID specified\n"); + open_dev(identify_opt.dev, &fd, 0, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } else if (identify_opt.nsid == NONE) { + close(fd); + fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } - open_dev(identify_opt.dev, &fd, 0, 1); + if (identify_opt.nsid != NONE) + nsid = identify_opt.nsid; read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; - pt.cmd.nsid = htole32(identify_opt.nsid); + pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(0x11); pt.buf = &nsdata; pt.len = sizeof(nsdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); close(fd); data = (uint8_t *)&nsdata; for (i = 0; i < sizeof(nsdata); i++) { if (data[i] != 0) break; } if (i == sizeof(nsdata)) - errx(1, "namespace %d is not allocated", identify_opt.nsid); + errx(1, "namespace %d is not allocated", nsid); /* Convert data to host endian */ nvme_namespace_data_swapbytes(&nsdata); if (identify_opt.hex) { i = sizeof(struct nvme_namespace_data); if (!identify_opt.verbose) { for (; i > 384; i--) { if (data[i - 1] != 0) break; } } print_hex(&nsdata, i); exit(0); } print_namespace(&nsdata); exit(0); } static void ns(const struct cmd *nf __unused, int argc, char *argv[]) { cmd_dispatch(argc, argv, &ns_cmd); } Index: head/sbin/nvmecontrol/power.c =================================================================== --- head/sbin/nvmecontrol/power.c (revision 360122) +++ head/sbin/nvmecontrol/power.c (revision 360123) @@ -1,201 +1,209 @@ /*- * Copyright (c) 2016 Netflix, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" _Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY, "nvme_power_state size wrong"); #define POWER_NONE 0xffffffffu static struct options { bool list; uint32_t power; uint32_t workload; const char *dev; } opt = { .list = false, .power = POWER_NONE, .workload = 0, .dev = NULL, }; static void power_list_one(int i, struct nvme_power_state *nps) { int mpower, apower, ipower; uint8_t mps, nops, aps, apw; mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) & NVME_PWR_ST_MPS_MASK; nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) & NVME_PWR_ST_NOPS_MASK; apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) & NVME_PWR_ST_APW_MASK; aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) & NVME_PWR_ST_APS_MASK; mpower = nps->mp; if (mps == 0) mpower *= 100; ipower = nps->idlp; if (nps->ips == 1) ipower *= 100; apower = nps->actp; if (aps == 1) apower *= 100; printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n", i, mpower / 10000, mpower % 10000, nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000, nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl, nps->rwt, nps->rwl, ipower / 10000, ipower % 10000, apower / 10000, apower % 10000, apw); } static void power_list(struct nvme_controller_data *cdata) { int i; printf("\nPower States Supported: %d\n\n", cdata->npss + 1); printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workloadd\n"); printf("-- -------- --------- --------- -- -- -- -- -------- -------- --\n"); for (i = 0; i <= cdata->npss; i++) power_list_one(i, &cdata->power_state[i]); } static void power_set(int fd, int power_val, int workload, int perm) { struct nvme_pt_command pt; uint32_t p; p = perm ? (1u << 31) : 0; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_SET_FEATURES; pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT | p); pt.cmd.cdw11 = htole32(power_val | (workload << 5)); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "set feature power mgmt request returned error"); } static void power_show(int fd) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_GET_FEATURES; pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "set feature power mgmt request returned error"); printf("Current Power Mode is %d\n", pt.cpl.cdw0); } static void power(const struct cmd *f, int argc, char *argv[]) { struct nvme_controller_data cdata; int fd; + char *path; + uint32_t nsid; if (arg_parse(argc, argv, f)) return; if (opt.list && opt.power != POWER_NONE) { fprintf(stderr, "Can't set power and list power states\n"); arg_help(argc, argv, f); } open_dev(opt.dev, &fd, 1, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } + free(path); if (opt.list) { read_controller_data(fd, &cdata); power_list(&cdata); goto out; } if (opt.power != POWER_NONE) { power_set(fd, opt.power, opt.workload, 0); goto out; } power_show(fd); out: close(fd); exit(0); } static const struct opts power_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("list", 'l', arg_none, opt, list, "List the valid power states"), OPT("power", 'p', arg_uint32, opt, power, "Set the power state"), OPT("workload", 'w', arg_uint32, opt, workload, "Set the workload"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args power_args[] = { - { arg_string, &opt.dev, "controller-id" }, + { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd power_cmd = { .name = "power", .fn = power, .descr = "Manage power states for the drive", .ctx_size = sizeof(opt), .opts = power_opts, .args = power_args, }; CMD_COMMAND(power_cmd); Index: head/sbin/nvmecontrol/reset.c =================================================================== --- head/sbin/nvmecontrol/reset.c (revision 360122) +++ head/sbin/nvmecontrol/reset.c (revision 360123) @@ -1,77 +1,85 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "nvmecontrol.h" static struct options { const char *dev; } opt = { .dev = NULL }; static const struct args args[] = { - { arg_string, &opt.dev, "controller-id" }, + { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static void reset(const struct cmd *f, int argc, char *argv[]) { int fd; + char *path; + uint32_t nsid; if (arg_parse(argc, argv, f)) return; open_dev(opt.dev, &fd, 1, 1); + get_nsid(fd, &path, &nsid); + if (nsid != 0) { + close(fd); + open_dev(path, &fd, 1, 1); + } + free(path); if (ioctl(fd, NVME_RESET_CONTROLLER) < 0) err(1, "reset request to %s failed", argv[optind]); exit(0); } static struct cmd reset_cmd = { .name = "reset", .fn = reset, .descr = "Perform a controller-level reset", .args = args, }; CMD_COMMAND(reset_cmd);