Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 31 Jul 2019 18:44:20 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r350477 - head/sbin/nvmecontrol
Message-ID:  <201907311844.x6VIiKuF034112@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <err.h>
 #include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201907311844.x6VIiKuF034112>