Index: head/sbin/nvmecontrol/Makefile =================================================================== --- head/sbin/nvmecontrol/Makefile (revision 333274) +++ head/sbin/nvmecontrol/Makefile (revision 333275) @@ -1,11 +1,11 @@ # $FreeBSD$ PACKAGE=runtime PROG= nvmecontrol SRCS= nvmecontrol.c devlist.c firmware.c format.c identify.c logpage.c \ - perftest.c reset.c nvme_util.c power.c util.c wdc.c + perftest.c reset.c ns.c nvme_util.c power.c util.c wdc.c MAN= nvmecontrol.8 .PATH: ${SRCTOP}/sys/dev/nvme .include Index: head/sbin/nvmecontrol/devlist.c =================================================================== --- head/sbin/nvmecontrol/devlist.c (revision 333274) +++ head/sbin/nvmecontrol/devlist.c (revision 333275) @@ -1,124 +1,126 @@ /*- * 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" static void devlist_usage(void) { fprintf(stderr, "usage:\n"); fprintf(stderr, DEVLIST_USAGE); exit(1); } 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); } void devlist(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; while ((ch = getopt(argc, argv, "")) != -1) { switch ((char)ch) { default: devlist_usage(); } } ctrlr = -1; found = 0; while (1) { ctrlr++; sprintf(name, "%s%d", NVME_CTRLR_PREFIX, ctrlr); ret = open_dev(name, &fd, 0, 0); if (ret != 0) { if (ret == EACCES) { warnx("could not open "_PATH_DEV"%s\n", name); continue; } else break; } 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); } Index: head/sbin/nvmecontrol/ns.c =================================================================== --- head/sbin/nvmecontrol/ns.c (nonexistent) +++ head/sbin/nvmecontrol/ns.c (revision 333275) @@ -0,0 +1,474 @@ +/*- + * 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" + +/* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */ + +#define NSCREATE_USAGE \ +" nvmecontrol ns create -s size [-c cap] [-f fmt] [-m mset] [-n nmic] [-p pi] [-l pil] nvmeN\n" + +#define NSDELETE_USAGE \ +" nvmecontrol ns delete -n nsid nvmeN\n" + +#define NSATTACH_USAGE \ +" nvmecontrol ns attach -n nsid [-c ctrlrid] nvmeN \n" + +#define NSDETACH_USAGE \ +" nvmecontrol ns detach -n nsid [-c ctrlrid] nvmeN\n" + +void nscreate(int argc, char *argv[]); +void nsdelete(int argc, char *argv[]); +void nsattach(int argc, char *argv[]); +void nsdetach(int argc, char *argv[]); + +static struct nvme_function ns_funcs[] = { + {"create", nscreate, NSCREATE_USAGE}, + {"delete", nsdelete, NSDELETE_USAGE}, + {"attach", nsattach, NSATTACH_USAGE}, + {"detach", nsdetach, NSDETACH_USAGE}, + {NULL, NULL, NULL}, +}; + +static void +nscreate_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSCREATE_USAGE); + exit(1); +} + +static void +nsdelete_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSDELETE_USAGE); + exit(1); +} + +static void +nsattach_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSATTACH_USAGE); + exit(1); +} + +static void +nsdetach_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSDETACH_USAGE); + exit(1); +} + +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 + */ +void +nscreate(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; + + if (optind >= argc) + nscreate_usage(); + + 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: + nscreate_usage(); + } + } + + if (optind >= argc) + nscreate_usage(); + + if (cap == -1) + cap = nsze; + if (nsze == -1 || cap == -1) + nscreate_usage(); + + open_dev(argv[optind], &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 << + 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; + nvme_namespace_data_swapbytes(&nsdata); + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc_fuse = NVME_CMD_SET_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); +} + +void +nsdelete(int argc, char *argv[]) +{ + struct nvme_pt_command pt; + struct nvme_controller_data cd; + int ch, fd, result, nsid = -2; + char buf[2]; + + if (optind >= argc) + nsdelete_usage(); + + while ((ch = getopt(argc, argv, "n:")) != -1) { + switch ((char)ch) { + case 'n': + nsid = strtol(optarg, (char **)NULL, 0); + break; + default: + nsdelete_usage(); + } + } + + if (optind >= argc || nsid == -2) + nsdelete_usage(); + + open_dev(argv[optind], &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_fuse = NVME_CMD_SET_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; + + 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 deletion failed: %s", + get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & + NVME_STATUS_SC_MASK)); + } + printf("namespace %d deleted\n", 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. + */ +void +nsattach(int argc, char *argv[]) +{ + struct nvme_pt_command pt; + struct nvme_controller_data cd; + int ctrlrid = -2; + int fd, ch, result, nsid = -1; + uint16_t clist[2048]; + + if (optind >= argc) + nsattach_usage(); + + 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: + nsattach_usage(); + } + } + + if (optind >= argc) + nsattach_usage(); + + if (nsid == -1 ) + nsattach_usage(); + + open_dev(argv[optind], &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) { + /* Get full list of controllers to attach to. */ + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc_fuse = NVME_CMD_SET_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; + memset(&clist, 0, sizeof(clist)); + clist[0] = htole16(1); + clist[1] = htole16(ctrlrid); + } + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc_fuse = NVME_CMD_SET_OPC(NVME_OPC_NAMESPACE_ATTACHMENT); + pt.cmd.cdw10 = 0; /* attach */ + pt.cmd.nsid = (uint32_t)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 attach failed: %s", + get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & + NVME_STATUS_SC_MASK)); + } + printf("namespace %d attached\n", nsid); + exit(0); +} + +void +nsdetach(int argc, char *argv[]) +{ + struct nvme_pt_command pt; + struct nvme_controller_data cd; + int ctrlrid = -2; + int fd, ch, result, nsid = -1; + uint16_t clist[2048]; + + if (optind >= argc) + nsdetach_usage(); + + 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: + nsdetach_usage(); + } + } + + if (optind >= argc) + nsdetach_usage(); + + if (nsid == -1) + nsdetach_usage(); + + open_dev(argv[optind], &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) { + /* Get list of controllers this namespace attached to. */ + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc_fuse = NVME_CMD_SET_OPC(NVME_OPC_IDENTIFY); + pt.cmd.nsid = htole32(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; + memset(&clist, 0, sizeof(clist)); + clist[0] = htole16(1); + clist[1] = htole16(ctrlrid); + } + } else { + /* By default detach from this controller. */ + if (ctrlrid == -2) + ctrlrid = cd.ctrlr_id; + memset(&clist, 0, sizeof(clist)); + clist[0] = htole16(1); + clist[1] = htole16(ctrlrid); + } + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc_fuse = NVME_CMD_SET_OPC(NVME_OPC_NAMESPACE_ATTACHMENT); + pt.cmd.cdw10 = 1; /* detach */ + pt.cmd.nsid = (uint32_t)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); + exit(0); +} + +void +ns(int argc, char *argv[]) +{ + + dispatch(argc, argv, ns_funcs); +} Property changes on: head/sbin/nvmecontrol/ns.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: head/sbin/nvmecontrol/nvmecontrol.c =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.c (revision 333274) +++ head/sbin/nvmecontrol/nvmecontrol.c (revision 333275) @@ -1,250 +1,251 @@ /*- * 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 "nvmecontrol.h" static struct nvme_function funcs[] = { {"devlist", devlist, DEVLIST_USAGE}, {"identify", identify, IDENTIFY_USAGE}, {"perftest", perftest, PERFTEST_USAGE}, {"reset", reset, RESET_USAGE}, {"logpage", logpage, LOGPAGE_USAGE}, {"firmware", firmware, FIRMWARE_USAGE}, {"format", format, FORMAT_USAGE}, {"power", power, POWER_USAGE}, {"wdc", wdc, WDC_USAGE}, + {"ns", ns, NS_USAGE}, {NULL, NULL, NULL}, }; void gen_usage(struct nvme_function *f) { fprintf(stderr, "usage:\n"); while (f->name != NULL) { fprintf(stderr, "%s", f->usage); f++; } exit(1); } void dispatch(int argc, char *argv[], struct nvme_function *tbl) { struct nvme_function *f = tbl; if (argv[1] == NULL) { gen_usage(tbl); return; } while (f->name != NULL) { if (strcmp(argv[1], f->name) == 0) f->fn(argc-1, &argv[1]); f++; } fprintf(stderr, "Unknown command: %s\n", argv[1]); gen_usage(tbl); } 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_fuse = NVME_CMD_SET_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_fuse = NVME_CMD_SET_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); } int main(int argc, char *argv[]) { if (argc < 2) gen_usage(funcs); dispatch(argc, argv, funcs); return (0); } Index: head/sbin/nvmecontrol/nvmecontrol.h =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.h (revision 333274) +++ head/sbin/nvmecontrol/nvmecontrol.h (revision 333275) @@ -1,120 +1,124 @@ /*- * 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 typedef void (*nvme_fn_t)(int argc, char *argv[]); struct nvme_function { const char *name; nvme_fn_t fn; const char *usage; }; #define NVME_CTRLR_PREFIX "nvme" #define NVME_NS_PREFIX "ns" #define DEVLIST_USAGE \ " nvmecontrol devlist\n" #define IDENTIFY_USAGE \ " nvmecontrol identify [-x [-v]] \n" #define PERFTEST_USAGE \ " nvmecontrol 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" #define RESET_USAGE \ " nvmecontrol reset \n" #define LOGPAGE_USAGE \ " nvmecontrol logpage <-p page_id> [-b] [-v vendor] [-x] \n" \ #define FIRMWARE_USAGE \ " nvmecontrol firmware [-s slot] [-f path_to_firmware] [-a] \n" #define FORMAT_USAGE \ " nvmecontrol format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] \n" #define POWER_USAGE \ " nvmecontrol power [-l] [-p new-state [-w workload-hint]] \n" #define WDC_USAGE \ " nvmecontrol wdc (cap-diag|drive-log|get-crash-dump|purge|purge-montior)\n" +#define NS_USAGE \ +" nvmecontrol ns (create|delete|attach|detach)\n" + void devlist(int argc, char *argv[]); void identify(int argc, char *argv[]); void perftest(int argc, char *argv[]); void reset(int argc, char *argv[]); void logpage(int argc, char *argv[]); void firmware(int argc, char *argv[]); void format(int argc, char *argv[]); void power(int argc, char *argv[]); void wdc(int argc, char *argv[]); +void ns(int argc, char *argv[]); 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 gen_usage(struct nvme_function *); void dispatch(int argc, char *argv[], struct nvme_function *f); /* 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