Index: stable/12/sbin/nvmecontrol/Makefile =================================================================== --- stable/12/sbin/nvmecontrol/Makefile (revision 350928) +++ stable/12/sbin/nvmecontrol/Makefile (revision 350929) @@ -1,14 +1,17 @@ # $FreeBSD$ 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 .include .include Index: stable/12/sbin/nvmecontrol/comnd.c =================================================================== --- stable/12/sbin/nvmecontrol/comnd.c (nonexistent) +++ stable/12/sbin/nvmecontrol/comnd.c (revision 350929) @@ -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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "=", len); + break; + case arg_path: + strlcat(buf, "=", len); + break; + default: + strlcat(buf, "=", 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, " "); + 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) +{ + +} Property changes on: stable/12/sbin/nvmecontrol/comnd.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/12/sbin/nvmecontrol/comnd.h =================================================================== --- stable/12/sbin/nvmecontrol/comnd.h (nonexistent) +++ stable/12/sbin/nvmecontrol/comnd.h (revision 350929) @@ -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 +#include + +/* + * 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 */ Property changes on: stable/12/sbin/nvmecontrol/comnd.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/12/sbin/nvmecontrol/devlist.c =================================================================== --- stable/12/sbin/nvmecontrol/devlist.c (revision 350928) +++ stable/12/sbin/nvmecontrol/devlist.c (revision 350929) @@ -1,123 +1,129 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #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) { uint8_t flbas_fmt, lbads; flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & NVME_NS_DATA_FLBAS_FORMAT_MASK; lbads = (nsdata->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & NVME_NS_DATA_LBAF_LBADS_MASK; return (1 << lbads); } 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; while (ctrlr < NVME_MAX_UNIT) { ctrlr++; sprintf(name, "%s%d", NVME_CTRLR_PREFIX, ctrlr); ret = open_dev(name, &fd, 0, 0); if (ret == EACCES) { warnx("could not open "_PATH_DEV"%s\n", name); continue; } else if (ret != 0) continue; found++; read_controller_data(fd, &cdata); nvme_strvis(mn, cdata.mn, sizeof(mn), NVME_MODEL_NUMBER_LENGTH); printf("%6s: %s\n", name, mn); for (i = 0; i < cdata.nn; i++) { sprintf(name, "%s%d%s%d", NVME_CTRLR_PREFIX, ctrlr, NVME_NS_PREFIX, i+1); read_namespace_data(fd, i+1, &nsdata); if (nsdata.nsze == 0) continue; printf(" %10s (%lldMB)\n", name, nsdata.nsze * (long long)ns_get_sector_size(&nsdata) / 1024 / 1024); } close(fd); } if (found == 0) printf("No NVMe controllers found.\n"); exit(1); } - -NVME_COMMAND(top, devlist, devlist, DEVLIST_USAGE); Index: stable/12/sbin/nvmecontrol/firmware.c =================================================================== --- stable/12/sbin/nvmecontrol/firmware.c (revision 350928) +++ stable/12/sbin/nvmecontrol/firmware.c (revision 350929) @@ -1,336 +1,357 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" -#define FIRMWARE_USAGE \ - "firmware [-s slot] [-f path_to_firmware] [-a] \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) { struct nvme_firmware_page fw; int has_fw = false; read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, NVME_GLOBAL_NAMESPACE_TAG, &fw, sizeof(fw)); if (fw.revision[slot-1] != 0LLU) has_fw = true; return (has_fw); } 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; int fd; *size = 0; *buf = NULL; if ((fd = open(path, O_RDONLY)) < 0) err(1, "unable to open '%s'", path); if (fstat(fd, &sb) < 0) err(1, "unable to stat '%s'", path); /* * The NVMe spec does not explicitly state a maximum firmware image * size, although one can be inferred from the dword size limitation * for the size and offset fields in the Firmware Image Download * command. * * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the * size and offsets are specified in terms of dwords (not bytes), but * realistically INT32_MAX is sufficient here and simplifies matters * a bit. */ if (sb.st_size > INT32_MAX) errx(1, "size of file '%s' is too large (%jd bytes)", path, (intmax_t)sb.st_size); filesize = (int32_t)sb.st_size; if ((*buf = malloc(filesize)) == NULL) errx(1, "unable to malloc %d bytes", filesize); if ((*size = read(fd, *buf, filesize)) < 0) err(1, "error reading '%s'", path); /* XXX assuming no short reads */ if (*size != filesize) errx(1, "error reading '%s' (read %d bytes, requested %d bytes)", path, *size, filesize); } static void update_firmware(int fd, uint8_t *payload, int32_t payload_size) { struct nvme_pt_command pt; int32_t off, resid, size; void *chunk; off = 0; resid = payload_size; if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL) errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); while (resid > 0) { size = (resid >= NVME_MAX_XFER_SIZE) ? NVME_MAX_XFER_SIZE : resid; memcpy(chunk, payload + off, size); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD; pt.cmd.cdw10 = htole32((size / sizeof(uint32_t)) - 1); pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); pt.buf = chunk; pt.len = size; pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "firmware download request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "firmware download request returned error"); resid -= size; off += size; } } static int activate_firmware(int fd, int slot, int activate_action) { struct nvme_pt_command pt; uint16_t sct, sc; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE; pt.cmd.cdw10 = htole32((activate_action << 3) | slot); pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "firmware activate request failed"); sct = NVME_STATUS_GET_SCT(pt.cpl.status); sc = NVME_STATUS_GET_SC(pt.cpl.status); if (sct == NVME_SCT_COMMAND_SPECIFIC && sc == NVME_SC_FIRMWARE_REQUIRES_RESET) return 1; if (nvme_completion_is_error(&pt.cpl)) errx(1, "firmware activate request returned error"); return 0; } 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) & NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; if (oacs_fw == 0) errx(1, "controller does not support firmware activate/download"); 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) { fgets(prompt, sizeof(prompt), stdin); if (strncasecmp(prompt, "yes", 3) == 0) break; if (strncasecmp(prompt, "no", 2) == 0) exit(1); 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; } else { 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 " "complete activation.\n"); } else { printf("New firmware image activated and will take " "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: stable/12/sbin/nvmecontrol/format.c =================================================================== --- stable/12/sbin/nvmecontrol/format.c (revision 350928) +++ stable/12/sbin/nvmecontrol/format.c (revision 350929) @@ -1,184 +1,230 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2018 Alexander Motin * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include +#include #include #include #include #include #include #include "nvmecontrol.h" -#define FORMAT_USAGE \ - "format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] \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. * This is a cleaner check for cases where the correct controller * is specified, but an invalid namespace on that controller. */ open_dev(target, &fd, 1, 1); /* * If device node contains "ns", we consider it a namespace, * otherwise, consider it a controller. */ if (strstr(target, NVME_NS_PREFIX) == NULL) { nsid = NVME_GLOBAL_NAMESPACE_TAG; } else { /* * We send FORMAT commands to the controller, not the namespace, * since it is an admin cmd. The namespace ID will be specified * in the command itself. So parse the namespace's device node * string to get the controller substring and namespace ID. */ close(fd); parse_ns_str(target, path, &nsid); open_dev(path, &fd, 1, 1); } /* Check that controller can execute this command. */ read_controller_data(fd, &cd); if (((cd.oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) & 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. */ read_namespace_data(fd, nsid, &nsd); if (lbaf < 0) lbaf = (nsd.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & 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) & NVME_NS_DATA_DPS_MD_START_MASK; if (pil < 0) pil = (nsd.dps >> NVME_NS_DATA_DPS_PIT_SHIFT) & NVME_NS_DATA_DPS_PIT_MASK; } else { /* 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) pil = 0; } memset(&pt, 0, sizeof(pt)); 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"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "format request returned error"); close(fd); exit(0); } - -NVME_COMMAND(top, format, format, FORMAT_USAGE); Index: stable/12/sbin/nvmecontrol/identify.c =================================================================== --- stable/12/sbin/nvmecontrol/identify.c (revision 350928) +++ stable/12/sbin/nvmecontrol/identify.c (revision 350929) @@ -1,302 +1,284 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include +#include #include #include #include #include #include #include "nvmecontrol.h" #include "nvmecontrol_ext.h" -#define IDENTIFY_USAGE \ - "identify [-x [-v]] \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) { uint32_t i; uint32_t lbaf, lbads, ms, rp; uint8_t thin_prov, ptype; uint8_t flbas_fmt, t; thin_prov = (nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_THIN_PROV_SHIFT) & NVME_NS_DATA_NSFEAT_THIN_PROV_MASK; flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & NVME_NS_DATA_FLBAS_FORMAT_MASK; printf("Size (in LBAs): %lld (%lldM)\n", (long long)nsdata->nsze, (long long)nsdata->nsze / 1024 / 1024); printf("Capacity (in LBAs): %lld (%lldM)\n", (long long)nsdata->ncap, (long long)nsdata->ncap / 1024 / 1024); printf("Utilization (in LBAs): %lld (%lldM)\n", (long long)nsdata->nuse, (long long)nsdata->nuse / 1024 / 1024); printf("Thin Provisioning: %s\n", thin_prov ? "Supported" : "Not Supported"); printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1); printf("Current LBA Format: LBA Format #%02d\n", flbas_fmt); printf("Data Protection Caps: %s%s%s%s%s%s\n", (nsdata->dpc == 0) ? "Not Supported" : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_END_SHIFT) & NVME_NS_DATA_DPC_MD_END_MASK) ? "Last Bytes, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_START_SHIFT) & NVME_NS_DATA_DPC_MD_START_MASK) ? "First Bytes, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT3_SHIFT) & NVME_NS_DATA_DPC_PIT3_MASK) ? "Type 3, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_SHIFT) & NVME_NS_DATA_DPC_PIT2_MASK) ? "Type 2, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_MASK) & NVME_NS_DATA_DPC_PIT1_MASK) ? "Type 1" : ""); printf("Data Protection Settings: "); ptype = (nsdata->dps >> NVME_NS_DATA_DPS_PIT_SHIFT) & NVME_NS_DATA_DPS_PIT_MASK; if (ptype) { printf("Type %d, %s Bytes\n", ptype, ((nsdata->dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) & NVME_NS_DATA_DPS_MD_START_MASK) ? "First" : "Last"); } else { printf("Not Enabled\n"); } printf("Multi-Path I/O Capabilities: %s%s\n", (nsdata->nmic == 0) ? "Not Supported" : "", ((nsdata->nmic >> NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) & NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK) ? "May be shared" : ""); printf("Reservation Capabilities: %s%s%s%s%s%s%s%s%s\n", (nsdata->rescap == 0) ? "Not Supported" : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_IEKEY13_SHIFT) & NVME_NS_DATA_RESCAP_IEKEY13_MASK) ? "IEKEY13, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_AR_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_AR_MASK) ? "EX_AC_AR, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_AR_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_AR_MASK) ? "WR_EX_AR, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_RO_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_RO_MASK) ? "EX_AC_RO, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_RO_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_RO_MASK) ? "WR_EX_RO, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_MASK) ? "EX_AC, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_MASK) ? "WR_EX, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_PTPL_SHIFT) & NVME_NS_DATA_RESCAP_PTPL_MASK) ? "PTPL" : ""); printf("Format Progress Indicator: "); if ((nsdata->fpi >> NVME_NS_DATA_FPI_SUPP_SHIFT) & NVME_NS_DATA_FPI_SUPP_MASK) { printf("%u%% remains\n", (nsdata->fpi >> NVME_NS_DATA_FPI_PERC_SHIFT) & NVME_NS_DATA_FPI_PERC_MASK); } else printf("Not Supported\n"); t = (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_READ_SHIFT) & NVME_NS_DATA_DLFEAT_READ_MASK; printf("Deallocate Logical Block: Read %s%s%s\n", (t == NVME_NS_DATA_DLFEAT_READ_NR) ? "Not Reported" : (t == NVME_NS_DATA_DLFEAT_READ_00) ? "00h" : (t == NVME_NS_DATA_DLFEAT_READ_FF) ? "FFh" : "Unknown", (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_DWZ_SHIFT) & NVME_NS_DATA_DLFEAT_DWZ_MASK ? ", Write Zero" : "", (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_GCRC_SHIFT) & NVME_NS_DATA_DLFEAT_GCRC_MASK ? ", Guard CRC" : ""); printf("Optimal I/O Boundary (LBAs): %u\n", nsdata->noiob); printf("Globally Unique Identifier: "); for (i = 0; i < sizeof(nsdata->nguid); i++) printf("%02x", nsdata->nguid[i]); printf("\n"); printf("IEEE EUI64: "); for (i = 0; i < sizeof(nsdata->eui64); i++) printf("%02x", nsdata->eui64[i]); printf("\n"); for (i = 0; i <= nsdata->nlbaf; i++) { lbaf = nsdata->lbaf[i]; lbads = (lbaf >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & NVME_NS_DATA_LBAF_LBADS_MASK; ms = (lbaf >> NVME_NS_DATA_LBAF_MS_SHIFT) & NVME_NS_DATA_LBAF_MS_MASK; rp = (lbaf >> NVME_NS_DATA_LBAF_RP_SHIFT) & NVME_NS_DATA_LBAF_RP_MASK; printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d" " Performance: %s\n", i, 1 << lbads, ms, (rp == 0) ? "Best" : (rp == 1) ? "Better" : (rp == 2) ? "Good" : "Degraded"); } } 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, reserved8); print_hex(&cdata, hexlength); 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); exit(0); } 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); /* * We send IDENTIFY commands to the controller, not the namespace, * since it is an admin cmd. The namespace ID will be specified in * the IDENTIFY command itself. So parse the namespace's device node * string to get the controller substring and namespace ID. */ - parse_ns_str(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, reserved6); print_hex(&nsdata, hexlength); 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); exit(0); } 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: stable/12/sbin/nvmecontrol/logpage.c =================================================================== --- stable/12/sbin/nvmecontrol/logpage.c (revision 350928) +++ stable/12/sbin/nvmecontrol/logpage.c (revision 350929) @@ -1,474 +1,491 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" -#define LOGPAGE_USAGE \ - "logpage <-p page_id> [-b] [-v vendor] [-x] \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, "" }, + { 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; void logpage_register(struct logpage_function *p) { SLIST_INSERT_HEAD(&logpages, p, link); } const char * kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) { static char bad[32]; size_t i; for (i = 0; i < kv_count; i++, kv++) if (kv->key == key) return kv->name; snprintf(bad, sizeof(bad), "Attribute %#x", key); return bad; } static void print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { print_hex(data, length); } static void print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { write(STDOUT_FILENO, data, length); } static void * get_log_buffer(uint32_t size) { void *buf; if ((buf = malloc(size)) == NULL) errx(1, "unable to malloc %u bytes", size); memset(buf, 0, size); return (buf); } void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size) { struct nvme_pt_command pt; struct nvme_error_information_entry *err_entry; int i, err_pages; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; pt.cmd.cdw10 |= log_page; pt.cmd.cdw10 = htole32(pt.cmd.cdw10); pt.buf = payload; pt.len = payload_size; pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "get log page request failed"); /* Convert data to host endian */ switch (log_page) { case NVME_LOG_ERROR: err_entry = (struct nvme_error_information_entry *)payload; err_pages = payload_size / sizeof(struct nvme_error_information_entry); for (i = 0; i < err_pages; i++) nvme_error_information_entry_swapbytes(err_entry++); break; case NVME_LOG_HEALTH_INFORMATION: nvme_health_information_page_swapbytes( (struct nvme_health_information_page *)payload); break; case NVME_LOG_FIRMWARE_SLOT: nvme_firmware_page_swapbytes( (struct nvme_firmware_page *)payload); break; case INTEL_LOG_TEMP_STATS: intel_log_temp_stats_swapbytes( (struct intel_log_temp_stats *)payload); break; default: break; } if (nvme_completion_is_error(&pt.cpl)) errx(1, "get log page request returned error"); } static void print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) { int i, nentries; uint16_t status; uint8_t p, sc, sct, m, dnr; struct nvme_error_information_entry *entry = buf; printf("Error Information Log\n"); printf("=====================\n"); if (entry->error_count == 0) { printf("No error entries found\n"); return; } nentries = size/sizeof(struct nvme_error_information_entry); for (i = 0; i < nentries; i++, entry++) { if (entry->error_count == 0) break; status = entry->status; p = NVME_STATUS_GET_P(status); sc = NVME_STATUS_GET_SC(status); sct = NVME_STATUS_GET_SCT(status); m = NVME_STATUS_GET_M(status); dnr = NVME_STATUS_GET_DNR(status); printf("Entry %02d\n", i + 1); printf("=========\n"); printf(" Error count: %ju\n", entry->error_count); printf(" Submission queue ID: %u\n", entry->sqid); printf(" Command ID: %u\n", entry->cid); /* TODO: Export nvme_status_string structures from kernel? */ printf(" Status:\n"); printf(" Phase tag: %d\n", p); printf(" Status code: %d\n", sc); printf(" Status code type: %d\n", sct); printf(" More: %d\n", m); printf(" DNR: %d\n", dnr); printf(" Error location: %u\n", entry->error_location); printf(" LBA: %ju\n", entry->lba); printf(" Namespace ID: %u\n", entry->nsid); printf(" Vendor specific info: %u\n", entry->vendor_specific); } } void print_temp(uint16_t t) { printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); } static void print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_health_information_page *health = buf; char cbuf[UINT128_DIG + 1]; uint8_t warning; int i; warning = health->critical_warning; printf("SMART/Health Information Log\n"); printf("============================\n"); printf("Critical Warning State: 0x%02x\n", warning); printf(" Available spare: %d\n", !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE)); printf(" Temperature: %d\n", !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE)); printf(" Device reliability: %d\n", !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY)); printf(" Read only: %d\n", !!(warning & NVME_CRIT_WARN_ST_READ_ONLY)); printf(" Volatile memory backup: %d\n", !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP)); printf("Temperature: "); print_temp(health->temperature); printf("Available spare: %u\n", health->available_spare); printf("Available spare threshold: %u\n", health->available_spare_threshold); printf("Percentage used: %u\n", health->percentage_used); printf("Data units (512,000 byte) read: %s\n", uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); printf("Data units written: %s\n", uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); printf("Host read commands: %s\n", uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); printf("Host write commands: %s\n", uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); printf("Controller busy time (minutes): %s\n", uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); printf("Power cycles: %s\n", uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); printf("Power on hours: %s\n", uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); printf("Unsafe shutdowns: %s\n", uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); printf("Media errors: %s\n", uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); printf("No. error info log entries: %s\n", uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); printf("Error Temp Composite Time: %d\n", health->error_temp_time); for (i = 0; i < 8; i++) { if (health->temp_sensor[i] == 0) continue; printf("Temperature Sensor %d: ", i + 1); print_temp(health->temp_sensor[i]); } } static void print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused) { int i, slots; const char *status; struct nvme_firmware_page *fw = buf; uint8_t afi_slot; uint16_t oacs_fw; uint8_t fw_num_slots; afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT; afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK; oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; printf("Firmware Slot Log\n"); printf("=================\n"); if (oacs_fw == 0) slots = 1; else slots = MIN(fw_num_slots, MAX_FW_SLOTS); for (i = 0; i < slots; i++) { printf("Slot %d: ", i + 1); if (afi_slot == i + 1) status = " Active"; else status = "Inactive"; if (fw->revision[i] == 0LLU) printf("Empty\n"); else if (isprint(*(char *)&fw->revision[i])) printf("[%s] %.8s\n", status, (char *)&fw->revision[i]); else printf("[%s] %016jx\n", status, fw->revision[i]); } } /* * Table of log page printer / sizing. * * Make sure you keep all the pages of one vendor together so -v help * lists all the vendors pages. */ NVME_LOGPAGE(error, NVME_LOG_ERROR, NULL, "Drive Error Log", print_log_error, 0); NVME_LOGPAGE(health, NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", print_log_health, sizeof(struct nvme_health_information_page)); NVME_LOGPAGE(fw, NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", print_log_firmware, sizeof(struct nvme_firmware_page)); static void logpage_help(void) { const struct logpage_function *f; const char *v; fprintf(stderr, "\n"); fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); fprintf(stderr, "-------- ---------- ----------\n"); SLIST_FOREACH(f, &logpages, link) { v = f->vendor == NULL ? "-" : f->vendor; fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name); } exit(1); } 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); ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & NVME_CTRLR_DATA_LPA_NS_SMART_MASK; /* * The log page attribtues indicate whether or not the controller * supports the SMART/Health information log page on a per * 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 " "smart/health information"); } 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 * (print_hex). If there was a vendor specified by the user, and * 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: stable/12/sbin/nvmecontrol/modules/wdc/wdc.c =================================================================== --- stable/12/sbin/nvmecontrol/modules/wdc/wdc.c (revision 350928) +++ stable/12/sbin/nvmecontrol/modules/wdc/wdc.c (revision 350929) @@ -1,597 +1,625 @@ /*- * Copyright (c) 2017 Netflix, Inc * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #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) { struct nvme_controller_data cdata; char sn[NVME_SERIAL_NUMBER_LENGTH + 1]; char *walker; len -= strlen(buf); buf += strlen(buf); read_controller_data(fd, &cdata); memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH); walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1; while (walker > sn && *walker == ' ') walker--; *++walker = '\0'; snprintf(buf, len, "%s%s.bin", sn, suffix); } static void wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd, uint8_t *buffer, size_t buflen) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = opcode; pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); /* - 1 like all the others ??? */ pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); pt.cmd.cdw12 = htole32(cmd); pt.buf = buffer; pt.len = buflen; pt.is_read = 1; // printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n", // (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "wdc_get_data request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "wdc_get_data request returned error"); } static void wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode, uint32_t cmd, int len_off) { int first; int fd2; uint8_t *buf; uint32_t len, offset; size_t resid; wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix); /* XXX overwrite protection? */ fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd2 < 0) err(1, "open %s", tmpl); buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE); if (buf == NULL) errx(1, "Can't get buffer to read dump"); offset = 0; len = NVME_MAX_XFER_SIZE; first = 1; do { resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len; wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid); if (first) { len = be32dec(buf + len_off); if (len == 0) errx(1, "No data for %s", suffix); if (memcmp("E6LG", buf, 4) != 0) printf("Expected header of E6LG, found '%4.4s' instead\n", buf); printf("Dumping %d bytes of version %d.%d log to %s\n", len, buf[8], buf[9], tmpl); /* * Adjust amount to dump if total dump < 1MB, * though it likely doesn't matter to the WDC * analysis tools. */ if (resid > len) resid = len; first = 0; } if (write(fd2, buf, resid) != (ssize_t)resid) err(1, "write"); offset += resid; len -= resid; } while (len > 0); free(buf); close(fd2); } 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); exit(1); } 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); } /* * HGST's 0xc1 page. This is a grab bag of additional data. Please see * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf * Appendix A for details */ typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); struct subpage_print { uint16_t key; subprint_fn_t fn; }; static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static struct subpage_print hgst_subpage[] = { { 0x02, print_hgst_info_write_errors }, { 0x03, print_hgst_info_read_errors }, { 0x05, print_hgst_info_verify_errors }, { 0x10, print_hgst_info_self_test }, { 0x15, print_hgst_info_background_scan }, { 0x30, print_hgst_info_erase_errors }, { 0x31, print_hgst_info_erase_counts }, { 0x32, print_hgst_info_temp_history }, { 0x37, print_hgst_info_ssd_perf }, { 0x38, print_hgst_info_firmware_load }, }; /* Print a subpage that is basically just key value pairs */ static void print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, const struct kv_name *kv, size_t kv_count) { uint8_t *wsp, *esp; uint16_t ptype; uint8_t plen; uint64_t param; int i; wsp = buf; esp = wsp + size; while (wsp < esp) { ptype = le16dec(wsp); wsp += 2; wsp++; /* Flags, just ignore */ plen = *wsp++; param = 0; for (i = 0; i < plen; i++) param |= (uint64_t)*wsp++ << (i * 8); printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); } } static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Writes" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Flash Write Commands" }, { 0x8001, "HGST Special" }, }; printf("Write Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Reads" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Flash Read Commands" }, { 0x8001, "XOR Recovered" }, { 0x8002, "Total Corrected Bits" }, }; printf("Read Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Reads" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Commands Processed" }, }; printf("Verify Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) { size_t i; uint8_t *walker = buf; uint16_t code, hrs; uint32_t lba; printf("Self Test Subpage:\n"); for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ code = le16dec(walker); walker += 2; walker++; /* Ignore fixed flags */ if (*walker == 0) /* Last entry is zero length */ break; if (*walker++ != 0x10) { printf("Bad length for self test report\n"); return; } printf(" %-30s: %d\n", "Recent Test", code); printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); walker++; printf(" %-28s: %#x\n", "Self-Test Number", *walker++); hrs = le16dec(walker); walker += 2; lba = le32dec(walker); walker += 4; printf(" %-28s: %u\n", "Total Power On Hrs", hrs); printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); } } static void print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) { uint8_t *walker = buf; uint8_t status; uint16_t code, nscan, progress; uint32_t pom, nand; printf("Background Media Scan Subpage:\n"); /* Decode the header */ code = le16dec(walker); walker += 2; walker++; /* Ignore fixed flags */ if (*walker++ != 0x10) { printf("Bad length for background scan header\n"); return; } if (code != 0) { printf("Expceted code 0, found code %#x\n", code); return; } pom = le32dec(walker); walker += 4; walker++; /* Reserved */ status = *walker++; nscan = le16dec(walker); walker += 2; progress = le16dec(walker); walker += 2; walker += 6; /* Reserved */ printf(" %-30s: %d\n", "Power On Minutes", pom); printf(" %-30s: %x (%s)\n", "BMS Status", status, status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); printf(" %-30s: %d\n", "Number of BMS", nscan); printf(" %-30s: %d\n", "Progress Current BMS", progress); /* Report retirements */ if (walker - (uint8_t *)buf != 20) { printf("Coding error, offset not 20\n"); return; } size -= 20; printf(" %-30s: %d\n", "BMS retirements", size / 0x18); while (size > 0) { code = le16dec(walker); walker += 2; walker++; if (*walker++ != 0x14) { printf("Bad length parameter\n"); return; } pom = le32dec(walker); walker += 4; /* * Spec sheet says the following are hard coded, if true, just * print the NAND retirement. */ if (walker[0] == 0x41 && walker[1] == 0x0b && walker[2] == 0x01 && walker[3] == 0x00 && walker[4] == 0x00 && walker[5] == 0x00 && walker[6] == 0x00 && walker[7] == 0x00) { walker += 8; walker += 4; /* Skip reserved */ nand = le32dec(walker); walker += 4; printf(" %-30s: %d\n", "Retirement number", code); printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); } else { printf("Parameter %#x entry corrupt\n", code); walker += 16; } } } static void print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Erase" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Flash Erase Commands" }, { 0x8001, "Mfg Defect Count" }, { 0x8002, "Grown Defect Count" }, { 0x8003, "Erase Count -- User" }, { 0x8004, "Erase Count -- System" }, }; printf("Erase Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { /* My drive doesn't export this -- so not coding up */ printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); } static void print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) { uint8_t *walker = buf; uint32_t min; printf("Temperature History:\n"); printf(" %-30s: %d C\n", "Current Temperature", *walker++); printf(" %-30s: %d C\n", "Reference Temperature", *walker++); printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); min = le32dec(walker); walker += 4; printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); min = le32dec(walker); walker += 4; printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60); min = le32dec(walker); walker += 4; printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); } static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) { uint8_t *walker = buf; uint64_t val; printf("SSD Performance Subpage Type %d:\n", res); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Read Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Read Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Read Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Read Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Write Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Write Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Read Before Writes", val); } static void print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) { uint8_t *walker = buf; printf("Firmware Load Subpage:\n"); printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); } static void kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) { size_t i; for (i = 0; i < nsp; i++, sp++) { if (sp->key == subtype) { sp->fn(buf, subtype, res, size); return; } } printf("No handler for page type %x\n", subtype); } static void print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { uint8_t *walker, *end, *subpage; int pages; uint16_t len; uint8_t subtype, res; printf("HGST Extra Info Log\n"); printf("===================\n"); walker = buf; pages = *walker++; walker++; len = le16dec(walker); walker += 2; end = walker + len; /* Length is exclusive of this header */ while (walker < end) { subpage = walker + 4; subtype = *walker++ & 0x3f; /* subtype */ res = *walker++; /* Reserved */ len = le16dec(walker); walker += len + 2; /* Length, not incl header */ if (walker > end) { printf("Ooops! Off the end of the list\n"); break; } kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); } } NVME_LOGPAGE(hgst_info, HGST_INFO_LOG, "hgst", "Detailed Health/SMART", print_hgst_info_log, DEFAULT_SIZE); 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: stable/12/sbin/nvmecontrol/ns.c =================================================================== --- stable/12/sbin/nvmecontrol/ns.c (revision 350928) +++ stable/12/sbin/nvmecontrol/ns.c (revision 350929) @@ -1,446 +1,538 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Netflix, Inc * Copyright (C) 2018 Alexander Motin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #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; }; static struct ns_result_str ns_result[] = { { 0x2, "Invalid Field"}, { 0xa, "Invalid Format"}, { 0xb, "Invalid Namespace or format"}, { 0x15, "Namespace insufficent capacity"}, { 0x16, "Namespace ID unavaliable"}, { 0x18, "Namespace already attached"}, { 0x19, "Namespace is private"}, { 0x1a, "Namespace is not attached"}, { 0x1b, "Thin provisioning not supported"}, { 0x1c, "Controller list invalid"}, { 0xFFFF, "Unknown"} }; static const char * get_res_str(uint16_t res) { struct ns_result_str *t = ns_result; while (t->res != 0xFFFF) { if (t->res == res) return (t->str); t++; } return t->str; } /* * NS MGMT Command specific status values: * 0xa = Invalid Format * 0x15 = Namespace Insuffience capacity * 0x16 = Namespace ID unavailable (number namespaces exceeded) * 0xb = Thin Provisioning Not supported */ static void -nscreate(const struct 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. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); /* Allow namespaces sharing if Multi-Path I/O is supported. */ - if (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)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; pt.cmd.cdw10 = 0; /* create */ pt.buf = &nsdata; pt.len = sizeof(struct nvme_namespace_data); pt.is_read = 0; /* passthrough writes data to ctrlr */ if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", argv[optind], result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace creation failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } printf("namespace %d created\n", pt.cpl.cdw0); exit(0); } static void -nsdelete(const struct 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. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; pt.cmd.cdw10 = 1; /* delete */ 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); } /* * Attach and Detach use Dword 10, and a controller list (section 4.9) * This struct is 4096 bytes in size. * 0h = attach * 1h = detach * * Result values for both attach/detach: * * Completion 18h = Already attached * 19h = NS is private and already attached to a controller * 1Ah = Not attached, request could not be completed * 1Ch = Controller list invalid. * * 0x2 Invalid Field can occur if ctrlrid d.n.e in system. */ static void -nsattach(const struct 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. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); - if (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; pt.cmd.cdw10 = htole32(0x13); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } else { /* By default attach to this controller. */ - if (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. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); - if (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); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); if (clist[0] == 0) { - 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); if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", argv[optind], result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace detach failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } - printf("namespace %d detached\n", 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: stable/12/sbin/nvmecontrol/nvmecontrol.c =================================================================== --- stable/12/sbin/nvmecontrol/nvmecontrol.c (revision 350928) +++ stable/12/sbin/nvmecontrol/nvmecontrol.c (revision 350929) @@ -1,348 +1,207 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #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; uint8_t *p, *end; end = (uint8_t *)data + length; for (i = 0; i < length; i++) { p = (uint8_t *)data + (i*16); printf("%03x: ", i*16); for (j = 0; j < 16 && p < end; j++) printf("%02x ", *p++); if (p >= end) break; printf("\n"); } printf("\n"); } static void print_dwords(void *data, uint32_t length) { uint32_t *p; uint32_t i, j; p = (uint32_t *)data; length /= sizeof(uint32_t); for (i = 0; i < length; i+=8) { printf("%03x: ", i*4); for (j = 0; j < 8; j++) printf("%08x ", p[i+j]); printf("\n"); } printf("\n"); } void print_hex(void *data, uint32_t length) { if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0) print_dwords(data, length); else print_bytes(data, length); } void read_controller_data(int fd, struct nvme_controller_data *cdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(1); pt.buf = cdata; pt.len = sizeof(*cdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_controller_data_swapbytes(cdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); pt.buf = nsdata; pt.len = sizeof(*nsdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_namespace_data_swapbytes(nsdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } int open_dev(const char *str, int *fd, int show_error, int exit_on_error) { char full_path[64]; if (!strnstr(str, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) { if (show_error) warnx("controller/namespace ids must begin with '%s'", NVME_CTRLR_PREFIX); if (exit_on_error) exit(1); else return (EINVAL); } snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str); *fd = open(full_path, O_RDWR); if (*fd < 0) { if (show_error) warn("could not open %s", full_path); if (exit_on_error) exit(1); else return (errno); } return (0); } void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid) { char *nsloc; /* * Pull the namespace id from the string. +2 skips past the "ns" part * of the string. Don't search past 10 characters into the string, * otherwise we know it is malformed. */ nsloc = strnstr(ns_str, NVME_NS_PREFIX, 10); if (nsloc != NULL) *nsid = strtol(nsloc + 2, NULL, 10); if (nsloc == NULL || (*nsid == 0 && errno != 0)) errx(1, "invalid namespace ID '%s'", ns_str); /* * The controller string will include only the nvmX part of the * nvmeXnsY string. */ 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: stable/12/sbin/nvmecontrol/nvmecontrol.h =================================================================== --- stable/12/sbin/nvmecontrol/nvmecontrol.h (revision 350928) +++ stable/12/sbin/nvmecontrol/nvmecontrol.h (revision 350929) @@ -1,153 +1,103 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __NVMECONTROL_H__ #define __NVMECONTROL_H__ -#include -#include #include +#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 { SLIST_ENTRY(logpage_function) link; uint8_t log_page; const char *vendor; const char *name; print_fn_t print_fn; size_t size; }; - #define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \ static struct logpage_function unique ## _lpf = { \ .log_page = lp, \ .vendor = vend, \ .name = nam, \ .print_fn = fn, \ .size = sz, \ } ; \ static void logpage_reg_##unique(void) __attribute__((constructor)); \ static void logpage_reg_##unique(void) { logpage_register(&unique##_lpf); } #define DEFAULT_SIZE (4096) struct kv_name { uint32_t key; const char *name; }; const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key); -NVME_CMD_DECLARE(top, struct nvme_function); void logpage_register(struct logpage_function *p); - -struct set_concat { - void **begin; - void **limit; -}; -void set_concat_add(struct set_concat *m, void *begin, void *end); -#define SET_CONCAT_DEF(set, t) \ -static struct set_concat set ## _concat; \ -static inline const t * const *set ## _begin(void) { return ((const t * const *)set ## _concat.begin); } \ -static inline const t * const *set ## _limit(void) { return ((const t * const *)set ## _concat.limit); } \ -void add_to_ ## set(t **b, t **e) \ -{ \ - set_concat_add(&set ## _concat, b, e); \ -} -#define SET_CONCAT_DECL(set, t) \ - void add_to_ ## set(t **b, t **e) -SET_CONCAT_DECL(top, struct nvme_function); - #define NVME_CTRLR_PREFIX "nvme" #define NVME_NS_PREFIX "ns" int open_dev(const char *str, int *fd, int show_error, int exit_on_error); void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid); void read_controller_data(int fd, struct nvme_controller_data *cdata); void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata); void print_hex(void *data, uint32_t length); void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size); 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 * doesn't exist, so we use 64-bit values. So, on 32-bit i386, * you'll get truncated values until someone implement 128bit * ints in sofware. */ #define UINT128_DIG 39 #ifdef __i386__ typedef uint64_t uint128_t; #else typedef __uint128_t uint128_t; #endif static __inline uint128_t to128(void *p) { return *(uint128_t *)p; } uint64_t le48dec(const void *pp); char * uint128_to_str(uint128_t u, char *buf, size_t buflen); - #endif Index: stable/12/sbin/nvmecontrol/perftest.c =================================================================== --- stable/12/sbin/nvmecontrol/perftest.c (revision 350928) +++ stable/12/sbin/nvmecontrol/perftest.c (revision 350929) @@ -1,179 +1,186 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #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" \ - " \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) { uint64_t io_completed = 0, iops, mbps; uint32_t i; for (i = 0; i < io_test->num_threads; i++) io_completed += io_test->io_completed[i]; iops = io_completed/io_test->time; mbps = iops * io_test->size / (1024*1024); printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7ju MB/s: %4ju\n", io_test->num_threads, io_test->size, io_test->opc == NVME_OPC_READ ? "READ" : "WRITE", io_test->time, (uintmax_t)iops, (uintmax_t)mbps); if (perthread) for (i = 0; i < io_test->num_threads; i++) printf("\t%3d: %8ju IO/s\n", i, (uintmax_t)io_test->io_completed[i]/io_test->time); } 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: stable/12/sbin/nvmecontrol/power.c =================================================================== --- stable/12/sbin/nvmecontrol/power.c (revision 350928) +++ stable/12/sbin/nvmecontrol/power.c (revision 350929) @@ -1,192 +1,201 @@ /*- * Copyright (c) 2016 Netflix, Inc * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include +#include #include #include #include #include #include #include "nvmecontrol.h" _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]] \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) { int mpower, apower, ipower; uint8_t mps, nops, aps, apw; mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) & NVME_PWR_ST_MPS_MASK; nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) & NVME_PWR_ST_NOPS_MASK; apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) & NVME_PWR_ST_APW_MASK; aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) & NVME_PWR_ST_APS_MASK; mpower = nps->mp; if (mps == 0) mpower *= 100; ipower = nps->idlp; if (nps->ips == 1) ipower *= 100; apower = nps->actp; if (aps == 1) apower *= 100; printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n", i, mpower / 10000, mpower % 10000, nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000, nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl, nps->rwt, nps->rwl, ipower / 10000, ipower % 10000, apower / 10000, apower % 10000, apw); } static void power_list(struct nvme_controller_data *cdata) { int i; printf("\nPower States Supported: %d\n\n", cdata->npss + 1); printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workloadd\n"); printf("-- -------- --------- --------- -- -- -- -- -------- -------- --\n"); for (i = 0; i <= cdata->npss; i++) power_list_one(i, &cdata->power_state[i]); } static void power_set(int fd, int power_val, int workload, int perm) { struct nvme_pt_command pt; uint32_t p; p = perm ? (1u << 31) : 0; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_SET_FEATURES; pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT | p); pt.cmd.cdw11 = htole32(power_val | (workload << 5)); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "set feature power mgmt request returned error"); } static void power_show(int fd) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_GET_FEATURES; pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "set feature power mgmt request returned error"); printf("Current Power Mode is %d\n", pt.cpl.cdw0); } 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); out: close(fd); 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: stable/12/sbin/nvmecontrol/reset.c =================================================================== --- stable/12/sbin/nvmecontrol/reset.c (revision 350928) +++ stable/12/sbin/nvmecontrol/reset.c (revision 350929) @@ -1,70 +1,76 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "nvmecontrol.h" -#define RESET_USAGE \ - "reset \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); Index: stable/12 =================================================================== --- stable/12 (revision 350928) +++ stable/12 (revision 350929) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r350057