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.
This commit is contained in:
parent
4d7486c30f
commit
3b3dd3f770
@ -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,
|
||||
|
@ -121,7 +121,7 @@ nvme_print_controller(struct nvme_controller_data *cdata)
|
||||
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);
|
||||
|
@ -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,8 +321,43 @@ 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 */
|
||||
|
||||
@ -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);
|
||||
@ -530,6 +767,115 @@ nsdetach(const struct cmd *f, int argc, char *argv[])
|
||||
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[])
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -126,6 +126,7 @@ read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata)
|
||||
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;
|
||||
|
@ -73,6 +73,7 @@ void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid);
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user