From owner-svn-src-all@freebsd.org Wed Jul 31 18:44:22 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 47F0DA5C37; Wed, 31 Jul 2019 18:44:22 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 45zMlp1phmz4BTT; Wed, 31 Jul 2019 18:44:22 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 1B5815F74; Wed, 31 Jul 2019 18:44:22 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x6VIiMqR034118; Wed, 31 Jul 2019 18:44:22 GMT (envelope-from mav@FreeBSD.org) Received: (from mav@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x6VIiKuF034112; Wed, 31 Jul 2019 18:44:20 GMT (envelope-from mav@FreeBSD.org) Message-Id: <201907311844.x6VIiKuF034112@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mav set sender to mav@FreeBSD.org using -f From: Alexander Motin Date: Wed, 31 Jul 2019 18:44:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r350477 - head/sbin/nvmecontrol X-SVN-Group: head X-SVN-Commit-Author: mav X-SVN-Commit-Paths: head/sbin/nvmecontrol X-SVN-Commit-Revision: 350477 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 45zMlp1phmz4BTT X-Spamd-Bar: / Authentication-Results: mx1.freebsd.org; none X-Spamd-Result: default: False [0.33 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_SPAM_SHORT(0.33)[0.331,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 31 Jul 2019 18:44:22 -0000 Author: mav Date: Wed Jul 31 18:44:20 2019 New Revision: 350477 URL: https://svnweb.freebsd.org/changeset/base/350477 Log: Feature-complete NVMe Namespace Management. This adds several previously missed but important subcommands to list namespaces and controllers. It also fixes few previously added but just found with real testing to be broken subcommands. Also while there, add possibility to explicitly specify nsid for `nvmecontrol identify` subcommand. It may be useful to specify nsids not having own devices, for example 0xffffffff, or just newly created ones. MFC after: 2 weeks Relnotes: yes Sponsored by: iXsystems, Inc. Modified: head/sbin/nvmecontrol/identify.c head/sbin/nvmecontrol/identify_ext.c head/sbin/nvmecontrol/ns.c head/sbin/nvmecontrol/nvmecontrol.8 head/sbin/nvmecontrol/nvmecontrol.c head/sbin/nvmecontrol/nvmecontrol.h Modified: head/sbin/nvmecontrol/identify.c ============================================================================== --- head/sbin/nvmecontrol/identify.c Wed Jul 31 18:40:43 2019 (r350476) +++ head/sbin/nvmecontrol/identify.c Wed Jul 31 18:44:20 2019 (r350477) @@ -48,13 +48,15 @@ static struct options { bool hex; bool verbose; const char *dev; + uint32_t nsid; } opt = { .hex = false, .verbose = false, .dev = NULL, + .nsid = 0, }; -static void +void print_namespace(struct nvme_namespace_data *nsdata) { uint32_t i; @@ -204,22 +206,24 @@ identify_ns(const struct cmd *f, int argc, char *argv[ int fd, hexlength; uint32_t nsid; - /* - * Check if the specified device node exists before continuing. - * This is a cleaner check for cases where the correct controller - * is specified, but an invalid namespace on that controller. - */ open_dev(opt.dev, &fd, 1, 1); - close(fd); - - /* - * We send IDENTIFY commands to the controller, not the namespace, - * since it is an admin cmd. The namespace ID will be specified in - * the IDENTIFY command itself. So parse the namespace's device node - * string to get the controller substring and namespace ID. - */ - parse_ns_str(opt.dev, path, &nsid); - open_dev(path, &fd, 1, 1); + if (strstr(opt.dev, NVME_NS_PREFIX) != NULL) { + /* + * Now we know that provided device name is valid, that is + * good for error reporting if specified controller name is + * valid, but namespace ID is not. But we send IDENTIFY + * commands to the controller, not the namespace, since it + * is an admin cmd. The namespace ID will be specified in + * the IDENTIFY command itself. So parse the namespace's + * device node string to get the controller device substring + * and namespace ID. + */ + close(fd); + parse_ns_str(opt.dev, path, &nsid); + open_dev(path, &fd, 1, 1); + } else { + nsid = opt.nsid; + } read_namespace_data(fd, nsid, &nsdata); close(fd); @@ -248,10 +252,10 @@ identify(const struct cmd *f, int argc, char *argv[]) arg_parse(argc, argv, f); /* - * If device node contains "ns", we consider it a namespace, - * otherwise, consider it a controller. + * If device node contains "ns" or nsid is specified, we consider + * it a namespace request, otherwise, consider it a controller. */ - if (strstr(opt.dev, NVME_NS_PREFIX) == NULL) + if (strstr(opt.dev, NVME_NS_PREFIX) == NULL && opt.nsid == 0) identify_ctrlr(f, argc, argv); else identify_ns(f, argc, argv); @@ -263,6 +267,8 @@ static const struct opts identify_opts[] = { "Print identiy information in hex"), OPT("verbose", 'v', arg_none, opt, verbose, "More verbosity: print entire identify table"), + OPT("nsid", 'n', arg_uint32, opt, nsid, + "Namespace ID to use if not in device name"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT @@ -275,7 +281,7 @@ static const struct args identify_args[] = { static struct cmd identify_cmd = { .name = "identify", .fn = identify, - .descr = "Print a human-readable summary of the IDENTIFY information", + .descr = "Print summary of the IDENTIFY information", .ctx_size = sizeof(opt), .opts = identify_opts, .args = identify_args, Modified: head/sbin/nvmecontrol/identify_ext.c ============================================================================== --- head/sbin/nvmecontrol/identify_ext.c Wed Jul 31 18:40:43 2019 (r350476) +++ head/sbin/nvmecontrol/identify_ext.c Wed Jul 31 18:44:20 2019 (r350477) @@ -121,7 +121,7 @@ nvme_print_controller(struct nvme_controller_data *cda printf("Unlimited\n"); else printf("%ld\n", PAGE_SIZE * (1L << cdata->mdts)); - printf("Controller ID: 0x%02x\n", cdata->ctrlr_id); + printf("Controller ID: 0x%04x\n", cdata->ctrlr_id); printf("Version: %d.%d.%d\n", (cdata->ver >> 16) & 0xffff, (cdata->ver >> 8) & 0xff, cdata->ver & 0xff); Modified: head/sbin/nvmecontrol/ns.c ============================================================================== --- head/sbin/nvmecontrol/ns.c Wed Jul 31 18:40:43 2019 (r350476) +++ head/sbin/nvmecontrol/ns.c Wed Jul 31 18:44:20 2019 (r350477) @@ -34,6 +34,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include #include #include #include @@ -44,10 +46,15 @@ __FBSDID("$FreeBSD$"); /* 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 @@ -55,11 +62,71 @@ static cmd_fn_t nsdetach; #define OPT_END { NULL, 0, arg_none, NULL, NULL } static struct cmd ns_cmd = { - .name = "ns", .fn = ns, .descr = "Namespace commands", .ctx_size = 0, .opts = NULL, .args = NULL, + .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_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_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; @@ -101,7 +168,7 @@ static const struct opts create_opts[] = { "PI field of FLBAS"), OPT("pil", 'l', arg_uint32, create_opt, pil, "PIL field of FLBAS"), - OPT("flbas", 'l', arg_uint32, create_opt, 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"), @@ -118,7 +185,7 @@ static const struct args create_args[] = { static struct cmd create_cmd = { .name = "create", .fn = nscreate, - .descr = "Create a new namespace", + .descr = "Create a namespace", .ctx_size = sizeof(create_opt), .opts = create_opts, .args = create_args, @@ -148,7 +215,7 @@ static const struct args delete_args[] = { static struct cmd delete_cmd = { .name = "delete", .fn = nsdelete, - .descr = "Delete a new namespace", + .descr = "Delete a namespace", .ctx_size = sizeof(delete_opt), .opts = delete_opts, .args = delete_args, @@ -169,7 +236,7 @@ static struct attach_options { 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, nsid, + OPT("controller", 'c', arg_uint32, attach_opt, ctrlrid, "The controller ID to attach"), OPT_END }; @@ -182,7 +249,7 @@ static const struct args attach_args[] = { static struct cmd attach_cmd = { .name = "attach", .fn = nsattach, - .descr = "Attach a new namespace", + .descr = "Attach a controller to a namespace", .ctx_size = sizeof(attach_opt), .opts = attach_opts, .args = attach_args, @@ -190,6 +257,36 @@ static struct cmd attach_cmd = { 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_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; @@ -203,7 +300,7 @@ static struct detach_options { 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, nsid, + OPT("controller", 'c', arg_uint32, detach_opt, ctrlrid, "The controller ID to detach"), OPT_END }; @@ -216,7 +313,7 @@ static const struct args detach_args[] = { static struct cmd detach_cmd = { .name = "detach", .fn = nsdetach, - .descr = "Detach a new namespace", + .descr = "Detach a controller from a namespace", .ctx_size = sizeof(detach_opt), .opts = detach_opts, .args = detach_args, @@ -224,9 +321,44 @@ static struct cmd detach_cmd = { CMD_SUBCOMMAND(ns_cmd, detach_cmd); -#define NS_USAGE \ - "ns (create|delete|attach|detach)\n" +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_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 { @@ -245,6 +377,8 @@ static struct ns_result_str ns_result[] = { { 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"} }; @@ -261,6 +395,110 @@ get_res_str(uint16_t res) return t->str; } +static void +nsactive(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + int fd, i; + uint32_t list[1024]; + + if (arg_parse(argc, argv, f)) + return; + open_dev(active_opt.dev, &fd, 1, 1); + + 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; + uint32_t list[1024]; + + if (arg_parse(argc, argv, f)) + return; + open_dev(active_opt.dev, &fd, 1, 1); + 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; + uint16_t clist[2048]; + + if (arg_parse(argc, argv, f)) + return; + open_dev(controllers_opt.dev, &fd, 1, 1); + 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 @@ -323,8 +561,7 @@ nscreate(const struct cmd *f, int argc, char *argv[]) memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; - - pt.cmd.cdw10 = 0; /* create */ + 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 */ @@ -366,7 +603,7 @@ nsdelete(const struct cmd *f, int argc, char *argv[]) memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; - pt.cmd.cdw10 = 1; /* delete */ + pt.cmd.cdw10 = htole32(1); /* delete */ pt.buf = buf; pt.len = sizeof(buf); pt.is_read = 1; @@ -444,7 +681,7 @@ nsattach(const struct cmd *f, int argc, char *argv[]) memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; - pt.cmd.cdw10 = 0; /* attach */ + pt.cmd.cdw10 = htole32(0); /* attach */ pt.cmd.nsid = attach_opt.nsid; pt.buf = &clist; pt.len = sizeof(clist); @@ -471,11 +708,11 @@ nsdetach(const struct cmd *f, int argc, char *argv[]) if (arg_parse(argc, argv, f)) return; - if (attach_opt.nsid == NONE) { + if (detach_opt.nsid == NONE) { fprintf(stderr, "No valid NSID specified\n"); arg_help(argc, argv, f); } - open_dev(attach_opt.dev, &fd, 1, 1); + open_dev(detach_opt.dev, &fd, 1, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ @@ -513,7 +750,7 @@ nsdetach(const struct cmd *f, int argc, char *argv[]) memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; - pt.cmd.cdw10 = 1; /* detach */ + pt.cmd.cdw10 = htole32(1); /* detach */ pt.cmd.nsid = detach_opt.nsid; pt.buf = &clist; pt.len = sizeof(clist); @@ -527,6 +764,115 @@ nsdetach(const struct cmd *f, int argc, char *argv[]) NVME_STATUS_SC_MASK)); } printf("namespace %d detached\n", detach_opt.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; + uint16_t clist[2048]; + + if (arg_parse(argc, argv, f)) + return; + if (attached_opt.nsid == NONE) { + fprintf(stderr, "No valid NSID specified\n"); + arg_help(argc, argv, f); + } + open_dev(attached_opt.dev, &fd, 1, 1); + 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.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; + u_int i; + + if (arg_parse(argc, argv, f)) + return; + if (identify_opt.nsid == NONE) { + fprintf(stderr, "No valid NSID specified\n"); + arg_help(argc, argv, f); + } + open_dev(identify_opt.dev, &fd, 1, 1); + 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.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); + + /* 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); } Modified: head/sbin/nvmecontrol/nvmecontrol.8 ============================================================================== --- head/sbin/nvmecontrol/nvmecontrol.8 Wed Jul 31 18:40:43 2019 (r350476) +++ head/sbin/nvmecontrol/nvmecontrol.8 Wed Jul 31 18:44:20 2019 (r350477) @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 7, 2018 +.Dd July 31, 2019 .Dt NVMECONTROL 8 .Os .Sh NAME @@ -69,6 +69,51 @@ .Aq device id .Aq namespace id .Nm +.Ic ns active +.Aq device id +.Nm +.Ic ns allocated +.Aq device id +.Nm +.Ic ns attach +.Aq Fl n Ar nsid +.Aq Fl c Ar cntid +.Aq device id +.Nm +.Ic ns attached +.Aq Fl n Ar nsid +.Aq device id +.Nm +.Ic ns controllers +.Aq device id +.Nm +.Ic ns create +.Aq Fl s Ar nsze +.Op Fl c Ar ncap +.Op Fl f Ar lbaf +.Op Fl m Ar mset +.Op Fl n Ar nmic +.Op Fl p Ar pi +.Op Fl l Ar pil +.Op Fl L Ar flbas +.Op Fl d Ar dps +.Aq device id +.Nm +.Ic ns delete +.Aq Fl n Ar nsid +.Aq device id +.Nm +.Ic ns detach +.Aq Fl n Ar nsid +.Aq Fl c Ar cntid +.Aq device id +.Nm +.Ic ns identify +.Op Fl v +.Op Fl x +.Aq Fl n Ar nsid +.Aq device id +.Nm .Ic firmware .Op Fl s Ar slot .Op Fl f Ar path_to_firmware @@ -143,6 +188,10 @@ will list all valid vendors and pages. will print the page as hex. .Fl b will print the binary data for the page. +.Ss ns +Various namespace management commands. +If namespace management is supported by device, allow list, create and delete +namespaces, list, attach and detach controllers to namespaces. .Ss format Format either specified namespace, or all namespaces of specified controller, using specified parameters: Modified: head/sbin/nvmecontrol/nvmecontrol.c ============================================================================== --- head/sbin/nvmecontrol/nvmecontrol.c Wed Jul 31 18:40:43 2019 (r350476) +++ head/sbin/nvmecontrol/nvmecontrol.c Wed Jul 31 18:44:20 2019 (r350477) @@ -126,6 +126,7 @@ read_namespace_data(int fd, uint32_t nsid, struct nvme memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); + pt.cmd.cdw10 = htole32(0); pt.buf = nsdata; pt.len = sizeof(*nsdata); pt.is_read = 1; Modified: head/sbin/nvmecontrol/nvmecontrol.h ============================================================================== --- head/sbin/nvmecontrol/nvmecontrol.h Wed Jul 31 18:40:43 2019 (r350476) +++ head/sbin/nvmecontrol/nvmecontrol.h Wed Jul 31 18:44:20 2019 (r350477) @@ -73,6 +73,7 @@ void parse_ns_str(const char *ns_str, char *ctrlr_str, void read_controller_data(int fd, struct nvme_controller_data *cdata); void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata); void print_hex(void *data, uint32_t length); +void print_namespace(struct nvme_namespace_data *nsdata); void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size); void print_temp(uint16_t t);