397bc7f075
While very useful by itself, it also makes `nvmecontrol` not depend on hardcoded device names parsing, that in its turn makes simple to take nvdX (and potentially any other) device names as arguments. Also added IOCTL bypass from nvdX to respective nvmeYnsZ makes them interchangeable for management purposes. MFC after: 2 weeks Sponsored by: iXsystems, Inc.
885 lines
23 KiB
C
885 lines
23 KiB
C
/*-
|
|
* 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);
|
|
}
|