Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F136957997
D19296.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
72 KB
Referenced Files
None
Subscribers
None
D19296.id.diff
View Options
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 = ⊤
+
+ 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)
+{
+
+}
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
Details
Attached
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)
Attached To
Mode
D19296: Generic cmd parsing
Attached
Detach File
Event Timeline
Log In to Comment