Index: sbin/nvmecontrol/Makefile =================================================================== --- sbin/nvmecontrol/Makefile +++ sbin/nvmecontrol/Makefile @@ -2,8 +2,9 @@ PACKAGE=runtime PROG= nvmecontrol -SRCS= nvmecontrol.c devlist.c firmware.c format.c identify.c identify_ext.c logpage.c \ +SRCS= opts.c 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+= passthru.c MAN= nvmecontrol.8 LDFLAGS+= -rdynamic SUBDIR= modules Index: sbin/nvmecontrol/devlist.c =================================================================== --- sbin/nvmecontrol/devlist.c +++ sbin/nvmecontrol/devlist.c @@ -45,6 +45,8 @@ #define DEVLIST_USAGE \ "devlist\n" +#define DEVLIST_DESCR \ + "Display a list of NVMe controllers and namespaces." #define NVME_MAX_UNIT 256 @@ -120,4 +122,4 @@ exit(1); } -NVME_COMMAND(top, devlist, devlist, DEVLIST_USAGE); +NVME_COMMAND(top, devlist, devlist, DEVLIST_USAGE, NULL, DEVLIST_DESCR); Index: sbin/nvmecontrol/firmware.c =================================================================== --- sbin/nvmecontrol/firmware.c +++ sbin/nvmecontrol/firmware.c @@ -50,8 +50,13 @@ #include "nvmecontrol.h" -#define FIRMWARE_USAGE \ +#define FIRMWARE_USAGE \ "firmware [-s slot] [-f path_to_firmware] [-a] \n" +#define FIRMWARE_ARGS \ + "" +#define FIRMWARE_DESCR \ + "Download firmware image to controller." + static int slot_has_valid_firmware(int fd, int slot) @@ -333,4 +338,4 @@ exit(0); } -NVME_COMMAND(top, firmware, firmware, FIRMWARE_USAGE); +NVME_COMMAND(top, firmware, firmware, FIRMWARE_USAGE, FIRMWARE_ARGS, FIRMWARE_DESCR); Index: sbin/nvmecontrol/format.c =================================================================== --- sbin/nvmecontrol/format.c +++ sbin/nvmecontrol/format.c @@ -43,8 +43,12 @@ #include "nvmecontrol.h" -#define FORMAT_USAGE \ +#define FORMAT_USAGE \ "format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] \n" +#define FORMAT_ARGS \ + "" +#define FORMAT_DESCR \ + "Format/erase one or all the namespaces." static void format(const struct nvme_function *nf, int argc, char *argv[]) @@ -181,4 +185,4 @@ exit(0); } -NVME_COMMAND(top, format, format, FORMAT_USAGE); +NVME_COMMAND(top, format, format, FORMAT_USAGE, FORMAT_ARGS, FORMAT_DESCR); Index: sbin/nvmecontrol/identify.c =================================================================== --- sbin/nvmecontrol/identify.c +++ sbin/nvmecontrol/identify.c @@ -45,6 +45,10 @@ #define IDENTIFY_USAGE \ "identify [-x [-v]] \n" +#define IDENTIFY_ARGS \ + "" +#define IDENTIFY_DESCR \ + "Print a human-readable summary of the IDENTIFY information from namespace or controller" static void print_namespace(struct nvme_namespace_data *nsdata) @@ -299,4 +303,4 @@ identify_ns(nf, argc, argv); } -NVME_COMMAND(top, identify, identify, IDENTIFY_USAGE); +NVME_COMMAND(top, identify, identify, IDENTIFY_USAGE, IDENTIFY_ARGS, IDENTIFY_DESCR); Index: sbin/nvmecontrol/logpage.c =================================================================== --- sbin/nvmecontrol/logpage.c +++ sbin/nvmecontrol/logpage.c @@ -49,7 +49,11 @@ #include "nvmecontrol.h" #define LOGPAGE_USAGE \ - "logpage <-p page_id> [-b] [-v vendor] [-x] \n" \ + "logpage <-p page_id> [-b] [-v vendor] [-x] \n" +#define LOGPAGE_ARGS \ + "" +#define LOGPAGE_DESCR \ + "Print logpages in human-readable form" #define MAX_FW_SLOTS (7) @@ -471,4 +475,4 @@ exit(0); } -NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE); +NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE, LOGPAGE_ARGS, LOGPAGE_DESCR); Index: sbin/nvmecontrol/modules/wdc/wdc.c =================================================================== --- sbin/nvmecontrol/modules/wdc/wdc.c +++ sbin/nvmecontrol/modules/wdc/wdc.c @@ -41,8 +41,12 @@ #include "nvmecontrol.h" -#define WDC_USAGE \ +#define WDC_USAGE \ "wdc (cap-diag)\n" +#define WDC_ARGS \ + "subcommand" +#define WDC_DESCR \ + "Western Digital (WDC, HGST) specific commands" NVME_CMD_DECLARE(wdc, struct nvme_function); @@ -54,8 +58,10 @@ static void wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]); #define WDC_CAP_DIAG_USAGE "wdc cap-diag [-o path-template]\n" +#define WDC_CAP_ARGS "" +#define WDC_CAP_DESCR "Retrieve the drive's cap-diag logs" -NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE); +NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE, WDC_CAP_ARGS, WDC_CAP_DESCR); static void wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix) @@ -593,4 +599,4 @@ NVME_LOGPAGE(wdc_info, HGST_INFO_LOG, "wdc", "Detailed Health/SMART", print_hgst_info_log, DEFAULT_SIZE); -NVME_COMMAND(top, wdc, wdc, WDC_USAGE); +NVME_COMMAND(top, wdc, wdc, WDC_USAGE, WDC_ARGS, WDC_DESCR); Index: sbin/nvmecontrol/ns.c =================================================================== --- sbin/nvmecontrol/ns.c +++ sbin/nvmecontrol/ns.c @@ -60,15 +60,29 @@ #define NSDETACH_USAGE \ "ns detach -n nsid [-c ctrlrid] nvmeN\n" +#define CREATE_DESCR \ + "Create a new namespace" +#define DELETE_DESCR \ + "Delete a new namespace" +#define ATTACH_DESCR \ + "Attach a new namespace" +#define DETACH_DESCR \ + "Detach a new namespace" + +#define NS_ARGS \ + "" +#define NS_DESCR \ + "Various commands to create and manage namespaces" + static void nscreate(const struct nvme_function *nf, int argc, char *argv[]); static void nsdelete(const struct nvme_function *nf, int argc, char *argv[]); static void nsattach(const struct nvme_function *nf, int argc, char *argv[]); static void nsdetach(const struct nvme_function *nf, int argc, char *argv[]); -NVME_COMMAND(ns, create, nscreate, NSCREATE_USAGE); -NVME_COMMAND(ns, delete, nsdelete, NSDELETE_USAGE); -NVME_COMMAND(ns, attach, nsattach, NSATTACH_USAGE); -NVME_COMMAND(ns, detach, nsdetach, NSDETACH_USAGE); +NVME_COMMAND(ns, create, nscreate, NSCREATE_USAGE, NS_ARGS, CREATE_DESCR); +NVME_COMMAND(ns, delete, nsdelete, NSDELETE_USAGE, NS_ARGS, DELETE_DESCR); +NVME_COMMAND(ns, attach, nsattach, NSATTACH_USAGE, NS_ARGS, ATTACH_DESCR); +NVME_COMMAND(ns, detach, nsdetach, NSDETACH_USAGE, NS_ARGS, DETACH_DESCR); struct ns_result_str { uint16_t res; @@ -443,4 +457,4 @@ DISPATCH(argc, argv, ns); } -NVME_COMMAND(top, ns, ns, NS_USAGE); +NVME_COMMAND(top, ns, ns, NS_USAGE, NS_ARGS, NS_DESCR); Index: sbin/nvmecontrol/nvmecontrol.h =================================================================== --- sbin/nvmecontrol/nvmecontrol.h +++ sbin/nvmecontrol/nvmecontrol.h @@ -42,13 +42,16 @@ const char *name; nvme_fn_t fn; const char *usage; + const char *args; + const char *descr; }; #define NVME_SETNAME(set) set #define NVME_CMDSET(set, sym) DATA_SET(NVME_SETNAME(set), sym) -#define NVME_COMMAND(set, nam, function, usage_str) \ +#define NVME_COMMAND(set, nam, function, usage_str, args_str, descr_str) \ static struct nvme_function function ## _nvme_cmd = \ - { .name = #nam, .fn = function, .usage = usage_str }; \ + { .name = #nam, .fn = function, .usage = usage_str, \ + .args = args_str, .descr = descr_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)) @@ -127,6 +130,9 @@ (const struct nvme_function * const *)NVME_CMD_BEGIN(set), \ (const struct nvme_function * const *)NVME_CMD_LIMIT(set)) \ +struct args; +int arg_parse_and_open(int argc, char * const * argv, const char *desc, const struct args *args); + /* Utility Routines */ /* * 128-bit integer augments to standard values. On i386 this Index: sbin/nvmecontrol/nvmecontrol.c =================================================================== --- sbin/nvmecontrol/nvmecontrol.c +++ sbin/nvmecontrol/nvmecontrol.c @@ -48,9 +48,21 @@ #include #include "nvmecontrol.h" +#include "opts.h" SET_CONCAT_DEF(top, struct nvme_function); +int +arg_parse_and_open(int argc, char * const * argv, const char *desc, const struct args *args) +{ + int fd; + + if (arg_parse(argc, argv, desc, args)) + return (-1); + open_dev(argv[optind], &fd, 1, 1); + return (fd); +} + static void print_usage(const struct nvme_function *f) { Index: sbin/nvmecontrol/opts.h =================================================================== --- sbin/nvmecontrol/opts.h +++ sbin/nvmecontrol/opts.h @@ -1,8 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (C) 2012-2013 Intel Corporation - * All rights reserved. + * Copyright (c) 2019 Netflix, Inc * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,47 +23,33 @@ * 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$ */ -#include -__FBSDID("$FreeBSD$"); - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "nvmecontrol.h" - -#define RESET_USAGE \ - "reset \n" +#ifndef ARG_H +#define ARG_H -static void -reset(const struct nvme_function *nf, int argc, char *argv[]) -{ - int ch, fd; - - while ((ch = getopt(argc, argv, "")) != -1) { - switch ((char)ch) { - default: - usage(nf); - } - } - - /* 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); -} +/* + * Regularized parsing of simple arguments built on top of getopt_long. + */ -NVME_COMMAND(top, reset, reset, RESET_USAGE); +typedef enum arg_type { + arg_none = 0, + arg_uint8, + arg_uint16, + arg_uint32, + arg_string, + arg_path, +} arg_type; + +struct args { + const char *long_arg; + int short_arg; + arg_type at; + void *ptr; + const char *descr; +}; + +int arg_parse(int argc, char * const * argv, const char *desc, const struct args *); +#endif /* ARG_H */ Index: sbin/nvmecontrol/opts.c =================================================================== --- /dev/null +++ sbin/nvmecontrol/opts.c @@ -0,0 +1,162 @@ +/*- + * 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 "opts.h" + +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; + } +} + +static void +arg_help(int argc __unused, char * const *argv, const char *desc, const struct args *args) +{ + int i; + char buf[41]; + char *cp; + + if (argv[optind]) + fprintf(stderr, "Unknown argument: %s\n", argv[optind]); + fprintf(stderr, "Usage:\n nvmecontrol %s device \n", argv[0]); + fprintf(stderr, "\n%s\n", desc); + fprintf(stderr, "Options:\n"); + for (i = 0; args[i].long_arg != NULL; i++) { + strlcpy(buf, " [ --", sizeof(buf)); + strlcat(buf, args[i].long_arg, sizeof(buf)); + arg_suffix(buf, sizeof(buf), args[i].at); + if (isprint(args[i].short_arg)) { + strlcat(buf, ", -", sizeof(buf)); + cp = buf + strlen(buf); + if (cp - buf < (ptrdiff_t)sizeof(buf) - 2) { + *cp++ = args[i].short_arg; + *cp++ = '\0'; + } + arg_suffix(buf, sizeof(buf), args[i].at); + } + strlcat(buf, " ]", sizeof(buf)); + fprintf(stderr, "%-40.40s - %s\n", buf, args[i].descr); + } + exit(1); +} + +int +arg_parse(int argc, char * const * argv, const char *desc, const struct args *args) +{ + int i, n, idx, ch; + unsigned long v; + struct option *opts; + char *shortopts, *p; + + for (n = 0; args[n].long_arg != NULL;) + n++; + opts = malloc((n + 2) * sizeof(struct option)); + if (opts == NULL) + err(1, "option memory"); + p = shortopts = malloc((n + 3) * sizeof(char)); + if (shortopts == NULL) + err(1, "shortopts memory"); + for (i = 0; i < n; i++) { + opts[i].name = args[i].long_arg; + opts[i].has_arg = args[i].at == arg_none ? no_argument : required_argument; + opts[i].flag = NULL; + opts[i].val = args[i].short_arg; + if (isprint(args[i].short_arg)) + *p++ = args[i].short_arg; + } + opts[n].name = "help"; + opts[n].has_arg = no_argument; + opts[n].flag = NULL; + opts[n].val = '?'; + *p++ = '?'; + *p++ = '\0'; + memset(opts + n + 1, 0, sizeof(struct option)); + while ((ch = getopt_long(argc, argv, shortopts, opts, &idx)) != -1) { + if (idx == n) + arg_help(argc, argv, desc, args); + switch (args[idx].at) { + case arg_none: + *(bool *)args[idx].ptr = true; + break; + case arg_string: + case arg_path: + *(const char **)args[idx].ptr = optarg; + break; + case arg_uint8: + v = strtoul(optarg, NULL, 0); + if (v > 0xff) + goto bad_arg; + *(uint8_t *)args[idx].ptr = v; + break; + case arg_uint16: + v = strtoul(optarg, NULL, 0); + if (v > 0xffff) + goto bad_arg; + *(uint16_t *)args[idx].ptr = v; + break; + case arg_uint32: + v = strtoul(optarg, NULL, 0); + if (v > 0xffffffff) + goto bad_arg; + *(uint32_t *)args[idx].ptr = v; + break; + } + } + return (0); +bad_arg: + fprintf(stderr, "Bad value to --%s: %s\n", args[idx].long_arg, optarg); + exit(1); +} Index: sbin/nvmecontrol/passthru.c =================================================================== --- /dev/null +++ sbin/nvmecontrol/passthru.c @@ -0,0 +1,276 @@ +/*- + * 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 "opts.h" +#include "nvmecontrol.h" + +#define ADMIN_USAGE \ + "admin-passthru >>args<<\n" +#define ADMIN_ARGS \ + "" +#define ADMIN_DESCR \ + "Send a raw administrative command to the controller via a passthrough interface." +#define IO_USAGE \ + "io-passthru >>args<<\n" +#define IO_ARGS \ + "" +#define IO_DESCR \ + "Send a raw I/O command to the controller via a passthrough interface." + +static void +passthru(const struct nvme_function *nf __unused, int argc, char *argv[], const char *desc) +{ + int fd = -1, ifd = -1; + void *data = NULL, *metadata = NULL; + struct nvme_pt_command pt; + + struct options { + uint8_t opcode; + uint8_t flags; + uint16_t rsvd; + uint32_t nsid; + uint32_t data_len; + uint32_t metadata_len; + uint32_t timeout; + uint32_t cdw2; + uint32_t cdw3; + uint32_t cdw10; + uint32_t cdw11; + uint32_t cdw12; + uint32_t cdw13; + uint32_t cdw14; + uint32_t cdw15; + const char *ifn; + bool binary; + bool show_command; + bool dry_run; + bool read; + bool write; + uint8_t prefill; + } opt = { + .binary = false, + .cdw10 = 0, + .cdw11 = 0, + .cdw12 = 0, + .cdw13 = 0, + .cdw14 = 0, + .cdw15 = 0, + .cdw2 = 0, + .cdw3 = 0, + .data_len = 0, + .dry_run = false, + .flags = 0, + .ifn = "", + .metadata_len = 0, + .nsid = 0, + .opcode = 0, + .prefill = 0, + .read = false, + .rsvd = 0, + .show_command = false, + .timeout = 0, + .write = false, + }; + + const char *opcode = "NVMe command opcode (required)"; + const char *flags = "NVMe command flags"; + const char *rsvd = "Reserved field value"; + const char *nsid = "Namespace id (ignored on FreeBSD)"; + const char *data_len = "Length of data for I/O (bytes)"; + const char *metadata_len = "Length of metadata segment (bytes) (igored)"; + const char *cdw2 = "Command dword 2 value"; + const char *cdw3 = "Command dword 3 value"; + const char *cdw10 = "Command dword 10 value"; + const char *cdw11 = "Command dword 11 value"; + const char *cdw12 = "Command dword 12 value"; + const char *cdw13 = "Command dword 13 value"; + const char *cdw14 = "Command dword 14 value"; + const char *cdw15 = "Command dword 15 value"; + const char *inf = "Input file to send (default stdin)"; + const char *timeout = "Command timeout (ms)"; + const char *raw = "Output in binary format"; + const char *prefill = "Value to prefill payload with"; + const char *dry_run = "Don't actually execute the command"; + const char *rd = "Command reads data from device"; + const char *wr = "Command writes data to device"; + const char *show = "Show all the command values on stdout"; + /* + * Argument names and short names selected to match the nvme-cli program + * so vendor-siupplied formulas work out of the box on FreeBSD with a simple + * s/nvme/nvmecontrol/. + */ + const struct args args[] = { + { "opcode", 'o', arg_uint8, &opt.opcode, opcode}, + { "cdw2", '2', arg_uint32, &opt.cdw2, cdw2}, + { "cdw3", '3', arg_uint32, &opt.cdw3, cdw3}, + { "cdw10", '4', arg_uint32, &opt.cdw10, cdw10}, + { "cdw11", '5', arg_uint32, &opt.cdw11, cdw11}, + { "cdw12", '6', arg_uint32, &opt.cdw12, cdw12}, + { "cdw13", '7', arg_uint32, &opt.cdw13, cdw13}, + { "cdw14", '8', arg_uint32, &opt.cdw14, cdw14}, + { "cdw15", '9', arg_uint32, &opt.cdw15, cdw15}, + { "data-len", 'l', arg_uint32, &opt.data_len, data_len}, + { "metadata-len", 'm', arg_uint32, &opt.metadata_len, metadata_len}, + { "flags", 'f', arg_uint8, &opt.flags, flags}, + { "input-file", 'i', arg_path, &opt.ifn, inf}, + { "namespace-id", 'n', arg_uint32, &opt.nsid, nsid}, + { "prefill", 'p', arg_uint8, &opt.prefill, prefill}, + { "rsvd", 'R', arg_uint16, &opt.rsvd, rsvd}, + { "timeout", 't', arg_uint32, &opt.timeout, timeout}, + { "raw-binary", 'b', arg_none, &opt.binary, raw}, + { "dry-run", 'd', arg_none, &opt.dry_run, dry_run}, + { "read", 'r', arg_none, &opt.read, rd}, + { "show-command", 's', arg_none, &opt.show_command, show}, + { "write", 'w', arg_none, &opt.write, wr}, + { NULL, 0, arg_none, NULL, NULL } + }; + + fd = arg_parse_and_open(argc, argv, desc, args); + if (fd == -1) + err(1, "open %s", argv[optind]); + + if (opt.read && opt.write) + errx(1, "need exactly one of --read or --write"); + if (opt.data_len != 0 && !opt.read && !opt.write) + errx(1, "need exactly one of --read or --write"); + if (*opt.ifn && (ifd = open(opt.ifn, O_RDONLY)) == -1) { + warn("open %s", opt.ifn); + goto cleanup; + } +#if notyet /* No support in kernel for this */ + if (opt.metadata_len != 0) { + if (posix_memalign(&metadata, getpagesize(), opt.metadata_len)) { + warn("can't allocate %d bytes for metadata", metadata_len); + goto cleanup; + } + } +#else + if (opt.metadata_len != 0) + errx(1, "metadata not supported on FreeBSD"); +#endif + if (opt.data_len) { + if (posix_memalign(&data, getpagesize(), opt.data_len)) { + warn("can't allocate %d bytes for data", opt.data_len); + goto cleanup; + } + memset(data, opt.prefill, opt.data_len); + if (opt.write && read(ifd, data, opt.data_len) < 0) { + warn("read %s", *opt.ifn ? opt.ifn : "stdin"); + goto cleanup; + } + } + if (opt.show_command) { + fprintf(stderr, "opcode : %#02x\n", opt.opcode); + fprintf(stderr, "flags : %#02x\n", opt.flags); + fprintf(stderr, "rsvd1 : %#04x\n", opt.rsvd); + fprintf(stderr, "nsid : %#04x\n", opt.nsid); + fprintf(stderr, "cdw2 : %#08x\n", opt.cdw2); + fprintf(stderr, "cdw3 : %#08x\n", opt.cdw3); + fprintf(stderr, "data_len : %#08x\n", opt.data_len); + fprintf(stderr, "metadata_len : %#08x\n", opt.metadata_len); + fprintf(stderr, "data : %p\n", data); + fprintf(stderr, "metadata : %p\n", metadata); + fprintf(stderr, "cdw10 : %#08x\n", opt.cdw10); + fprintf(stderr, "cdw11 : %#08x\n", opt.cdw11); + fprintf(stderr, "cdw12 : %#08x\n", opt.cdw12); + fprintf(stderr, "cdw13 : %#08x\n", opt.cdw13); + fprintf(stderr, "cdw14 : %#08x\n", opt.cdw14); + fprintf(stderr, "cdw15 : %#08x\n", opt.cdw15); + fprintf(stderr, "timeout_ms : %d\n", opt.timeout); + } + if (opt.dry_run) { + errno = 0; + goto cleanup; + } + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = opt.opcode; + pt.cmd.fuse = opt.flags; + pt.cmd.cid = htole16(opt.rsvd); + pt.cmd.nsid = opt.nsid; /* XXX note: kernel overrides this */ + pt.cmd.rsvd2 = htole32(opt.cdw2); + pt.cmd.rsvd3 = htole32(opt.cdw3); + pt.cmd.cdw10 = htole32(opt.cdw10); + pt.cmd.cdw11 = htole32(opt.cdw11); + pt.cmd.cdw12 = htole32(opt.cdw12); + pt.cmd.cdw13 = htole32(opt.cdw13); + pt.cmd.cdw14 = htole32(opt.cdw14); + pt.cmd.cdw15 = htole32(opt.cdw15); + pt.buf = data; + pt.len = opt.data_len; + pt.is_read = opt.read; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(1, "passthrough request failed"); + /* XXX report status */ + if (opt.read) { + if (opt.binary) + write(STDOUT_FILENO, data, opt.data_len); + else { + /* print status here */ + print_hex(data, opt.data_len); + } + } +cleanup: + if (errno) + exit(1); +} + +static void +admin_passthru(const struct nvme_function *nf, int argc, char *argv[]) +{ + const char *desc = "Send a pass through Admin command to the specified device\n"; + + passthru(nf, argc, argv, desc); +} + +static void +io_passthru(const struct nvme_function *nf, int argc, char *argv[]) +{ + const char *desc = "Send a pass through I/O command to the specified device\n"; + + passthru(nf, argc, argv, desc); +} + +NVME_COMMAND(top, admin-passthru, admin_passthru, ADMIN_USAGE, ADMIN_ARGS, ADMIN_DESCR); +NVME_COMMAND(top, admin-passthru, io_passthru, IO_USAGE, IO_ARGS, IO_DESCR); Index: sbin/nvmecontrol/perftest.c =================================================================== --- sbin/nvmecontrol/perftest.c +++ sbin/nvmecontrol/perftest.c @@ -50,6 +50,10 @@ " <-s size_in_bytes> <-t time_in_seconds>\n" \ " <-i intr|wait> [-f refthread] [-p]\n" \ " \n" +#define PERFTEST_ARGS \ + "" +#define PERFTEST_DESCR \ + "PErform low-level driver performance testing." static void print_perftest(struct nvme_io_test *io_test, bool perthread) @@ -176,4 +180,4 @@ exit(0); } -NVME_COMMAND(top, perftest, perftest, PERFTEST_USAGE); +NVME_COMMAND(top, perftest, perftest, PERFTEST_USAGE, PERFTEST_ARGS, PERFTEST_DESCR); Index: sbin/nvmecontrol/power.c =================================================================== --- sbin/nvmecontrol/power.c +++ sbin/nvmecontrol/power.c @@ -43,8 +43,12 @@ _Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY, "nvme_power_state size wrong"); -#define POWER_USAGE \ +#define POWER_USAGE \ "power [-l] [-p new-state [-w workload-hint]] \n" +#define POWER_ARGS \ + "" +#define POWER_DESCR \ + "Manage power states for the drive" static void power_list_one(int i, struct nvme_power_state *nps) @@ -188,4 +192,4 @@ exit(0); } -NVME_COMMAND(top, power, power, POWER_USAGE); +NVME_COMMAND(top, power, power, POWER_USAGE, POWER_ARGS, POWER_DESCR); Index: sbin/nvmecontrol/reset.c =================================================================== --- sbin/nvmecontrol/reset.c +++ sbin/nvmecontrol/reset.c @@ -41,8 +41,12 @@ #include "nvmecontrol.h" -#define RESET_USAGE \ +#define RESET_USAGE \ "reset \n" +#define RESET_ARGS \ + "" +#define RESET_DESCR \ + "Perform a controller-level reset." static void reset(const struct nvme_function *nf, int argc, char *argv[]) @@ -67,4 +71,4 @@ exit(0); } -NVME_COMMAND(top, reset, reset, RESET_USAGE); +NVME_COMMAND(top, reset, reset, RESET_USAGE, RESET_ARGS, RESET_DESCR);