Page MenuHomeFreeBSD

D19296.id.diff
No OneTemporary

D19296.id.diff

Index: head/sbin/nvmecontrol/Makefile
===================================================================
--- head/sbin/nvmecontrol/Makefile
+++ head/sbin/nvmecontrol/Makefile
@@ -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
Index: head/sbin/nvmecontrol/comnd.h
===================================================================
--- head/sbin/nvmecontrol/comnd.h
+++ head/sbin/nvmecontrol/comnd.h
@@ -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 */
Index: head/sbin/nvmecontrol/comnd.c
===================================================================
--- head/sbin/nvmecontrol/comnd.c
+++ head/sbin/nvmecontrol/comnd.c
@@ -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 = &top;
+
+ 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 = &top;
+ 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)
+{
+
+}
Index: head/sbin/nvmecontrol/devlist.c
===================================================================
--- head/sbin/nvmecontrol/devlist.c
+++ head/sbin/nvmecontrol/devlist.c
@@ -42,12 +42,24 @@
#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 @@
}
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 @@
exit(1);
}
-
-NVME_COMMAND(top, devlist, devlist, DEVLIST_USAGE);
Index: head/sbin/nvmecontrol/firmware.c
===================================================================
--- head/sbin/nvmecontrol/firmware.c
+++ head/sbin/nvmecontrol/firmware.c
@@ -50,9 +50,53 @@
#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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 @@
"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);
Index: head/sbin/nvmecontrol/format.c
===================================================================
--- head/sbin/nvmecontrol/format.c
+++ head/sbin/nvmecontrol/format.c
@@ -35,6 +35,7 @@
#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 @@
#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 @@
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 @@
& 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 @@
/* 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 @@
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 @@
close(fd);
exit(0);
}
-
-NVME_COMMAND(top, format, format, FORMAT_USAGE);
Index: head/sbin/nvmecontrol/identify.c
===================================================================
--- head/sbin/nvmecontrol/identify.c
+++ head/sbin/nvmecontrol/identify.c
@@ -34,6 +34,7 @@
#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 @@
#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 @@
}
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 @@
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 @@
}
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 @@
* 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 @@
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 @@
}
static void
-identify(const struct nvme_function *nf, int argc, char *argv[])
+identify(const struct cmd *f, int argc, char *argv[])
{
- char *target;
+ arg_parse(argc, argv, f);
- 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;
-
/*
* 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);
Index: head/sbin/nvmecontrol/logpage.c
===================================================================
--- head/sbin/nvmecontrol/logpage.c
+++ head/sbin/nvmecontrol/logpage.c
@@ -48,9 +48,57 @@
#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)
static SLIST_HEAD(,logpage_function) logpages;
@@ -348,69 +396,40 @@
}
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 @@
* 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 @@
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 @@
* 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);
Index: head/sbin/nvmecontrol/modules/wdc/wdc.c
===================================================================
--- head/sbin/nvmecontrol/modules/wdc/wdc.c
+++ head/sbin/nvmecontrol/modules/wdc/wdc.c
@@ -41,22 +41,58 @@
#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 WDC_NVME_TOC_SIZE 8
+#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 }
-#define WDC_NVME_CAP_DIAG_OPCODE 0xe6
-#define WDC_NVME_CAP_DIAG_CMD 0x0000
+static struct cmd wdc_cmd = {
+ .name = "wdc", .fn = wdc, .descr = "wdc vendor specific commands", .ctx_size = 0, .opts = NULL, .args = NULL,
+};
-static void wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]);
+CMD_COMMAND(wdc_cmd);
-#define WDC_CAP_DIAG_USAGE "wdc cap-diag [-o path-template]\n"
+static struct options
+{
+ const char *template;
+ const char *dev;
+} opt = {
+ .template = NULL,
+ .dev = NULL,
+};
-NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE);
+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_append_serial_name(int fd, char *buf, size_t len, const char *suffix)
{
@@ -153,27 +189,20 @@
}
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 @@
}
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(wdc_info,
HGST_INFO_LOG, "wdc", "Detailed Health/SMART",
print_hgst_info_log, DEFAULT_SIZE);
-NVME_COMMAND(top, wdc, wdc, WDC_USAGE);
Index: head/sbin/nvmecontrol/ns.c
===================================================================
--- head/sbin/nvmecontrol/ns.c
+++ head/sbin/nvmecontrol/ns.c
@@ -41,35 +41,194 @@
#include "nvmecontrol.h"
-NVME_CMD_DECLARE(ns, struct nvme_function);
+/* Tables for command line parsing */
-#define NS_USAGE \
- "ns (create|delete|attach|detach)\n"
+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;
-/* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */
+#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 }
-#define NSCREATE_USAGE \
- "ns create -s size [-c cap] [-f fmt] [-m mset] [-n nmic] [-p pi] [-l pil] nvmeN\n"
+static struct cmd ns_cmd = {
+ .name = "ns", .fn = ns, .descr = "Namespace commands", .ctx_size = 0, .opts = NULL, .args = NULL,
+};
-#define NSDELETE_USAGE \
- "ns delete -n nsid nvmeN\n"
+CMD_COMMAND(ns_cmd);
-#define NSATTACH_USAGE \
- "ns attach -n nsid [-c ctrlrid] nvmeN \n"
+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,
+};
-#define NSDETACH_USAGE \
- "ns detach -n nsid [-c ctrlrid] nvmeN\n"
+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 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[]);
+static const struct args create_args[] = {
+ { arg_string, &create_opt.dev, "controller-id" },
+ { arg_none, NULL, NULL },
+};
-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);
+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 */
+
struct ns_result_str {
uint16_t res;
const char * str;
@@ -110,54 +269,25 @@
* 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 @@
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 @@
}
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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);
Index: head/sbin/nvmecontrol/nvmecontrol.h
===================================================================
--- head/sbin/nvmecontrol/nvmecontrol.h
+++ head/sbin/nvmecontrol/nvmecontrol.h
@@ -31,29 +31,9 @@
#ifndef __NVMECONTROL_H__
#define __NVMECONTROL_H__
-#include <sys/linker_set.h>
-#include <sys/queue.h>
#include <dev/nvme/nvme.h>
+#include "comnd.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)
-
typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
struct logpage_function {
@@ -65,7 +45,6 @@
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 @@
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 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 @@
uint64_t le48dec(const void *pp);
char * uint128_to_str(uint128_t u, char *buf, size_t buflen);
-
#endif
Index: head/sbin/nvmecontrol/nvmecontrol.c
===================================================================
--- head/sbin/nvmecontrol/nvmecontrol.c
+++ head/sbin/nvmecontrol/nvmecontrol.c
@@ -35,7 +35,6 @@
#include <ctype.h>
#include <dlfcn.h>
-#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -49,102 +48,7 @@
#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)
{
uint32_t i, j;
@@ -288,61 +192,16 @@
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);
}
Index: head/sbin/nvmecontrol/perftest.c
===================================================================
--- head/sbin/nvmecontrol/perftest.c
+++ head/sbin/nvmecontrol/perftest.c
@@ -45,12 +45,70 @@
#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 @@
}
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);
Index: head/sbin/nvmecontrol/power.c
===================================================================
--- head/sbin/nvmecontrol/power.c
+++ head/sbin/nvmecontrol/power.c
@@ -32,6 +32,7 @@
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -43,9 +44,20 @@
_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 @@
}
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 @@
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);
Index: head/sbin/nvmecontrol/reset.c
===================================================================
--- head/sbin/nvmecontrol/reset.c
+++ head/sbin/nvmecontrol/reset.c
@@ -41,30 +41,36 @@
#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);

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 21, 8:20 PM (7 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25796553
Default Alt Text
D19296.id.diff (72 KB)

Event Timeline