Create generic command / arg parsing routines
Create a set of routines and structures to hold the data for the args for a command. Use them to generate help and to parse args. Convert all the current commands over to the new format. "comnd" is a hat-tip to the TOPS-20 %COMND JSYS that (very) loosely inspired much of the subsequent command line notions in the industry, but this is far simpler (the %COMND man page is longer than this code) and not in the kernel... Also, it implements today's de-facto command [verb]+ [opts]* [args]* format rather than the old, archaic TOPS-20 command format :) This is a snapshot of a work in progress to get the nvme passthru stuff committed. In time it will become a private library and used by some other programs in the tree that conform to the above pattern. Differential Revision: https://reviews.freebsd.org/D19296
This commit is contained in:
parent
dd8bacfd4e
commit
f634b4c1be
@ -2,10 +2,13 @@
|
||||
|
||||
PACKAGE=runtime
|
||||
PROG= nvmecontrol
|
||||
SRCS= nvmecontrol.c devlist.c firmware.c format.c identify.c identify_ext.c logpage.c \
|
||||
perftest.c reset.c ns.c nvme_util.c power.c nc_util.c
|
||||
SRCS= comnd.c nvmecontrol.c
|
||||
SRCS+= devlist.c firmware.c format.c identify.c logpage.c ns.c perftest.c power.c reset.c
|
||||
#SRCS+= passthru.c
|
||||
SRCS+= identify_ext.c nvme_util.c nc_util.c
|
||||
MAN= nvmecontrol.8
|
||||
LDFLAGS+= -rdynamic
|
||||
LIBADD+= util
|
||||
SUBDIR= modules
|
||||
|
||||
.PATH: ${SRCTOP}/sys/dev/nvme
|
||||
|
326
sbin/nvmecontrol/comnd.c
Normal file
326
sbin/nvmecontrol/comnd.c
Normal file
@ -0,0 +1,326 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (C) 2019 Netflix, Inc
|
||||
*
|
||||
* 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.
|
||||
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <libutil.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "comnd.h"
|
||||
|
||||
static struct cmd top;
|
||||
|
||||
static void
|
||||
print_usage(const struct cmd *f)
|
||||
{
|
||||
|
||||
fprintf(stderr, " %s %-15s - %s\n", getprogname(), f->name, f->descr);
|
||||
}
|
||||
|
||||
static void
|
||||
gen_usage(const struct cmd *t)
|
||||
{
|
||||
struct cmd *walker;
|
||||
|
||||
fprintf(stderr, "usage:\n");
|
||||
SLIST_FOREACH(walker, &t->subcmd, link) {
|
||||
print_usage(walker);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
cmd_dispatch(int argc, char *argv[], const struct cmd *t)
|
||||
{
|
||||
struct cmd *walker;
|
||||
|
||||
if (t == NULL)
|
||||
t = ⊤
|
||||
|
||||
if (argv[1] == NULL) {
|
||||
gen_usage(t);
|
||||
return (1);
|
||||
}
|
||||
SLIST_FOREACH(walker, &t->subcmd, link) {
|
||||
if (strcmp(argv[1], walker->name) == 0) {
|
||||
walker->fn(walker, argc-1, &argv[1]);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Unknown command: %s\n", argv[1]);
|
||||
gen_usage(t);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
arg_suffix(char *buf, size_t len, arg_type at)
|
||||
{
|
||||
switch (at) {
|
||||
case arg_none:
|
||||
break;
|
||||
case arg_string:
|
||||
strlcat(buf, "=<STRING>", len);
|
||||
break;
|
||||
case arg_path:
|
||||
strlcat(buf, "=<FILE>", len);
|
||||
break;
|
||||
default:
|
||||
strlcat(buf, "=<NUM>", len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
arg_help(int argc __unused, char * const *argv, const struct cmd *f)
|
||||
{
|
||||
int i;
|
||||
char buf[31];
|
||||
const struct opts *opts = f->opts;
|
||||
const struct args *args = f->args;
|
||||
|
||||
// XXX walk up the cmd list...
|
||||
if (argv[optind])
|
||||
fprintf(stderr, "Unknown argument: %s\n", argv[optind]);
|
||||
fprintf(stderr, "Usage:\n %s %s", getprogname(), argv[0]);
|
||||
if (opts)
|
||||
fprintf(stderr, " <args>");
|
||||
if (args) {
|
||||
while (args->descr != NULL) {
|
||||
fprintf(stderr, " %s", args->descr);
|
||||
args++;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n\n%s\n", f->descr);
|
||||
if (opts != NULL) {
|
||||
fprintf(stderr, "Options:\n");
|
||||
for (i = 0; opts[i].long_arg != NULL; i++) {
|
||||
*buf = '\0';
|
||||
if (isprint(opts[i].short_arg)) {
|
||||
snprintf(buf, sizeof(buf), " -%c, ", opts[i].short_arg);
|
||||
} else {
|
||||
strlcpy(buf, " ", sizeof(buf));
|
||||
}
|
||||
strlcat(buf, "--", sizeof(buf));
|
||||
strlcat(buf, opts[i].long_arg, sizeof(buf));
|
||||
arg_suffix(buf, sizeof(buf), opts[i].at);
|
||||
fprintf(stderr, "%-30.30s - %s\n", buf, opts[i].descr);
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
find_long(struct option *lopts, int ch)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; lopts[i].val != ch && lopts[i].name != NULL; i++)
|
||||
continue;
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
arg_parse(int argc, char * const * argv, const struct cmd *f)
|
||||
{
|
||||
int i, n, idx, ch;
|
||||
uint64_t v;
|
||||
struct option *lopts;
|
||||
char *shortopts, *p;
|
||||
const struct opts *opts = f->opts;
|
||||
const struct args *args = f->args;
|
||||
|
||||
if (opts == NULL)
|
||||
n = 0;
|
||||
else
|
||||
for (n = 0; opts[n].long_arg != NULL;)
|
||||
n++;
|
||||
lopts = malloc((n + 2) * sizeof(struct option));
|
||||
if (lopts == NULL)
|
||||
err(1, "option memory");
|
||||
p = shortopts = malloc((n + 3) * sizeof(char));
|
||||
if (shortopts == NULL)
|
||||
err(1, "shortopts memory");
|
||||
idx = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
lopts[i].name = opts[i].long_arg;
|
||||
lopts[i].has_arg = opts[i].at == arg_none ? no_argument : required_argument;
|
||||
lopts[i].flag = NULL;
|
||||
lopts[i].val = opts[i].short_arg;
|
||||
if (isprint(opts[i].short_arg)) {
|
||||
*p++ = opts[i].short_arg;
|
||||
if (lopts[i].has_arg)
|
||||
*p++ = ':';
|
||||
}
|
||||
}
|
||||
lopts[n].name = "help";
|
||||
lopts[n].has_arg = no_argument;
|
||||
lopts[n].flag = NULL;
|
||||
lopts[n].val = '?';
|
||||
*p++ = '?';
|
||||
*p++ = '\0';
|
||||
memset(lopts + n + 1, 0, sizeof(struct option));
|
||||
while ((ch = getopt_long(argc, argv, shortopts, lopts, &idx)) != -1) {
|
||||
/*
|
||||
* If ch != 0, we've found a short option, and we have to
|
||||
* look it up lopts table. Otherwise idx is valid.
|
||||
*/
|
||||
if (ch != 0)
|
||||
idx = find_long(lopts, ch);
|
||||
if (idx == n)
|
||||
arg_help(argc, argv, f);
|
||||
switch (opts[idx].at) {
|
||||
case arg_none:
|
||||
*(bool *)opts[idx].ptr = true;
|
||||
break;
|
||||
case arg_string:
|
||||
case arg_path:
|
||||
*(const char **)opts[idx].ptr = optarg;
|
||||
break;
|
||||
case arg_uint8:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xff)
|
||||
goto bad_arg;
|
||||
*(uint8_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_uint16:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xffff)
|
||||
goto bad_arg;
|
||||
*(uint16_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_uint32:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xffffffffu)
|
||||
goto bad_arg;
|
||||
*(uint32_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_uint64:
|
||||
v = strtoul(optarg, NULL, 0);
|
||||
if (v > 0xffffffffffffffffull)
|
||||
goto bad_arg;
|
||||
*(uint64_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
case arg_size:
|
||||
if (expand_number(optarg, &v) < 0)
|
||||
goto bad_arg;
|
||||
*(uint64_t *)opts[idx].ptr = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (args) {
|
||||
while (args->descr) {
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "Missing arg %s\n", args->descr);
|
||||
arg_help(argc, argv, f);
|
||||
return (1);
|
||||
}
|
||||
*(char **)args->ptr = argv[optind++];
|
||||
args++;
|
||||
}
|
||||
}
|
||||
free(lopts);
|
||||
return (0);
|
||||
bad_arg:
|
||||
fprintf(stderr, "Bad value to --%s: %s\n", opts[idx].long_arg, optarg);
|
||||
free(lopts);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads all the .so's from the specified directory.
|
||||
*/
|
||||
void
|
||||
cmd_load_dir(const char *dir __unused, cmd_load_cb_t cb __unused, void *argp __unused)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *dent;
|
||||
char *path = NULL;
|
||||
void *h;
|
||||
|
||||
d = opendir(dir);
|
||||
if (d == NULL)
|
||||
return;
|
||||
for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
|
||||
if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0)
|
||||
continue;
|
||||
asprintf(&path, "%s/%s", dir, dent->d_name);
|
||||
if (path == NULL)
|
||||
err(1, "Can't malloc for path, giving up.");
|
||||
if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL)
|
||||
warnx("Can't load %s: %s", path, dlerror());
|
||||
else {
|
||||
if (cb != NULL)
|
||||
cb(argp, h);
|
||||
}
|
||||
free(path);
|
||||
path = NULL;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
void
|
||||
cmd_register(struct cmd *up, struct cmd *cmd)
|
||||
{
|
||||
struct cmd *walker, *last;
|
||||
|
||||
if (up == NULL)
|
||||
up = ⊤
|
||||
SLIST_INIT(&cmd->subcmd);
|
||||
cmd->parent = up;
|
||||
last = NULL;
|
||||
SLIST_FOREACH(walker, &up->subcmd, link) {
|
||||
if (strcmp(walker->name, cmd->name) > 0)
|
||||
break;
|
||||
last = walker;
|
||||
}
|
||||
if (last == NULL) {
|
||||
SLIST_INSERT_HEAD(&up->subcmd, cmd, link);
|
||||
} else {
|
||||
SLIST_INSERT_AFTER(last, cmd, link);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cmd_init(void)
|
||||
{
|
||||
|
||||
}
|
102
sbin/nvmecontrol/comnd.h
Normal file
102
sbin/nvmecontrol/comnd.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Netflix, Inc
|
||||
*
|
||||
* 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.
|
||||
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef COMND_H
|
||||
#define COMND_H
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/linker_set.h>
|
||||
|
||||
/*
|
||||
* Regularized parsing of simple arguments built on top of getopt_long.
|
||||
*/
|
||||
|
||||
typedef enum arg_type {
|
||||
arg_none = 0,
|
||||
arg_uint8,
|
||||
arg_uint16,
|
||||
arg_uint32,
|
||||
arg_uint64,
|
||||
arg_size,
|
||||
arg_string,
|
||||
arg_path,
|
||||
} arg_type;
|
||||
|
||||
// XXX need to change to offsetof for opts and args.
|
||||
// we then need to allocate ctx and pass that into the cmd
|
||||
// stuff. this will be a little tricky and we may need to expand
|
||||
// arg_type stuff.
|
||||
|
||||
struct opts {
|
||||
const char *long_arg;
|
||||
int short_arg;
|
||||
arg_type at;
|
||||
void *ptr; // XXXX change to offset of
|
||||
const char *descr;
|
||||
};
|
||||
|
||||
// XXX TDB: subcommand vs actual argument. maybe with subcmd?
|
||||
// XXX TBD: do we need parsing callback functions?
|
||||
struct args {
|
||||
arg_type at;
|
||||
void *ptr; // XXXX change to offset of
|
||||
const char *descr;
|
||||
};
|
||||
|
||||
typedef void (cmd_load_cb_t)(void *, void *);
|
||||
struct cmd;
|
||||
typedef void (cmd_fn_t)(const struct cmd *nf, int argc, char *argv[]);
|
||||
|
||||
struct cmd {
|
||||
SLIST_ENTRY(cmd) link;
|
||||
const char *name;
|
||||
cmd_fn_t *fn;
|
||||
size_t ctx_size;
|
||||
const struct opts *opts;
|
||||
const struct args *args;
|
||||
const char *descr;
|
||||
SLIST_HEAD(,cmd) subcmd;
|
||||
struct cmd *parent;
|
||||
};
|
||||
|
||||
void cmd_register(struct cmd *, struct cmd *);
|
||||
#define CMD_COMMAND(c) \
|
||||
static void cmd_register_##c(void) __attribute__((constructor)); \
|
||||
static void cmd_register_##c(void) { cmd_register(NULL, &c); }
|
||||
#define CMD_SUBCOMMAND(c,sc) \
|
||||
static void cmd_register_##c_##sc(void) __attribute__((constructor)); \
|
||||
static void cmd_register_##c_##sc(void) { cmd_register(&c, &sc); }
|
||||
|
||||
int arg_parse(int argc, char * const *argv, const struct cmd *f);
|
||||
void arg_help(int argc, char * const *argv, const struct cmd *f);
|
||||
void cmd_init(void);
|
||||
void cmd_load_dir(const char *dir, cmd_load_cb_t *cb, void *argp);
|
||||
int cmd_dispatch(int argc, char *argv[], const struct cmd *);
|
||||
|
||||
#endif /* COMND_H */
|
@ -42,12 +42,24 @@ __FBSDID("$FreeBSD$");
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
#include "comnd.h"
|
||||
|
||||
#define DEVLIST_USAGE \
|
||||
"devlist\n"
|
||||
/* Tables for command line parsing */
|
||||
|
||||
#define NVME_MAX_UNIT 256
|
||||
|
||||
static cmd_fn_t devlist;
|
||||
|
||||
static struct cmd devlist_cmd = {
|
||||
.name = "devlist",
|
||||
.fn = devlist,
|
||||
.descr = "Display a list of NVMe controllers and namespaces."
|
||||
};
|
||||
|
||||
CMD_COMMAND(devlist_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static inline uint32_t
|
||||
ns_get_sector_size(struct nvme_namespace_data *nsdata)
|
||||
{
|
||||
@ -62,21 +74,17 @@ ns_get_sector_size(struct nvme_namespace_data *nsdata)
|
||||
}
|
||||
|
||||
static void
|
||||
devlist(const struct nvme_function *nf, int argc, char *argv[])
|
||||
devlist(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
struct nvme_namespace_data nsdata;
|
||||
char name[64];
|
||||
uint8_t mn[64];
|
||||
uint32_t i;
|
||||
int ch, ctrlr, fd, found, ret;
|
||||
int ctrlr, fd, found, ret;
|
||||
|
||||
while ((ch = getopt(argc, argv, "")) != -1) {
|
||||
switch ((char)ch) {
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
}
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
ctrlr = -1;
|
||||
found = 0;
|
||||
@ -119,5 +127,3 @@ devlist(const struct nvme_function *nf, int argc, char *argv[])
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, devlist, devlist, DEVLIST_USAGE);
|
||||
|
@ -50,8 +50,52 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
#define FIRMWARE_USAGE \
|
||||
"firmware [-s slot] [-f path_to_firmware] [-a] <controller id>\n"
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t firmware;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
static struct options {
|
||||
bool activate;
|
||||
uint32_t slot;
|
||||
const char *fw_img;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.activate = false,
|
||||
.slot = NONE,
|
||||
.fw_img = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts firmware_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("activate", 'a', arg_none, opt, activate,
|
||||
"Attempt to activate firmware"),
|
||||
OPT("slot", 's', arg_uint32, opt, slot,
|
||||
"Slot to activate and/or download firmware to"),
|
||||
OPT("firmware", 'f', arg_path, opt, fw_img,
|
||||
"Firmware image to download"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args firmware_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd firmware_cmd = {
|
||||
.name = "firmware",
|
||||
.fn = firmware,
|
||||
.descr = "Download firmware image to controller.",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = firmware_opts,
|
||||
.args = firmware_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(firmware_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static int
|
||||
slot_has_valid_firmware(int fd, int slot)
|
||||
@ -69,7 +113,7 @@ slot_has_valid_firmware(int fd, int slot)
|
||||
}
|
||||
|
||||
static void
|
||||
read_image_file(char *path, void **buf, int32_t *size)
|
||||
read_image_file(const char *path, void **buf, int32_t *size)
|
||||
{
|
||||
struct stat sb;
|
||||
int32_t filesize;
|
||||
@ -174,74 +218,52 @@ activate_firmware(int fd, int slot, int activate_action)
|
||||
}
|
||||
|
||||
static void
|
||||
firmware(const struct nvme_function *nf, int argc, char *argv[])
|
||||
firmware(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
int fd = -1, slot = 0;
|
||||
int a_flag, f_flag;
|
||||
int fd = -1;
|
||||
int activate_action, reboot_required;
|
||||
int opt;
|
||||
char *p, *image = NULL;
|
||||
char *controller = NULL, prompt[64];
|
||||
char prompt[64];
|
||||
void *buf = NULL;
|
||||
int32_t size = 0;
|
||||
uint16_t oacs_fw;
|
||||
uint8_t fw_slot1_ro, fw_num_slots;
|
||||
struct nvme_controller_data cdata;
|
||||
|
||||
a_flag = f_flag = false;
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
while ((opt = getopt(argc, argv, "af:s:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
a_flag = true;
|
||||
break;
|
||||
case 's':
|
||||
slot = strtol(optarg, &p, 0);
|
||||
if (p != NULL && *p != '\0') {
|
||||
fprintf(stderr,
|
||||
"\"%s\" not valid slot.\n",
|
||||
optarg);
|
||||
usage(nf);
|
||||
} else if (slot == 0) {
|
||||
fprintf(stderr,
|
||||
"0 is not a valid slot number. "
|
||||
"Slot numbers start at 1.\n");
|
||||
usage(nf);
|
||||
} else if (slot > 7) {
|
||||
fprintf(stderr,
|
||||
"Slot number %s specified which is "
|
||||
"greater than max allowed slot number of "
|
||||
"7.\n", optarg);
|
||||
usage(nf);
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
image = optarg;
|
||||
f_flag = true;
|
||||
break;
|
||||
}
|
||||
if (opt.slot == 0) {
|
||||
fprintf(stderr,
|
||||
"0 is not a valid slot number. "
|
||||
"Slot numbers start at 1.\n");
|
||||
arg_help(argc, argv, f);
|
||||
} else if (opt.slot > 7 && opt.slot != NONE) {
|
||||
fprintf(stderr,
|
||||
"Slot number %s specified which is "
|
||||
"greater than max allowed slot number of "
|
||||
"7.\n", optarg);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
/* Check that a controller (and not a namespace) was specified. */
|
||||
if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL)
|
||||
usage(nf);
|
||||
|
||||
if (!f_flag && !a_flag) {
|
||||
if (!opt.activate && opt.fw_img == NULL) {
|
||||
fprintf(stderr,
|
||||
"Neither a replace ([-f path_to_firmware]) nor "
|
||||
"activate ([-a]) firmware image action\n"
|
||||
"was specified.\n");
|
||||
usage(nf);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
if (!f_flag && a_flag && slot == 0) {
|
||||
/* Check that a controller (and not a namespace) was specified. */
|
||||
if (strstr(opt.dev, NVME_NS_PREFIX) != NULL)
|
||||
arg_help(argc, argv, f);
|
||||
|
||||
if (opt.activate && opt.fw_img == NULL && opt.slot == 0) {
|
||||
fprintf(stderr,
|
||||
"Slot number to activate not specified.\n");
|
||||
usage(nf);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
controller = argv[optind];
|
||||
open_dev(controller, &fd, 1, 1);
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cdata);
|
||||
|
||||
oacs_fw = (cdata.oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
|
||||
@ -254,44 +276,45 @@ firmware(const struct nvme_function *nf, int argc, char *argv[])
|
||||
fw_slot1_ro = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) &
|
||||
NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK;
|
||||
|
||||
if (f_flag && slot == 1 && fw_slot1_ro)
|
||||
errx(1, "slot %d is marked as read only", slot);
|
||||
if (opt.fw_img && opt.slot == 1 && fw_slot1_ro)
|
||||
errx(1, "slot %d is marked as read only", opt.slot);
|
||||
|
||||
fw_num_slots = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
|
||||
NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
|
||||
|
||||
if (slot > fw_num_slots)
|
||||
if (opt.slot > fw_num_slots)
|
||||
errx(1,
|
||||
"slot %d specified but controller only supports %d slots",
|
||||
slot, fw_num_slots);
|
||||
opt.slot, fw_num_slots);
|
||||
|
||||
if (a_flag && !f_flag && !slot_has_valid_firmware(fd, slot))
|
||||
if (opt.activate && opt.fw_img == NULL &&
|
||||
!slot_has_valid_firmware(fd, opt.slot))
|
||||
errx(1,
|
||||
"slot %d does not contain valid firmware,\n"
|
||||
"try 'nvmecontrol logpage -p 3 %s' to get a list "
|
||||
"of available images\n",
|
||||
slot, controller);
|
||||
opt.slot, opt.dev);
|
||||
|
||||
if (f_flag)
|
||||
read_image_file(image, &buf, &size);
|
||||
if (opt.fw_img)
|
||||
read_image_file(opt.fw_img, &buf, &size);
|
||||
|
||||
if (f_flag && a_flag)
|
||||
if (opt.fw_img != NULL&& opt.activate)
|
||||
printf("You are about to download and activate "
|
||||
"firmware image (%s) to controller %s.\n"
|
||||
"This may damage your controller and/or "
|
||||
"overwrite an existing firmware image.\n",
|
||||
image, controller);
|
||||
else if (a_flag)
|
||||
opt.fw_img, opt.dev);
|
||||
else if (opt.activate)
|
||||
printf("You are about to activate a new firmware "
|
||||
"image on controller %s.\n"
|
||||
"This may damage your controller.\n",
|
||||
controller);
|
||||
else if (f_flag)
|
||||
opt.dev);
|
||||
else if (opt.fw_img != NULL)
|
||||
printf("You are about to download firmware image "
|
||||
"(%s) to controller %s.\n"
|
||||
"This may damage your controller and/or "
|
||||
"overwrite an existing firmware image.\n",
|
||||
image, controller);
|
||||
opt.fw_img, opt.dev);
|
||||
|
||||
printf("Are you sure you want to continue? (yes/no) ");
|
||||
while (1) {
|
||||
@ -303,9 +326,9 @@ firmware(const struct nvme_function *nf, int argc, char *argv[])
|
||||
printf("Please answer \"yes\" or \"no\". ");
|
||||
}
|
||||
|
||||
if (f_flag) {
|
||||
if (opt.fw_img != NULL) {
|
||||
update_firmware(fd, buf, size);
|
||||
if (a_flag)
|
||||
if (opt.activate)
|
||||
activate_action = NVME_AA_REPLACE_ACTIVATE;
|
||||
else
|
||||
activate_action = NVME_AA_REPLACE_NO_ACTIVATE;
|
||||
@ -313,9 +336,9 @@ firmware(const struct nvme_function *nf, int argc, char *argv[])
|
||||
activate_action = NVME_AA_ACTIVATE;
|
||||
}
|
||||
|
||||
reboot_required = activate_firmware(fd, slot, activate_action);
|
||||
reboot_required = activate_firmware(fd, opt.slot, activate_action);
|
||||
|
||||
if (a_flag) {
|
||||
if (opt.activate) {
|
||||
if (reboot_required) {
|
||||
printf("New firmware image activated but requires "
|
||||
"conventional reset (i.e. reboot) to "
|
||||
@ -325,12 +348,10 @@ firmware(const struct nvme_function *nf, int argc, char *argv[])
|
||||
"effect after next controller reset.\n"
|
||||
"Controller reset can be initiated via "
|
||||
"'nvmecontrol reset %s'\n",
|
||||
controller);
|
||||
opt.dev);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, firmware, firmware, FIRMWARE_USAGE);
|
||||
|
@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -43,57 +44,104 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
#define FORMAT_USAGE \
|
||||
"format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] <controller id|namespace id>\n"
|
||||
#define NONE 0xffffffffu
|
||||
#define SES_NONE 0
|
||||
#define SES_USER 1
|
||||
#define SES_CRYPTO 2
|
||||
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t format;
|
||||
|
||||
static struct options {
|
||||
uint32_t lbaf;
|
||||
uint32_t ms;
|
||||
uint32_t pi;
|
||||
uint32_t pil;
|
||||
uint32_t ses;
|
||||
bool Eflag;
|
||||
bool Cflag;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.lbaf = NONE,
|
||||
.ms = NONE,
|
||||
.pi = NONE,
|
||||
.pil = NONE,
|
||||
.ses = SES_NONE,
|
||||
.Eflag = false,
|
||||
.Cflag = false,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts format_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("crypto", 'C', arg_none, opt, Cflag,
|
||||
"Crptographically erase user data by forgetting key"),
|
||||
OPT("erase", 'E', arg_none, opt, Eflag,
|
||||
"Erase user data"),
|
||||
OPT("lbaf", 'f', arg_uint32, opt, lbaf,
|
||||
"Set the LBA Format to apply to the media"),
|
||||
OPT("ms", 'm', arg_uint32, opt, ms,
|
||||
"Slot to activate and/or download format to"),
|
||||
OPT("pi", 'p', arg_uint32, opt, pi,
|
||||
"Slot to activate and/or download format to"),
|
||||
OPT("pil", 'l', arg_uint32, opt, pil,
|
||||
"Slot to activate and/or download format to"),
|
||||
OPT("ses", 's', arg_uint32, opt, ses,
|
||||
"Slot to activate and/or download format to"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args format_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id|namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd format_cmd = {
|
||||
.name = "format",
|
||||
.fn = format,
|
||||
.descr = "Format/erase one or all the namespaces.",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = format_opts,
|
||||
.args = format_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(format_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static void
|
||||
format(const struct nvme_function *nf, int argc, char *argv[])
|
||||
format(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cd;
|
||||
struct nvme_namespace_data nsd;
|
||||
struct nvme_pt_command pt;
|
||||
char path[64];
|
||||
char *target;
|
||||
const char *target;
|
||||
uint32_t nsid;
|
||||
int ch, fd;
|
||||
int lbaf = -1, mset = -1, pi = -1, pil = -1, ses = 0;
|
||||
int lbaf, ms, pi, pil, ses, fd;
|
||||
|
||||
if (argc < 2)
|
||||
usage(nf);
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
while ((ch = getopt(argc, argv, "f:m:p:l:EC")) != -1) {
|
||||
switch ((char)ch) {
|
||||
case 'f':
|
||||
lbaf = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'm':
|
||||
mset = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'p':
|
||||
pi = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'l':
|
||||
pil = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'E':
|
||||
if (ses == 2)
|
||||
errx(1, "-E and -C are mutually exclusive");
|
||||
ses = 1;
|
||||
break;
|
||||
case 'C':
|
||||
if (ses == 1)
|
||||
errx(1, "-E and -C are mutually exclusive");
|
||||
ses = 2;
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
if (opt.Eflag || opt.Cflag || opt.ses != SES_NONE) {
|
||||
fprintf(stderr,
|
||||
"Only one of -E, -C or -s may be specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
/* Check that a controller or namespace was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
target = argv[optind];
|
||||
target = opt.dev;
|
||||
lbaf = opt.lbaf;
|
||||
ms = opt.ms;
|
||||
pi = opt.pi;
|
||||
pil = opt.pil;
|
||||
if (opt.Eflag)
|
||||
ses = SES_USER;
|
||||
else if (opt.Cflag)
|
||||
ses = SES_CRYPTO;
|
||||
else
|
||||
ses = opt.ses;
|
||||
|
||||
/*
|
||||
* Check if the specified device node exists before continuing.
|
||||
@ -126,15 +174,15 @@ format(const struct nvme_function *nf, int argc, char *argv[])
|
||||
NVME_CTRLR_DATA_OACS_FORMAT_MASK) == 0)
|
||||
errx(1, "controller does not support format");
|
||||
if (((cd.fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == 2)
|
||||
NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == SES_CRYPTO)
|
||||
errx(1, "controller does not support cryptographic erase");
|
||||
|
||||
if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
|
||||
if (((cd.fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == 0)
|
||||
NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == SES_NONE)
|
||||
errx(1, "controller does not support per-NS format");
|
||||
if (((cd.fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) &
|
||||
NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != 0)
|
||||
NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != SES_NONE)
|
||||
errx(1, "controller does not support per-NS erase");
|
||||
|
||||
/* Try to keep previous namespace parameters. */
|
||||
@ -144,8 +192,8 @@ format(const struct nvme_function *nf, int argc, char *argv[])
|
||||
& NVME_NS_DATA_FLBAS_FORMAT_MASK;
|
||||
if (lbaf > nsd.nlbaf)
|
||||
errx(1, "LBA format is out of range");
|
||||
if (mset < 0)
|
||||
mset = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT)
|
||||
if (ms < 0)
|
||||
ms = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT)
|
||||
& NVME_NS_DATA_FLBAS_EXTENDED_MASK;
|
||||
if (pi < 0)
|
||||
pi = (nsd.dps >> NVME_NS_DATA_DPS_MD_START_SHIFT)
|
||||
@ -158,8 +206,8 @@ format(const struct nvme_function *nf, int argc, char *argv[])
|
||||
/* We have no previous parameters, so default to zeroes. */
|
||||
if (lbaf < 0)
|
||||
lbaf = 0;
|
||||
if (mset < 0)
|
||||
mset = 0;
|
||||
if (ms < 0)
|
||||
ms = 0;
|
||||
if (pi < 0)
|
||||
pi = 0;
|
||||
if (pil < 0)
|
||||
@ -170,7 +218,7 @@ format(const struct nvme_function *nf, int argc, char *argv[])
|
||||
pt.cmd.opc = NVME_OPC_FORMAT_NVM;
|
||||
pt.cmd.nsid = htole32(nsid);
|
||||
pt.cmd.cdw10 = htole32((ses << 9) + (pil << 8) + (pi << 5) +
|
||||
(mset << 4) + lbaf);
|
||||
(ms << 4) + lbaf);
|
||||
|
||||
if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
|
||||
err(1, "format request failed");
|
||||
@ -180,5 +228,3 @@ format(const struct nvme_function *nf, int argc, char *argv[])
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, format, format, FORMAT_USAGE);
|
||||
|
@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -43,8 +44,15 @@ __FBSDID("$FreeBSD$");
|
||||
#include "nvmecontrol.h"
|
||||
#include "nvmecontrol_ext.h"
|
||||
|
||||
#define IDENTIFY_USAGE \
|
||||
"identify [-x [-v]] <controller id|namespace id>\n"
|
||||
static struct options {
|
||||
bool hex;
|
||||
bool verbose;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.hex = false,
|
||||
.verbose = false,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static void
|
||||
print_namespace(struct nvme_namespace_data *nsdata)
|
||||
@ -160,35 +168,17 @@ print_namespace(struct nvme_namespace_data *nsdata)
|
||||
}
|
||||
|
||||
static void
|
||||
identify_ctrlr(const struct nvme_function *nf, int argc, char *argv[])
|
||||
identify_ctrlr(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
int ch, fd, hexflag = 0, hexlength;
|
||||
int verboseflag = 0;
|
||||
int fd, hexlength;
|
||||
|
||||
while ((ch = getopt(argc, argv, "vx")) != -1) {
|
||||
switch ((char)ch) {
|
||||
case 'v':
|
||||
verboseflag = 1;
|
||||
break;
|
||||
case 'x':
|
||||
hexflag = 1;
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that a controller was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cdata);
|
||||
close(fd);
|
||||
|
||||
if (hexflag == 1) {
|
||||
if (verboseflag == 1)
|
||||
if (opt.hex) {
|
||||
if (opt.verbose)
|
||||
hexlength = sizeof(struct nvme_controller_data);
|
||||
else
|
||||
hexlength = offsetof(struct nvme_controller_data,
|
||||
@ -197,9 +187,9 @@ identify_ctrlr(const struct nvme_function *nf, int argc, char *argv[])
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (verboseflag == 1) {
|
||||
if (opt.verbose) {
|
||||
fprintf(stderr, "-v not currently supported without -x\n");
|
||||
usage(nf);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
nvme_print_controller(&cdata);
|
||||
@ -207,37 +197,19 @@ identify_ctrlr(const struct nvme_function *nf, int argc, char *argv[])
|
||||
}
|
||||
|
||||
static void
|
||||
identify_ns(const struct nvme_function *nf,int argc, char *argv[])
|
||||
identify_ns(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_namespace_data nsdata;
|
||||
char path[64];
|
||||
int ch, fd, hexflag = 0, hexlength;
|
||||
int verboseflag = 0;
|
||||
int fd, hexlength;
|
||||
uint32_t nsid;
|
||||
|
||||
while ((ch = getopt(argc, argv, "vx")) != -1) {
|
||||
switch ((char)ch) {
|
||||
case 'v':
|
||||
verboseflag = 1;
|
||||
break;
|
||||
case 'x':
|
||||
hexflag = 1;
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that a namespace was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
/*
|
||||
* 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(argv[optind], &fd, 1, 1);
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
close(fd);
|
||||
|
||||
/*
|
||||
@ -246,13 +218,13 @@ identify_ns(const struct nvme_function *nf,int argc, char *argv[])
|
||||
* the IDENTIFY command itself. So parse the namespace's device node
|
||||
* string to get the controller substring and namespace ID.
|
||||
*/
|
||||
parse_ns_str(argv[optind], path, &nsid);
|
||||
parse_ns_str(opt.dev, path, &nsid);
|
||||
open_dev(path, &fd, 1, 1);
|
||||
read_namespace_data(fd, nsid, &nsdata);
|
||||
close(fd);
|
||||
|
||||
if (hexflag == 1) {
|
||||
if (verboseflag == 1)
|
||||
if (opt.hex) {
|
||||
if (opt.verbose)
|
||||
hexlength = sizeof(struct nvme_namespace_data);
|
||||
else
|
||||
hexlength = offsetof(struct nvme_namespace_data,
|
||||
@ -261,9 +233,9 @@ identify_ns(const struct nvme_function *nf,int argc, char *argv[])
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (verboseflag == 1) {
|
||||
if (opt.verbose) {
|
||||
fprintf(stderr, "-v not currently supported without -x\n");
|
||||
usage(nf);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
print_namespace(&nsdata);
|
||||
@ -271,32 +243,42 @@ identify_ns(const struct nvme_function *nf,int argc, char *argv[])
|
||||
}
|
||||
|
||||
static void
|
||||
identify(const struct nvme_function *nf, int argc, char *argv[])
|
||||
identify(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
char *target;
|
||||
|
||||
if (argc < 2)
|
||||
usage(nf);
|
||||
|
||||
while (getopt(argc, argv, "vx") != -1) ;
|
||||
|
||||
/* Check that a controller or namespace was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
target = argv[optind];
|
||||
|
||||
optreset = 1;
|
||||
optind = 1;
|
||||
arg_parse(argc, argv, f);
|
||||
|
||||
/*
|
||||
* If device node contains "ns", we consider it a namespace,
|
||||
* otherwise, consider it a controller.
|
||||
*/
|
||||
if (strstr(target, NVME_NS_PREFIX) == NULL)
|
||||
identify_ctrlr(nf, argc, argv);
|
||||
if (strstr(opt.dev, NVME_NS_PREFIX) == NULL)
|
||||
identify_ctrlr(f, argc, argv);
|
||||
else
|
||||
identify_ns(nf, argc, argv);
|
||||
identify_ns(f, argc, argv);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, identify, identify, IDENTIFY_USAGE);
|
||||
static const struct opts identify_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("hex", 'x', arg_none, opt, hex,
|
||||
"Print identiy information in hex"),
|
||||
OPT("verbose", 'v', arg_none, opt, verbose,
|
||||
"More verbosity: print entire identify table"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args identify_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id|namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd identify_cmd = {
|
||||
.name = "identify",
|
||||
.fn = identify,
|
||||
.descr = "Print a human-readable summary of the IDENTIFY information",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = identify_opts,
|
||||
.args = identify_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(identify_cmd);
|
||||
|
@ -48,8 +48,56 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
#define LOGPAGE_USAGE \
|
||||
"logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace id>\n" \
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t logpage;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
static struct options {
|
||||
bool binary;
|
||||
bool hex;
|
||||
uint32_t page;
|
||||
const char *vendor;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.binary = false,
|
||||
.hex = false,
|
||||
.page = NONE,
|
||||
.vendor = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts logpage_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("binary", 'b', arg_none, opt, binary,
|
||||
"Dump the log page as binary"),
|
||||
OPT("hex", 'x', arg_none, opt, hex,
|
||||
"Dump the log page as hex"),
|
||||
OPT("page", 'p', arg_uint32, opt, page,
|
||||
"Page to dump"),
|
||||
OPT("vendor", 'v', arg_string, opt, vendor,
|
||||
"Vendor specific formatting"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args logpage_args[] = {
|
||||
{ arg_string, &opt.dev, "<controller id|namespace id>" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd logpage_cmd = {
|
||||
.name = "logpage",
|
||||
.fn = logpage,
|
||||
.descr = "Print logpages in human-readable form",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = logpage_opts,
|
||||
.args = logpage_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(logpage_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
#define MAX_FW_SLOTS (7)
|
||||
|
||||
@ -348,69 +396,40 @@ logpage_help(void)
|
||||
}
|
||||
|
||||
static void
|
||||
logpage(const struct nvme_function *nf, int argc, char *argv[])
|
||||
logpage(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
int log_page = 0, pageflag = false;
|
||||
int binflag = false, hexflag = false, ns_specified;
|
||||
int opt;
|
||||
char *p;
|
||||
bool ns_specified;
|
||||
char cname[64];
|
||||
uint32_t nsid, size;
|
||||
void *buf;
|
||||
const char *vendor = NULL;
|
||||
const struct logpage_function *f;
|
||||
const struct logpage_function *lpf;
|
||||
struct nvme_controller_data cdata;
|
||||
print_fn_t print_fn;
|
||||
uint8_t ns_smart;
|
||||
|
||||
while ((opt = getopt(argc, argv, "bp:xv:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
binflag = true;
|
||||
break;
|
||||
case 'p':
|
||||
if (strcmp(optarg, "help") == 0)
|
||||
logpage_help();
|
||||
|
||||
/* TODO: Add human-readable ASCII page IDs */
|
||||
log_page = strtol(optarg, &p, 0);
|
||||
if (p != NULL && *p != '\0') {
|
||||
fprintf(stderr,
|
||||
"\"%s\" not valid log page id.\n",
|
||||
optarg);
|
||||
usage(nf);
|
||||
}
|
||||
pageflag = true;
|
||||
break;
|
||||
case 'x':
|
||||
hexflag = true;
|
||||
break;
|
||||
case 'v':
|
||||
if (strcmp(optarg, "help") == 0)
|
||||
logpage_help();
|
||||
vendor = optarg;
|
||||
break;
|
||||
}
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (opt.hex && opt.binary) {
|
||||
fprintf(stderr,
|
||||
"Can't specify both binary and hex\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
if (!pageflag) {
|
||||
printf("Missing page_id (-p).\n");
|
||||
usage(nf);
|
||||
if (opt.vendor != NULL && strcmp(opt.vendor, "help") == 0)
|
||||
logpage_help();
|
||||
if (opt.page == NONE) {
|
||||
fprintf(stderr, "Missing page_id (-p).\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
/* Check that a controller and/or namespace was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
|
||||
if (strstr(opt.dev, NVME_NS_PREFIX) != NULL) {
|
||||
ns_specified = true;
|
||||
parse_ns_str(argv[optind], cname, &nsid);
|
||||
parse_ns_str(opt.dev, cname, &nsid);
|
||||
open_dev(cname, &fd, 1, 1);
|
||||
} else {
|
||||
ns_specified = false;
|
||||
nsid = NVME_GLOBAL_NAMESPACE_TAG;
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
}
|
||||
|
||||
read_controller_data(fd, &cdata);
|
||||
@ -424,9 +443,9 @@ logpage(const struct nvme_function *nf, int argc, char *argv[])
|
||||
* namespace basis.
|
||||
*/
|
||||
if (ns_specified) {
|
||||
if (log_page != NVME_LOG_HEALTH_INFORMATION)
|
||||
if (opt.page != NVME_LOG_HEALTH_INFORMATION)
|
||||
errx(1, "log page %d valid only at controller level",
|
||||
log_page);
|
||||
opt.page);
|
||||
if (ns_smart == 0)
|
||||
errx(1,
|
||||
"controller does not support per namespace "
|
||||
@ -435,9 +454,9 @@ logpage(const struct nvme_function *nf, int argc, char *argv[])
|
||||
|
||||
print_fn = print_log_hex;
|
||||
size = DEFAULT_SIZE;
|
||||
if (binflag)
|
||||
if (opt.binary)
|
||||
print_fn = print_bin;
|
||||
if (!binflag && !hexflag) {
|
||||
if (!opt.binary && !opt.hex) {
|
||||
/*
|
||||
* See if there is a pretty print function for the specified log
|
||||
* page. If one isn't found, we just revert to the default
|
||||
@ -445,30 +464,28 @@ logpage(const struct nvme_function *nf, int argc, char *argv[])
|
||||
* the page is vendor specific, don't match the print function
|
||||
* unless the vendors match.
|
||||
*/
|
||||
SLIST_FOREACH(f, &logpages, link) {
|
||||
if (f->vendor != NULL && vendor != NULL &&
|
||||
strcmp(f->vendor, vendor) != 0)
|
||||
SLIST_FOREACH(lpf, &logpages, link) {
|
||||
if (lpf->vendor != NULL && vendor != NULL &&
|
||||
strcmp(lpf->vendor, vendor) != 0)
|
||||
continue;
|
||||
if (log_page != f->log_page)
|
||||
if (opt.page != lpf->log_page)
|
||||
continue;
|
||||
print_fn = f->print_fn;
|
||||
size = f->size;
|
||||
print_fn = lpf->print_fn;
|
||||
size = lpf->size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (log_page == NVME_LOG_ERROR) {
|
||||
if (opt.page == NVME_LOG_ERROR) {
|
||||
size = sizeof(struct nvme_error_information_entry);
|
||||
size *= (cdata.elpe + 1);
|
||||
}
|
||||
|
||||
/* Read the log page */
|
||||
buf = get_log_buffer(size);
|
||||
read_logpage(fd, log_page, nsid, buf, size);
|
||||
read_logpage(fd, opt.page, nsid, buf, size);
|
||||
print_fn(&cdata, buf, size);
|
||||
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE);
|
||||
|
@ -41,22 +41,58 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
#define WDC_USAGE \
|
||||
"wdc (cap-diag)\n"
|
||||
/* Tables for command line parsing */
|
||||
|
||||
NVME_CMD_DECLARE(wdc, struct nvme_function);
|
||||
static cmd_fn_t wdc;
|
||||
static cmd_fn_t wdc_cap_diag;
|
||||
|
||||
#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 wdc_cmd = {
|
||||
.name = "wdc", .fn = wdc, .descr = "wdc vendor specific commands", .ctx_size = 0, .opts = NULL, .args = NULL,
|
||||
};
|
||||
|
||||
CMD_COMMAND(wdc_cmd);
|
||||
|
||||
static struct options
|
||||
{
|
||||
const char *template;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.template = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static const struct opts opts[] = {
|
||||
OPT("template", 'o', arg_string, opt, template,
|
||||
"Template for paths to use for different logs"),
|
||||
OPT_END
|
||||
};
|
||||
|
||||
static const struct args args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd cap_diag_cmd = {
|
||||
.name = "cap-diag",
|
||||
.fn = wdc_cap_diag,
|
||||
.descr = "Retrieve the cap-diag logs from the drive",
|
||||
.ctx_size = sizeof(struct options),
|
||||
.opts = opts,
|
||||
.args = args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(wdc_cmd, cap_diag_cmd);
|
||||
|
||||
#define WDC_NVME_TOC_SIZE 8
|
||||
|
||||
#define WDC_NVME_CAP_DIAG_OPCODE 0xe6
|
||||
#define WDC_NVME_CAP_DIAG_CMD 0x0000
|
||||
|
||||
static void wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]);
|
||||
|
||||
#define WDC_CAP_DIAG_USAGE "wdc cap-diag [-o path-template]\n"
|
||||
|
||||
NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE);
|
||||
|
||||
static void
|
||||
wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix)
|
||||
{
|
||||
@ -153,27 +189,20 @@ wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode,
|
||||
}
|
||||
|
||||
static void
|
||||
wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[])
|
||||
wdc_cap_diag(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
char path_tmpl[MAXPATHLEN];
|
||||
int ch, fd;
|
||||
char tmpl[MAXPATHLEN];
|
||||
int fd;
|
||||
|
||||
path_tmpl[0] = '\0';
|
||||
while ((ch = getopt(argc, argv, "o:")) != -1) {
|
||||
switch ((char)ch) {
|
||||
case 'o':
|
||||
strlcpy(path_tmpl, optarg, MAXPATHLEN);
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (opt.template == NULL) {
|
||||
fprintf(stderr, "Missing template arg.\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
/* Check that a controller was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
|
||||
wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE,
|
||||
strlcpy(tmpl, opt.template, sizeof(tmpl));
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
wdc_do_dump(fd, tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE,
|
||||
WDC_NVME_CAP_DIAG_CMD, 4);
|
||||
|
||||
close(fd);
|
||||
@ -182,10 +211,10 @@ wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[])
|
||||
}
|
||||
|
||||
static void
|
||||
wdc(const struct nvme_function *nf __unused, int argc, char *argv[])
|
||||
wdc(const struct cmd *nf __unused, int argc, char *argv[])
|
||||
{
|
||||
|
||||
DISPATCH(argc, argv, wdc);
|
||||
cmd_dispatch(argc, argv, &wdc_cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -593,4 +622,3 @@ NVME_LOGPAGE(hgst_info,
|
||||
NVME_LOGPAGE(wdc_info,
|
||||
HGST_INFO_LOG, "wdc", "Detailed Health/SMART",
|
||||
print_hgst_info_log, DEFAULT_SIZE);
|
||||
NVME_COMMAND(top, wdc, wdc, WDC_USAGE);
|
||||
|
@ -41,35 +41,194 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
NVME_CMD_DECLARE(ns, struct nvme_function);
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t ns;
|
||||
static cmd_fn_t nscreate;
|
||||
static cmd_fn_t nsdelete;
|
||||
static cmd_fn_t nsattach;
|
||||
static cmd_fn_t nsdetach;
|
||||
|
||||
#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 commands", .ctx_size = 0, .opts = NULL, .args = NULL,
|
||||
};
|
||||
|
||||
CMD_COMMAND(ns_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 new 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 new 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, nsid,
|
||||
"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 new namespace",
|
||||
.ctx_size = sizeof(attach_opt),
|
||||
.opts = attach_opts,
|
||||
.args = attach_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, attach_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, nsid,
|
||||
"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 new namespace",
|
||||
.ctx_size = sizeof(detach_opt),
|
||||
.opts = detach_opts,
|
||||
.args = detach_args,
|
||||
};
|
||||
|
||||
CMD_SUBCOMMAND(ns_cmd, detach_cmd);
|
||||
|
||||
#define NS_USAGE \
|
||||
"ns (create|delete|attach|detach)\n"
|
||||
|
||||
/* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */
|
||||
|
||||
#define NSCREATE_USAGE \
|
||||
"ns create -s size [-c cap] [-f fmt] [-m mset] [-n nmic] [-p pi] [-l pil] nvmeN\n"
|
||||
|
||||
#define NSDELETE_USAGE \
|
||||
"ns delete -n nsid nvmeN\n"
|
||||
|
||||
#define NSATTACH_USAGE \
|
||||
"ns attach -n nsid [-c ctrlrid] nvmeN \n"
|
||||
|
||||
#define NSDETACH_USAGE \
|
||||
"ns detach -n nsid [-c ctrlrid] nvmeN\n"
|
||||
|
||||
static void nscreate(const struct nvme_function *nf, int argc, char *argv[]);
|
||||
static void nsdelete(const struct nvme_function *nf, int argc, char *argv[]);
|
||||
static void nsattach(const struct nvme_function *nf, int argc, char *argv[]);
|
||||
static void nsdetach(const struct nvme_function *nf, int argc, char *argv[]);
|
||||
|
||||
NVME_COMMAND(ns, create, nscreate, NSCREATE_USAGE);
|
||||
NVME_COMMAND(ns, delete, nsdelete, NSDELETE_USAGE);
|
||||
NVME_COMMAND(ns, attach, nsattach, NSATTACH_USAGE);
|
||||
NVME_COMMAND(ns, detach, nsdetach, NSDETACH_USAGE);
|
||||
|
||||
struct ns_result_str {
|
||||
uint16_t res;
|
||||
const char * str;
|
||||
@ -110,54 +269,25 @@ get_res_str(uint16_t res)
|
||||
* 0xb = Thin Provisioning Not supported
|
||||
*/
|
||||
static void
|
||||
nscreate(const struct nvme_function *nf, int argc, char *argv[])
|
||||
nscreate(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
struct nvme_namespace_data nsdata;
|
||||
int64_t nsze = -1, cap = -1;
|
||||
int ch, fd, result, lbaf = 0, mset = 0, nmic = -1, pi = 0, pil = 0;
|
||||
int fd, result;
|
||||
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
while ((ch = getopt(argc, argv, "s:c:f:m:n:p:l:")) != -1) {
|
||||
switch (ch) {
|
||||
case 's':
|
||||
nsze = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
case 'c':
|
||||
cap = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
case 'f':
|
||||
lbaf = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
case 'm':
|
||||
mset = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'n':
|
||||
nmic = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'p':
|
||||
pi = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'l':
|
||||
pil = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
if (cap == -1)
|
||||
cap = nsze;
|
||||
if (nsze == -1 || cap == -1)
|
||||
usage(nf);
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
open_dev(create_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
@ -166,23 +296,29 @@ nscreate(const struct nvme_function *nf, int argc, char *argv[])
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
/* Allow namespaces sharing if Multi-Path I/O is supported. */
|
||||
if (nmic == -1) {
|
||||
nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK <<
|
||||
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 = (uint64_t)nsze;
|
||||
nsdata.ncap = (uint64_t)cap;
|
||||
nsdata.flbas = ((lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK)
|
||||
<< NVME_NS_DATA_FLBAS_FORMAT_SHIFT) |
|
||||
((mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK)
|
||||
<< NVME_NS_DATA_FLBAS_EXTENDED_SHIFT);
|
||||
nsdata.dps = ((pi & NVME_NS_DATA_DPS_MD_START_MASK)
|
||||
<< NVME_NS_DATA_DPS_MD_START_SHIFT) |
|
||||
((pil & NVME_NS_DATA_DPS_PIT_MASK)
|
||||
<< NVME_NS_DATA_DPS_PIT_SHIFT);
|
||||
nsdata.nmic = nmic;
|
||||
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));
|
||||
@ -205,30 +341,22 @@ nscreate(const struct nvme_function *nf, int argc, char *argv[])
|
||||
}
|
||||
|
||||
static void
|
||||
nsdelete(const struct nvme_function *nf, int argc, char *argv[])
|
||||
nsdelete(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int ch, fd, result, nsid = -2;
|
||||
int fd, result;
|
||||
char buf[2];
|
||||
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
while ((ch = getopt(argc, argv, "n:")) != -1) {
|
||||
switch ((char)ch) {
|
||||
case 'n':
|
||||
nsid = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (delete_opt.nsid == NONE) {
|
||||
fprintf(stderr,
|
||||
"No NSID specified");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
if (optind >= argc || nsid == -2)
|
||||
usage(nf);
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
open_dev(delete_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
@ -242,17 +370,17 @@ nsdelete(const struct nvme_function *nf, int argc, char *argv[])
|
||||
pt.buf = buf;
|
||||
pt.len = sizeof(buf);
|
||||
pt.is_read = 1;
|
||||
pt.cmd.nsid = (uint32_t)nsid;
|
||||
pt.cmd.nsid = delete_opt.nsid;
|
||||
|
||||
if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0)
|
||||
errx(1, "ioctl request to %s failed: %d", argv[optind], result);
|
||||
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", nsid);
|
||||
printf("namespace %d deleted\n", delete_opt.nsid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -272,37 +400,20 @@ nsdelete(const struct nvme_function *nf, int argc, char *argv[])
|
||||
* 0x2 Invalid Field can occur if ctrlrid d.n.e in system.
|
||||
*/
|
||||
static void
|
||||
nsattach(const struct nvme_function *nf, int argc, char *argv[])
|
||||
nsattach(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int ctrlrid = -2;
|
||||
int fd, ch, result, nsid = -1;
|
||||
int fd, result;
|
||||
uint16_t clist[2048];
|
||||
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
while ((ch = getopt(argc, argv, "n:c:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'n':
|
||||
nsid = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
case 'c':
|
||||
ctrlrid = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (attach_opt.nsid == NONE) {
|
||||
fprintf(stderr, "No valid NSID specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
if (nsid == -1 )
|
||||
usage(nf);
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
open_dev(attach_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
@ -310,7 +421,7 @@ nsattach(const struct nvme_function *nf, int argc, char *argv[])
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
if (ctrlrid == -1) {
|
||||
if (attach_opt.ctrlrid == NONE) {
|
||||
/* Get full list of controllers to attach to. */
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_IDENTIFY;
|
||||
@ -324,64 +435,47 @@ nsattach(const struct nvme_function *nf, int argc, char *argv[])
|
||||
errx(1, "identify request returned error");
|
||||
} else {
|
||||
/* By default attach to this controller. */
|
||||
if (ctrlrid == -2)
|
||||
ctrlrid = cd.ctrlr_id;
|
||||
if (attach_opt.ctrlrid == NONE - 1)
|
||||
attach_opt.ctrlrid = cd.ctrlr_id;
|
||||
memset(&clist, 0, sizeof(clist));
|
||||
clist[0] = htole16(1);
|
||||
clist[1] = htole16(ctrlrid);
|
||||
clist[1] = htole16(attach_opt.ctrlrid);
|
||||
}
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
|
||||
pt.cmd.cdw10 = 0; /* attach */
|
||||
pt.cmd.nsid = (uint32_t)nsid;
|
||||
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", argv[optind], result);
|
||||
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", nsid);
|
||||
printf("namespace %d attached\n", attach_opt.nsid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
nsdetach(const struct nvme_function *nf, int argc, char *argv[])
|
||||
nsdetach(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_pt_command pt;
|
||||
struct nvme_controller_data cd;
|
||||
int ctrlrid = -2;
|
||||
int fd, ch, result, nsid = -1;
|
||||
int fd, result;
|
||||
uint16_t clist[2048];
|
||||
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
while ((ch = getopt(argc, argv, "n:c:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'n':
|
||||
nsid = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
case 'c':
|
||||
ctrlrid = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
if (attach_opt.nsid == NONE) {
|
||||
fprintf(stderr, "No valid NSID specified\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
if (nsid == -1)
|
||||
usage(nf);
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
open_dev(attach_opt.dev, &fd, 1, 1);
|
||||
read_controller_data(fd, &cd);
|
||||
|
||||
/* Check that controller can execute this command. */
|
||||
@ -389,11 +483,11 @@ nsdetach(const struct nvme_function *nf, int argc, char *argv[])
|
||||
NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0)
|
||||
errx(1, "controller does not support namespace management");
|
||||
|
||||
if (ctrlrid == -1) {
|
||||
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(nsid);
|
||||
pt.cmd.nsid = htole32(detach_opt.nsid);
|
||||
pt.cmd.cdw10 = htole32(0x12);
|
||||
pt.buf = clist;
|
||||
pt.len = sizeof(clist);
|
||||
@ -403,24 +497,24 @@ nsdetach(const struct nvme_function *nf, int argc, char *argv[])
|
||||
if (nvme_completion_is_error(&pt.cpl))
|
||||
errx(1, "identify request returned error");
|
||||
if (clist[0] == 0) {
|
||||
ctrlrid = cd.ctrlr_id;
|
||||
detach_opt.ctrlrid = cd.ctrlr_id;
|
||||
memset(&clist, 0, sizeof(clist));
|
||||
clist[0] = htole16(1);
|
||||
clist[1] = htole16(ctrlrid);
|
||||
clist[1] = htole16(detach_opt.ctrlrid);
|
||||
}
|
||||
} else {
|
||||
/* By default detach from this controller. */
|
||||
if (ctrlrid == -2)
|
||||
ctrlrid = cd.ctrlr_id;
|
||||
if (detach_opt.ctrlrid == NONE - 1)
|
||||
detach_opt.ctrlrid = cd.ctrlr_id;
|
||||
memset(&clist, 0, sizeof(clist));
|
||||
clist[0] = htole16(1);
|
||||
clist[1] = htole16(ctrlrid);
|
||||
clist[1] = htole16(detach_opt.ctrlrid);
|
||||
}
|
||||
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT;
|
||||
pt.cmd.cdw10 = 1; /* detach */
|
||||
pt.cmd.nsid = (uint32_t)nsid;
|
||||
pt.cmd.nsid = detach_opt.nsid;
|
||||
pt.buf = &clist;
|
||||
pt.len = sizeof(clist);
|
||||
|
||||
@ -432,15 +526,13 @@ nsdetach(const struct nvme_function *nf, int argc, char *argv[])
|
||||
get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) &
|
||||
NVME_STATUS_SC_MASK));
|
||||
}
|
||||
printf("namespace %d detached\n", nsid);
|
||||
printf("namespace %d detached\n", detach_opt.nsid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
ns(const struct nvme_function *nf __unused, int argc, char *argv[])
|
||||
ns(const struct cmd *nf __unused, int argc, char *argv[])
|
||||
{
|
||||
|
||||
DISPATCH(argc, argv, ns);
|
||||
cmd_dispatch(argc, argv, &ns_cmd);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, ns, ns, NS_USAGE);
|
||||
|
@ -35,7 +35,6 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@ -49,101 +48,6 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
SET_CONCAT_DEF(top, struct nvme_function);
|
||||
|
||||
static void
|
||||
print_usage(const struct nvme_function *f)
|
||||
{
|
||||
const char *cp;
|
||||
char ch;
|
||||
bool need_prefix = true;
|
||||
|
||||
cp = f->usage;
|
||||
while (*cp) {
|
||||
ch = *cp++;
|
||||
if (need_prefix) {
|
||||
if (ch != ' ')
|
||||
fputs(" nvmecontrol ", stderr);
|
||||
else
|
||||
fputs(" ", stderr);
|
||||
}
|
||||
fputc(ch, stderr);
|
||||
need_prefix = (ch == '\n');
|
||||
}
|
||||
if (!need_prefix)
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
static void
|
||||
gen_usage_set(const struct nvme_function * const *f, const struct nvme_function * const *flimit)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage:\n");
|
||||
while (f < flimit) {
|
||||
print_usage(*f);
|
||||
f++;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
usage(const struct nvme_function *f)
|
||||
{
|
||||
|
||||
fprintf(stderr, "usage:\n");
|
||||
print_usage(f);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
dispatch_set(int argc, char *argv[], const struct nvme_function * const *tbl,
|
||||
const struct nvme_function * const *tbl_limit)
|
||||
{
|
||||
const struct nvme_function * const *f = tbl;
|
||||
|
||||
if (argv[1] == NULL) {
|
||||
gen_usage_set(tbl, tbl_limit);
|
||||
return;
|
||||
}
|
||||
|
||||
while (f < tbl_limit) {
|
||||
if (strcmp(argv[1], (*f)->name) == 0) {
|
||||
(*f)->fn(*f, argc-1, &argv[1]);
|
||||
return;
|
||||
}
|
||||
f++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown command: %s\n", argv[1]);
|
||||
gen_usage_set(tbl, tbl_limit);
|
||||
}
|
||||
|
||||
void
|
||||
set_concat_add(struct set_concat *m, void *b, void *e)
|
||||
{
|
||||
void **bp, **ep;
|
||||
int add_n, cur_n;
|
||||
|
||||
if (b == NULL)
|
||||
return;
|
||||
/*
|
||||
* Args are really pointers to arrays of pointers, but C's
|
||||
* casting rules kinda suck since you can't directly cast
|
||||
* struct foo ** to a void **.
|
||||
*/
|
||||
bp = (void **)b;
|
||||
ep = (void **)e;
|
||||
add_n = ep - bp;
|
||||
cur_n = 0;
|
||||
if (m->begin != NULL)
|
||||
cur_n = m->limit - m->begin;
|
||||
m->begin = reallocarray(m->begin, cur_n + add_n, sizeof(void *));
|
||||
if (m->begin == NULL)
|
||||
err(1, "expanding concat set");
|
||||
memcpy(m->begin + cur_n, bp, add_n * sizeof(void *));
|
||||
m->limit = m->begin + cur_n + add_n;
|
||||
}
|
||||
|
||||
static void
|
||||
print_bytes(void *data, uint32_t length)
|
||||
{
|
||||
@ -288,61 +192,16 @@ parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid)
|
||||
snprintf(ctrlr_str, nsloc - ns_str + 1, "%s", ns_str);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads all the .so's from the specified directory.
|
||||
*/
|
||||
static void
|
||||
load_dir(const char *dir)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *dent;
|
||||
char *path = NULL;
|
||||
void *h;
|
||||
|
||||
d = opendir(dir);
|
||||
if (d == NULL)
|
||||
return;
|
||||
for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
|
||||
if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0)
|
||||
continue;
|
||||
asprintf(&path, "%s/%s", dir, dent->d_name);
|
||||
if (path == NULL)
|
||||
err(1, "Can't malloc for path, giving up.");
|
||||
if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL)
|
||||
warnx("Can't load %s: %s", path, dlerror());
|
||||
else {
|
||||
/*
|
||||
* Add in the top (for cli commands)linker sets. We have
|
||||
* to do this by hand because linker sets aren't
|
||||
* automatically merged.
|
||||
*/
|
||||
void *begin, *limit;
|
||||
|
||||
begin = dlsym(h, "__start_set_top");
|
||||
limit = dlsym(h, "__stop_set_top");
|
||||
if (begin)
|
||||
add_to_top(begin, limit);
|
||||
/* log pages use constructors so are done on load */
|
||||
}
|
||||
free(path);
|
||||
path = NULL;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
add_to_top(NVME_CMD_BEGIN(top), NVME_CMD_LIMIT(top));
|
||||
cmd_init();
|
||||
|
||||
load_dir("/lib/nvmecontrol");
|
||||
load_dir("/usr/local/lib/nvmecontrol");
|
||||
cmd_load_dir("/lib/nvmecontrol", NULL, NULL);
|
||||
cmd_load_dir("/usr/local/lib/nvmecontrol", NULL, NULL);
|
||||
|
||||
if (argc < 2)
|
||||
gen_usage_set(top_begin(), top_limit());
|
||||
|
||||
dispatch_set(argc, argv, top_begin(), top_limit());
|
||||
cmd_dispatch(argc, argv, NULL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -31,28 +31,8 @@
|
||||
#ifndef __NVMECONTROL_H__
|
||||
#define __NVMECONTROL_H__
|
||||
|
||||
#include <sys/linker_set.h>
|
||||
#include <sys/queue.h>
|
||||
#include <dev/nvme/nvme.h>
|
||||
|
||||
struct nvme_function;
|
||||
typedef void (*nvme_fn_t)(const struct nvme_function *nf, int argc, char *argv[]);
|
||||
|
||||
struct nvme_function {
|
||||
const char *name;
|
||||
nvme_fn_t fn;
|
||||
const char *usage;
|
||||
};
|
||||
|
||||
#define NVME_SETNAME(set) set
|
||||
#define NVME_CMDSET(set, sym) DATA_SET(NVME_SETNAME(set), sym)
|
||||
#define NVME_COMMAND(set, nam, function, usage_str) \
|
||||
static struct nvme_function function ## _nvme_cmd = \
|
||||
{ .name = #nam, .fn = function, .usage = usage_str }; \
|
||||
NVME_CMDSET(set, function ## _nvme_cmd)
|
||||
#define NVME_CMD_BEGIN(set) SET_BEGIN(NVME_SETNAME(set))
|
||||
#define NVME_CMD_LIMIT(set) SET_LIMIT(NVME_SETNAME(set))
|
||||
#define NVME_CMD_DECLARE(set, t) SET_DECLARE(NVME_SETNAME(set), t)
|
||||
#include "comnd.h"
|
||||
|
||||
typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
|
||||
|
||||
@ -65,7 +45,6 @@ struct logpage_function {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
|
||||
#define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \
|
||||
static struct logpage_function unique ## _lpf = { \
|
||||
.log_page = lp, \
|
||||
@ -85,26 +64,7 @@ struct kv_name {
|
||||
|
||||
const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key);
|
||||
|
||||
NVME_CMD_DECLARE(top, struct nvme_function);
|
||||
void logpage_register(struct logpage_function *p);
|
||||
|
||||
struct set_concat {
|
||||
void **begin;
|
||||
void **limit;
|
||||
};
|
||||
void set_concat_add(struct set_concat *m, void *begin, void *end);
|
||||
#define SET_CONCAT_DEF(set, t) \
|
||||
static struct set_concat set ## _concat; \
|
||||
static inline const t * const *set ## _begin(void) { return ((const t * const *)set ## _concat.begin); } \
|
||||
static inline const t * const *set ## _limit(void) { return ((const t * const *)set ## _concat.limit); } \
|
||||
void add_to_ ## set(t **b, t **e) \
|
||||
{ \
|
||||
set_concat_add(&set ## _concat, b, e); \
|
||||
}
|
||||
#define SET_CONCAT_DECL(set, t) \
|
||||
void add_to_ ## set(t **b, t **e)
|
||||
SET_CONCAT_DECL(top, struct nvme_function);
|
||||
|
||||
#define NVME_CTRLR_PREFIX "nvme"
|
||||
#define NVME_NS_PREFIX "ns"
|
||||
|
||||
@ -118,15 +78,6 @@ void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
|
||||
void print_temp(uint16_t t);
|
||||
void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused);
|
||||
|
||||
void usage(const struct nvme_function *f);
|
||||
void dispatch_set(int argc, char *argv[], const struct nvme_function * const *tbl,
|
||||
const struct nvme_function * const *tbl_limit);
|
||||
|
||||
#define DISPATCH(argc, argv, set) \
|
||||
dispatch_set(argc, argv, \
|
||||
(const struct nvme_function * const *)NVME_CMD_BEGIN(set), \
|
||||
(const struct nvme_function * const *)NVME_CMD_LIMIT(set)) \
|
||||
|
||||
/* Utility Routines */
|
||||
/*
|
||||
* 128-bit integer augments to standard values. On i386 this
|
||||
@ -149,5 +100,4 @@ to128(void *p)
|
||||
|
||||
uint64_t le48dec(const void *pp);
|
||||
char * uint128_to_str(uint128_t u, char *buf, size_t buflen);
|
||||
|
||||
#endif
|
||||
|
@ -45,11 +45,69 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
#define PERFTEST_USAGE \
|
||||
"perftest <-n num_threads> <-o read|write>\n" \
|
||||
" <-s size_in_bytes> <-t time_in_seconds>\n" \
|
||||
" <-i intr|wait> [-f refthread] [-p]\n" \
|
||||
" <namespace id>\n"
|
||||
/* Tables for command line parsing */
|
||||
|
||||
static cmd_fn_t perftest;
|
||||
|
||||
#define NONE 0xffffffffu
|
||||
static struct options {
|
||||
bool perthread;
|
||||
uint32_t threads;
|
||||
uint32_t size;
|
||||
uint32_t time;
|
||||
const char *op;
|
||||
const char *intr;
|
||||
const char *flags;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.perthread = false,
|
||||
.threads = 0,
|
||||
.size = 0,
|
||||
.time = 0,
|
||||
.op = NULL,
|
||||
.intr = NULL,
|
||||
.flags = NULL,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
|
||||
static const struct opts perftest_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("perthread", 'p', arg_none, opt, perthread,
|
||||
"Report per-thread results"),
|
||||
OPT("threads", 'n', arg_uint32, opt, threads,
|
||||
"Number of threads to run"),
|
||||
OPT("size", 's', arg_uint32, opt, size,
|
||||
"Size of the test"),
|
||||
OPT("time", 't', arg_uint32, opt, time,
|
||||
"How long to run the test in seconds"),
|
||||
OPT("operation", 'o', arg_string, opt, op,
|
||||
"Operation type: 'read' or 'write'"),
|
||||
OPT("interrupt", 'i', arg_string, opt, intr,
|
||||
"Interrupt mode: 'intr' or 'wait'"),
|
||||
OPT("flags", 'f', arg_string, opt, flags,
|
||||
"Turn on testing flags: refthread"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args perftest_args[] = {
|
||||
{ arg_string, &opt.dev, "namespace-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd perftest_cmd = {
|
||||
.name = "perftest",
|
||||
.fn = perftest,
|
||||
.descr = "Perform low-level driver performance testing.",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = perftest_opts,
|
||||
.args = perftest_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(perftest_cmd);
|
||||
|
||||
/* End of tables for command line parsing */
|
||||
|
||||
static void
|
||||
print_perftest(struct nvme_io_test *io_test, bool perthread)
|
||||
@ -75,105 +133,54 @@ print_perftest(struct nvme_io_test *io_test, bool perthread)
|
||||
}
|
||||
|
||||
static void
|
||||
perftest(const struct nvme_function *nf, int argc, char *argv[])
|
||||
perftest(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_io_test io_test;
|
||||
int fd;
|
||||
int opt;
|
||||
char *p;
|
||||
u_long ioctl_cmd = NVME_IO_TEST;
|
||||
bool nflag, oflag, sflag, tflag;
|
||||
int perthread = 0;
|
||||
|
||||
nflag = oflag = sflag = tflag = false;
|
||||
|
||||
memset(&io_test, 0, sizeof(io_test));
|
||||
|
||||
while ((opt = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
if (!strcmp(optarg, "refthread"))
|
||||
io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
|
||||
break;
|
||||
case 'i':
|
||||
if (!strcmp(optarg, "bio") ||
|
||||
!strcmp(optarg, "wait"))
|
||||
ioctl_cmd = NVME_BIO_TEST;
|
||||
else if (!strcmp(optarg, "io") ||
|
||||
!strcmp(optarg, "intr"))
|
||||
ioctl_cmd = NVME_IO_TEST;
|
||||
break;
|
||||
case 'n':
|
||||
nflag = true;
|
||||
io_test.num_threads = strtoul(optarg, &p, 0);
|
||||
if (p != NULL && *p != '\0') {
|
||||
fprintf(stderr,
|
||||
"\"%s\" not valid number of threads.\n",
|
||||
optarg);
|
||||
usage(nf);
|
||||
} else if (io_test.num_threads == 0 ||
|
||||
io_test.num_threads > 128) {
|
||||
fprintf(stderr,
|
||||
"\"%s\" not valid number of threads.\n",
|
||||
optarg);
|
||||
usage(nf);
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
oflag = true;
|
||||
if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
|
||||
io_test.opc = NVME_OPC_READ;
|
||||
else if (!strcmp(optarg, "write") ||
|
||||
!strcmp(optarg, "WRITE"))
|
||||
io_test.opc = NVME_OPC_WRITE;
|
||||
else {
|
||||
fprintf(stderr, "\"%s\" not valid opcode.\n",
|
||||
optarg);
|
||||
usage(nf);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
perthread = 1;
|
||||
break;
|
||||
case 's':
|
||||
sflag = true;
|
||||
io_test.size = strtoul(optarg, &p, 0);
|
||||
if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
|
||||
// do nothing
|
||||
} else if (toupper(*p) == 'K') {
|
||||
io_test.size *= 1024;
|
||||
} else if (toupper(*p) == 'M') {
|
||||
io_test.size *= 1024 * 1024;
|
||||
} else {
|
||||
fprintf(stderr, "\"%s\" not valid size.\n",
|
||||
optarg);
|
||||
usage(nf);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
tflag = true;
|
||||
io_test.time = strtoul(optarg, &p, 0);
|
||||
if (p != NULL && *p != '\0') {
|
||||
fprintf(stderr,
|
||||
"\"%s\" not valid time duration.\n",
|
||||
optarg);
|
||||
usage(nf);
|
||||
}
|
||||
break;
|
||||
if (arg_parse(argc, argv, f))
|
||||
return;
|
||||
|
||||
if (opt.flags == NULL || opt.op == NULL)
|
||||
arg_help(argc, argv, f);
|
||||
if (strcmp(opt.flags, "refthread") == 0)
|
||||
io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
|
||||
if (opt.intr != NULL) {
|
||||
if (strcmp(opt.intr, "bio") == 0 ||
|
||||
strcmp(opt.intr, "wait") == 0)
|
||||
ioctl_cmd = NVME_BIO_TEST;
|
||||
else if (strcmp(opt.intr, "io") == 0 ||
|
||||
strcmp(opt.intr, "intr") == 0)
|
||||
ioctl_cmd = NVME_IO_TEST;
|
||||
else {
|
||||
fprintf(stderr, "Unknown interrupt test type %s\n", opt.intr);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nflag || !oflag || !sflag || !tflag || optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
if (opt.threads <= 0 || opt.threads > 128) {
|
||||
fprintf(stderr, "Bad number of threads %d\n", opt.threads);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
if (strcasecmp(opt.op, "read") == 0)
|
||||
io_test.opc = NVME_OPC_READ;
|
||||
else if (strcasecmp(opt.op, "write") == 0)
|
||||
io_test.opc = NVME_OPC_WRITE;
|
||||
else {
|
||||
fprintf(stderr, "\"%s\" not valid opcode.\n", opt.op);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
if (opt.time == 0) {
|
||||
fprintf(stderr, "No time speciifed\n");
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
io_test.time = opt.time;
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
if (ioctl(fd, ioctl_cmd, &io_test) < 0)
|
||||
err(1, "ioctl NVME_IO_TEST failed");
|
||||
|
||||
close(fd);
|
||||
print_perftest(&io_test, perthread);
|
||||
print_perftest(&io_test, opt.perthread);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, perftest, perftest, PERFTEST_USAGE);
|
||||
|
@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -43,8 +44,19 @@ __FBSDID("$FreeBSD$");
|
||||
_Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY,
|
||||
"nvme_power_state size wrong");
|
||||
|
||||
#define POWER_USAGE \
|
||||
"power [-l] [-p new-state [-w workload-hint]] <controller id>\n"
|
||||
#define POWER_NONE 0xffffffffu
|
||||
|
||||
static struct options {
|
||||
bool list;
|
||||
uint32_t power;
|
||||
uint32_t workload;
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.list = false,
|
||||
.power = POWER_NONE,
|
||||
.workload = 0,
|
||||
.dev = NULL,
|
||||
};
|
||||
|
||||
static void
|
||||
power_list_one(int i, struct nvme_power_state *nps)
|
||||
@ -128,57 +140,28 @@ power_show(int fd)
|
||||
}
|
||||
|
||||
static void
|
||||
power(const struct nvme_function *nf, int argc, char *argv[])
|
||||
power(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
struct nvme_controller_data cdata;
|
||||
int ch, listflag = 0, powerflag = 0, power_val = 0, fd;
|
||||
int workload = 0;
|
||||
char *end;
|
||||
int fd;
|
||||
|
||||
while ((ch = getopt(argc, argv, "lp:w:")) != -1) {
|
||||
switch ((char)ch) {
|
||||
case 'l':
|
||||
listflag = 1;
|
||||
break;
|
||||
case 'p':
|
||||
powerflag = 1;
|
||||
power_val = strtol(optarg, &end, 0);
|
||||
if (*end != '\0') {
|
||||
fprintf(stderr, "Invalid power state number: %s\n", optarg);
|
||||
usage(nf);
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
workload = strtol(optarg, &end, 0);
|
||||
if (*end != '\0') {
|
||||
fprintf(stderr, "Invalid workload hint: %s\n", optarg);
|
||||
usage(nf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
}
|
||||
arg_parse(argc, argv, f);
|
||||
|
||||
/* Check that a controller was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
if (listflag && powerflag) {
|
||||
if (opt.list && opt.power != POWER_NONE) {
|
||||
fprintf(stderr, "Can't set power and list power states\n");
|
||||
usage(nf);
|
||||
arg_help(argc, argv, f);
|
||||
}
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
read_controller_data(fd, &cdata);
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
|
||||
if (listflag) {
|
||||
if (opt.list) {
|
||||
read_controller_data(fd, &cdata);
|
||||
power_list(&cdata);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (powerflag) {
|
||||
power_set(fd, power_val, workload, 0);
|
||||
if (opt.power != POWER_NONE) {
|
||||
power_set(fd, opt.power, opt.workload, 0);
|
||||
goto out;
|
||||
}
|
||||
power_show(fd);
|
||||
@ -188,4 +171,30 @@ power(const struct nvme_function *nf, int argc, char *argv[])
|
||||
exit(0);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, power, power, POWER_USAGE);
|
||||
static const struct opts power_opts[] = {
|
||||
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
|
||||
OPT("list", 'l', arg_none, opt, list,
|
||||
"List the valid power states"),
|
||||
OPT("power", 'p', arg_uint32, opt, power,
|
||||
"Set the power state"),
|
||||
OPT("workload", 'w', arg_uint32, opt, workload,
|
||||
"Set the workload"),
|
||||
{ NULL, 0, arg_none, NULL, NULL }
|
||||
};
|
||||
#undef OPT
|
||||
|
||||
static const struct args power_args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd power_cmd = {
|
||||
.name = "power",
|
||||
.fn = power,
|
||||
.descr = "Manage power states for the drive",
|
||||
.ctx_size = sizeof(opt),
|
||||
.opts = power_opts,
|
||||
.args = power_args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(power_cmd);
|
||||
|
@ -41,30 +41,36 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "nvmecontrol.h"
|
||||
|
||||
#define RESET_USAGE \
|
||||
"reset <controller id>\n"
|
||||
static struct options {
|
||||
const char *dev;
|
||||
} opt = {
|
||||
.dev = NULL
|
||||
};
|
||||
|
||||
static const struct args args[] = {
|
||||
{ arg_string, &opt.dev, "controller-id" },
|
||||
{ arg_none, NULL, NULL },
|
||||
};
|
||||
|
||||
static void
|
||||
reset(const struct nvme_function *nf, int argc, char *argv[])
|
||||
reset(const struct cmd *f, int argc, char *argv[])
|
||||
{
|
||||
int ch, fd;
|
||||
int fd;
|
||||
|
||||
while ((ch = getopt(argc, argv, "")) != -1) {
|
||||
switch ((char)ch) {
|
||||
default:
|
||||
usage(nf);
|
||||
}
|
||||
}
|
||||
arg_parse(argc, argv, f);
|
||||
open_dev(opt.dev, &fd, 1, 1);
|
||||
|
||||
/* Check that a controller was specified. */
|
||||
if (optind >= argc)
|
||||
usage(nf);
|
||||
|
||||
open_dev(argv[optind], &fd, 1, 1);
|
||||
if (ioctl(fd, NVME_RESET_CONTROLLER) < 0)
|
||||
err(1, "reset request to %s failed", argv[optind]);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
NVME_COMMAND(top, reset, reset, RESET_USAGE);
|
||||
static struct cmd reset_cmd = {
|
||||
.name = "reset",
|
||||
.fn = reset,
|
||||
.descr = "Perform a controller-level reset.",
|
||||
.args = args,
|
||||
};
|
||||
|
||||
CMD_COMMAND(reset_cmd);
|
||||
|
Loading…
Reference in New Issue
Block a user