summaryrefslogtreecommitdiffstats
path: root/freebsd/sbin/nvmecontrol/ns.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sbin/nvmecontrol/ns.c')
-rw-r--r--freebsd/sbin/nvmecontrol/ns.c886
1 files changed, 886 insertions, 0 deletions
diff --git a/freebsd/sbin/nvmecontrol/ns.c b/freebsd/sbin/nvmecontrol/ns.c
new file mode 100644
index 00000000..9705706b
--- /dev/null
+++ b/freebsd/sbin/nvmecontrol/ns.c
@@ -0,0 +1,886 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017 Netflix, Inc.
+ * Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioccom.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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_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;
+ 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_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_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_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_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_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_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;
+ 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
+ * 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;
+
+ 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);
+ 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 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);
+ 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;
+
+ 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);
+ 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;
+ uint16_t clist[2048];
+
+ if (arg_parse(argc, argv, f))
+ return;
+ if (attach_opt.nsid == NONE) {
+ fprintf(stderr, "No valid NSID specified\n");
+ arg_help(argc, argv, f);
+ }
+ open_dev(attach_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");
+
+ 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.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);
+ 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;
+ uint16_t clist[2048];
+
+ if (arg_parse(argc, argv, f))
+ return;
+ if (detach_opt.nsid == NONE) {
+ fprintf(stderr, "No valid NSID specified\n");
+ arg_help(argc, argv, f);
+ }
+ open_dev(detach_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");
+
+ 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.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.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);
+ 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);
+}
+
+static void
+ns(const struct cmd *nf __unused, int argc, char *argv[])
+{
+
+ cmd_dispatch(argc, argv, &ns_cmd);
+}