Module Name: src Committed By: nonaka Date: Sat Jun 4 16:29:35 UTC 2016
Modified Files: src/distrib/sets/lists/base: mi src/distrib/sets/lists/debug: mi src/distrib/sets/lists/man: mi src/sbin: Makefile src/sys/dev/ic: nvmereg.h Added Files: src/sbin/nvmectl: Makefile devlist.c firmware.c identify.c logpage.c nvme.h nvmectl.8 nvmectl.c nvmectl.h perftest.c power.c reset.c Log Message: nvmectl(8): Added NVM Express control utility. Ported from FreeBSD nvmecontrol(8). To generate a diff of this commit: cvs rdiff -u -r1.1126 -r1.1127 src/distrib/sets/lists/base/mi cvs rdiff -u -r1.154 -r1.155 src/distrib/sets/lists/debug/mi cvs rdiff -u -r1.1525 -r1.1526 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.127 -r1.128 src/sbin/Makefile cvs rdiff -u -r0 -r1.1 src/sbin/nvmectl/Makefile src/sbin/nvmectl/devlist.c \ src/sbin/nvmectl/firmware.c src/sbin/nvmectl/identify.c \ src/sbin/nvmectl/logpage.c src/sbin/nvmectl/nvme.h \ src/sbin/nvmectl/nvmectl.8 src/sbin/nvmectl/nvmectl.c \ src/sbin/nvmectl/nvmectl.h src/sbin/nvmectl/perftest.c \ src/sbin/nvmectl/power.c src/sbin/nvmectl/reset.c cvs rdiff -u -r1.2 -r1.3 src/sys/dev/ic/nvmereg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/base/mi diff -u src/distrib/sets/lists/base/mi:1.1126 src/distrib/sets/lists/base/mi:1.1127 --- src/distrib/sets/lists/base/mi:1.1126 Sat Jun 4 15:27:11 2016 +++ src/distrib/sets/lists/base/mi Sat Jun 4 16:29:35 2016 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1126 2016/06/04 15:27:11 agc Exp $ +# $NetBSD: mi,v 1.1127 2016/06/04 16:29:35 nonaka Exp $ # # Note: Don't delete entries from here - mark them as "obsolete" instead, # unless otherwise stated below. @@ -527,6 +527,7 @@ ./sbin/nfsiod base-obsolete obsolete ./sbin/nologin base-sysutil-root ./sbin/npfctl base-npf-bin npf +./sbin/nvmectl base-sysutil-root ./sbin/pdisk base-sysutil-root ./sbin/pfctl base-pf-root pf ./sbin/pflogd base-pf-root pf Index: src/distrib/sets/lists/debug/mi diff -u src/distrib/sets/lists/debug/mi:1.154 src/distrib/sets/lists/debug/mi:1.155 --- src/distrib/sets/lists/debug/mi:1.154 Wed Jun 1 23:59:21 2016 +++ src/distrib/sets/lists/debug/mi Sat Jun 4 16:29:35 2016 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.154 2016/06/01 23:59:21 joerg Exp $ +# $NetBSD: mi,v 1.155 2016/06/04 16:29:35 nonaka Exp $ ./etc/mtree/set.debug comp-sys-root ./usr/lib comp-sys-usr compatdir ./usr/lib/i18n/libBIG5_g.a comp-c-debuglib debuglib,compatfile @@ -377,6 +377,7 @@ ./usr/libdata/debug/sbin/newfs_udf.debug comp-sysutil-debug debug ./usr/libdata/debug/sbin/newfs_v7fs.debug comp-sysutil-debug debug ./usr/libdata/debug/sbin/npfctl.debug comp-npf-debug npf,debug +./usr/libdata/debug/sbin/nvmectl.debug comp-sysutil-debug debug ./usr/libdata/debug/sbin/pdisk.debug comp-sysutil-debug debug ./usr/libdata/debug/sbin/pfctl.debug comp-pf-debug pf,debug ./usr/libdata/debug/sbin/pflogd.debug comp-pf-debug pf,debug Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1525 src/distrib/sets/lists/man/mi:1.1526 --- src/distrib/sets/lists/man/mi:1.1525 Sat Jun 4 15:27:11 2016 +++ src/distrib/sets/lists/man/mi Sat Jun 4 16:29:35 2016 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1525 2016/06/04 15:27:11 agc Exp $ +# $NetBSD: mi,v 1.1526 2016/06/04 16:29:35 nonaka Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -2779,6 +2779,7 @@ ./usr/share/man/cat8/ntpq.0 man-ntp-catman .cat ./usr/share/man/cat8/ntptime.0 man-ntp-catman .cat ./usr/share/man/cat8/ntptrace.0 man-ntp-catman .cat +./usr/share/man/cat8/nvmectl.0 man-sysutil-catman .cat ./usr/share/man/cat8/ofctl.0 man-sysutil-catman .cat ./usr/share/man/cat8/ofppc/MAKEDEV.0 man-obsolete obsolete ./usr/share/man/cat8/ofppc/makedev.0 man-obsolete obsolete @@ -5630,6 +5631,7 @@ ./usr/share/man/html8/ntpq.html man-ntp-htmlman html ./usr/share/man/html8/ntptime.html man-ntp-htmlman html ./usr/share/man/html8/ntptrace.html man-ntp-htmlman html +./usr/share/man/html8/nvmectl.html man-sysutil-htmlman html ./usr/share/man/html8/ofctl.html man-sysutil-htmlman html ./usr/share/man/html8/oqmgr.html man-postfix-htmlman postfix,html ./usr/share/man/html8/pac.html man-sysutil-htmlman html @@ -8705,6 +8707,7 @@ ./usr/share/man/man8/ntpq.8 man-ntp-man .man ./usr/share/man/man8/ntptime.8 man-ntp-man .man ./usr/share/man/man8/ntptrace.8 man-ntp-man .man +./usr/share/man/man8/nvmectl.8 man-sysutil-man .man ./usr/share/man/man8/ofctl.8 man-sysutil-man .man ./usr/share/man/man8/ofppc/MAKEDEV.8 man-obsolete obsolete ./usr/share/man/man8/ofppc/makedev.8 man-obsolete obsolete Index: src/sbin/Makefile diff -u src/sbin/Makefile:1.127 src/sbin/Makefile:1.128 --- src/sbin/Makefile:1.127 Thu Sep 11 13:10:03 2014 +++ src/sbin/Makefile Sat Jun 4 16:29:35 2016 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.127 2014/09/11 13:10:03 roy Exp $ +# $NetBSD: Makefile,v 1.128 2016/06/04 16:29:35 nonaka Exp $ # @(#)Makefile 8.5 (Berkeley) 3/31/94 # Not ported: XNSrouted enpload scsiformat startslip @@ -9,7 +9,8 @@ SUBDIR= amrctl apmlabel atactl badsect bioctl brconfig ccdconfig \ chown devpubd disklabel dkctl dkscan_bsdlabel dmesg dmctl \ drvctl fastboot fdisk fsck fsirand gpt ifconfig init ldconfig luactl \ - mbrlabel mknod modload modstat modunload mount newbtconf nologin \ + mbrlabel mknod modload modstat modunload mount \ + newbtconf nologin nvmectl \ ping pppoectl raidctl reboot rcorder rndctl route routed \ savecore scan_ffs scsictl shutdown slattach svhlabel swapctl sysctl \ ttyflags umount veriexecctl wdogctl wsconsctl Index: src/sys/dev/ic/nvmereg.h diff -u src/sys/dev/ic/nvmereg.h:1.2 src/sys/dev/ic/nvmereg.h:1.3 --- src/sys/dev/ic/nvmereg.h:1.2 Sat Jun 4 16:11:51 2016 +++ src/sys/dev/ic/nvmereg.h Sat Jun 4 16:29:35 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: nvmereg.h,v 1.2 2016/06/04 16:11:51 nonaka Exp $ */ +/* $NetBSD: nvmereg.h,v 1.3 2016/06/04 16:29:35 nonaka Exp $ */ /* $OpenBSD: nvmereg.h,v 1.10 2016/04/14 11:18:32 dlg Exp $ */ /* @@ -211,12 +211,15 @@ struct nvme_cqe { uint16_t flags; #define NVME_CQE_DNR __BIT(15) #define NVME_CQE_M __BIT(14) +#define NVME_CQE_SCT_MASK __BITS(8, 10) #define NVME_CQE_SCT(_f) ((_f) & (0x07 << 8)) #define NVME_CQE_SCT_GENERIC (0x00 << 8) #define NVME_CQE_SCT_COMMAND (0x01 << 8) #define NVME_CQE_SCT_MEDIAERR (0x02 << 8) #define NVME_CQE_SCT_VENDOR (0x07 << 8) +#define NVME_CQE_SC_MASK __BITS(1, 7) #define NVME_CQE_SC(_f) ((_f) & (0x7f << 1)) +/* generic command status codes */ #define NVME_CQE_SC_SUCCESS (0x00 << 1) #define NVME_CQE_SC_INVALID_OPCODE (0x01 << 1) #define NVME_CQE_SC_INVALID_FIELD (0x02 << 1) @@ -237,8 +240,36 @@ struct nvme_cqe { #define NVME_CQE_SC_SGL_TYPE_INVALID (0x11 << 1) #define NVME_CQE_SC_LBA_RANGE (0x80 << 1) #define NVME_CQE_SC_CAP_EXCEEDED (0x81 << 1) -#define NVME_CQE_NS_NOT_RDY (0x82 << 1) -#define NVME_CQE_RSV_CONFLICT (0x83 << 1) +#define NVME_CQE_SC_NS_NOT_RDY (0x82 << 1) +#define NVME_CQE_SC_RSV_CONFLICT (0x83 << 1) +/* command specific status codes */ +#define NVME_CQE_SC_CQE_INVALID (0x00 << 1) +#define NVME_CQE_SC_INVALID_QID (0x01 << 1) +#define NVME_CQE_SC_MAX_Q_SIZE (0x02 << 1) +#define NVME_CQE_SC_ABORT_LIMIT (0x03 << 1) +#define NVME_CQE_SC_ASYNC_EV_REQ_LIMIT (0x05 << 1) +#define NVME_CQE_SC_INVALID_FW_SLOT (0x06 << 1) +#define NVME_CQE_SC_INVALID_FW_IMAGE (0x07 << 1) +#define NVME_CQE_SC_INVALID_INT_VEC (0x08 << 1) +#define NVME_CQE_SC_INVALID_LOG_PAGE (0x09 << 1) +#define NVME_CQE_SC_INVALID_FORMAT (0x0a << 1) +#define NVME_CQE_SC_FW_REQ_CNV_RESET (0x0b << 1) +#define NVME_CQE_SC_FW_REQ_NVM_RESET (0x10 << 1) +#define NVME_CQE_SC_FW_REQ_RESET (0x11 << 1) +#define NVME_CQE_SC_FW_MAX_TIME_VIO (0x12 << 1) +#define NVME_CQE_SC_FW_PROHIBIT (0x13 << 1) +#define NVME_CQE_SC_OVERLAP_RANGE (0x14 << 1) +#define NVME_CQE_SC_CONFLICT_ATTRS (0x80 << 1) +#define NVME_CQE_SC_INVALID_PROT_INFO (0x81 << 1) +#define NVME_CQE_SC_ATT_WR_TO_RO_PAGE (0x82 << 1) +/* media error status codes */ +#define NVME_CQE_SC_WRITE_FAULTS (0x80 << 1) +#define NVME_CQE_SC_UNRECV_READ_ERR (0x81 << 1) +#define NVME_CQE_SC_GUARD_CHECK_ERR (0x82 << 1) +#define NVME_CQE_SC_APPL_TAG_CHECK_ERR (0x83 << 1) +#define NVME_CQE_SC_REF_TAG_CHECK_ERR (0x84 << 1) +#define NVME_CQE_SC_CMP_FAIL (0x85 << 1) +#define NVME_CQE_SC_ACCESS_DENIED (0x86 << 1) #define NVME_CQE_PHASE __BIT(0) } __packed __aligned(8); @@ -252,7 +283,7 @@ struct nvme_cqe { #define NVM_ADMIN_SET_FEATURES 0x09 /* Set Features */ #define NVM_ADMIN_GET_FEATURES 0x0a /* Get Features */ #define NVM_ADMIN_ASYNC_EV_REQ 0x0c /* Asynchronous Event Request */ -#define NVM_ADMIN_FW_ACTIVATE 0x10 /* Firmware Activate */ +#define NVM_ADMIN_FW_COMMIT 0x10 /* Firmware Commit */ #define NVM_ADMIN_FW_DOWNLOAD 0x11 /* Firmware Image Download */ #define NVM_CMD_FLUSH 0x00 /* Flush */ @@ -265,18 +296,34 @@ struct nvme_cqe { /* Power State Descriptor Data */ struct nvm_identify_psd { uint16_t mp; /* Max Power */ - uint16_t flags; + uint8_t _reserved1; + uint8_t flags; +#define NVME_PSD_NOPS __BIT(1) +#define NVME_PSD_MPS __BIT(0) uint32_t enlat; /* Entry Latency */ uint32_t exlat; /* Exit Latency */ uint8_t rrt; /* Relative Read Throughput */ +#define NVME_PSD_RRT_MASK __BITS(0, 4) uint8_t rrl; /* Relative Read Latency */ +#define NVME_PSD_RRL_MASK __BITS(0, 4) uint8_t rwt; /* Relative Write Throughput */ +#define NVME_PSD_RWT_MASK __BITS(0, 4) uint8_t rwl; /* Relative Write Latency */ +#define NVME_PSD_RWL_MASK __BITS(0, 4) - uint8_t _reserved[16]; + uint16_t idlp; /* Idle Power */ + uint8_t ips; /* Idle Power Scale */ +#define NVME_PSD_IPS_MASK __BITS(0, 1) + uint8_t _reserved2; + uint16_t actp; /* Active Power */ + uint16_t ap; /* Active Power Workload/Scale */ +#define NVME_PSD_APW_MASK __BITS(0, 2) +#define NVME_PSD_APS_MASK __BITS(6, 7) + + uint8_t _reserved[8]; } __packed __aligned(8); struct nvm_identify_controller { @@ -302,11 +349,20 @@ struct nvm_identify_controller { /* Admin Command Set Attributes & Optional Controller Capabilities */ uint16_t oacs; /* Optional Admin Command Support */ +#define NVME_ID_CTRLR_OACS_NS __BIT(3) +#define NVME_ID_CTRLR_OACS_FW __BIT(2) +#define NVME_ID_CTRLR_OACS_FORMAT __BIT(1) +#define NVME_ID_CTRLR_OACS_SECURITY __BIT(0) uint8_t acl; /* Abort Command Limit */ uint8_t aerl; /* Asynchronous Event Request Limit */ uint8_t frmw; /* Firmware Updates */ +#define NVME_ID_CTRLR_FRMW_NOREQ_RESET __BIT(4) +#define NVME_ID_CTRLR_FRMW_NSLOT __BITS(1, 3) +#define NVME_ID_CTRLR_FRMW_SLOT1_RO __BIT(0) uint8_t lpa; /* Log Page Attributes */ +#define NVME_ID_CTRLR_LPA_CMD_EFFECT __BIT(1) +#define NVME_ID_CTRLR_LPA_NS_SMART __BIT(0) uint8_t elpe; /* Error Log Page Entries */ uint8_t npss; /* Number of Power States Support */ @@ -320,16 +376,27 @@ struct nvm_identify_controller { /* NVM Command Set Attributes */ uint8_t sqes; /* Submission Queue Entry Size */ +#define NVME_ID_CTRLR_SQES_MAX __BITS(4, 7) +#define NVME_ID_CTRLR_SQES_MIN __BITS(0, 3) uint8_t cqes; /* Completion Queue Entry Size */ +#define NVME_ID_CTRLR_CQES_MAX __BITS(4, 7) +#define NVME_ID_CTRLR_CQES_MIN __BITS(0, 3) uint8_t _reserved3[2]; uint32_t nn; /* Number of Namespaces */ uint16_t oncs; /* Optional NVM Command Support */ +#define NVME_ID_CTRLR_ONCS_RESERVATION __BIT(5) +#define NVME_ID_CTRLR_ONCS_SET_FEATURES __BIT(4) +#define NVME_ID_CTRLR_ONCS_WRITE_ZERO __BIT(3) +#define NVME_ID_CTRLR_ONCS_DSM __BIT(2) +#define NVME_ID_CTRLR_ONCS_WRITE_UNC __BIT(1) +#define NVME_ID_CTRLR_ONCS_COMPARE __BIT(0) uint16_t fuses; /* Fused Operation Support */ uint8_t fna; /* Format NVM Attributes */ uint8_t vwc; /* Volatile Write Cache */ +#define NVME_ID_CTRLR_VWC_PRESENT __BIT(0) uint16_t awun; /* Atomic Write Unit Normal */ uint16_t awupf; /* Atomic Write Unit Power Fail */ @@ -370,6 +437,9 @@ struct nvm_identify_namespace { uint64_t nuse; /* Namespace Utilization */ uint8_t nsfeat; /* Namespace Features */ +#define NVME_ID_NS_NSFEAT_LOGICAL_BLK_ERR __BIT(2) +#define NVME_ID_NS_NSFEAT_NS __BIT(1) +#define NVME_ID_NS_NSFEAT_THIN_PROV __BIT(0) uint8_t nlbaf; /* Number of LBA Formats */ uint8_t flbas; /* Formatted LBA Size */ #define NVME_ID_NS_FLBAS(_f) ((_f) & 0x0f) Added files: Index: src/sbin/nvmectl/Makefile diff -u /dev/null src/sbin/nvmectl/Makefile:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/Makefile Sat Jun 4 16:29:35 2016 @@ -0,0 +1,19 @@ +# $NetBSD: Makefile,v 1.1 2016/06/04 16:29:35 nonaka Exp $ + +.include <bsd.own.mk> + +PROG= nvmectl +SRCS= nvmectl.c +SRCS+= devlist.c +SRCS+= firmware.c +SRCS+= identify.c +SRCS+= logpage.c +SRCS+= perftest.c +SRCS+= power.c +SRCS+= reset.c +MAN= nvmectl.8 + +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +.include <bsd.prog.mk> Index: src/sbin/nvmectl/devlist.c diff -u /dev/null src/sbin/nvmectl/devlist.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/devlist.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,123 @@ +/* $NetBSD: devlist.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: devlist.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/devlist.c 260381 2014-01-06 23:48:47Z jimharris $"); +#endif +#endif + +#include <sys/param.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.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 nvm_identify_namespace *nsdata) +{ + + return 1 << nsdata->lbaf[NVME_ID_NS_FLBAS(nsdata->flbas)].lbads; +} + +void +devlist(int argc, char *argv[]) +{ + struct nvm_identify_controller cdata; + struct nvm_identify_namespace nsdata; + char name[64]; + uint8_t mn[64]; + uint32_t i; + int ch, ctrlr, fd, found, ret; + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (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, sizeof(mn), cdata.mn, sizeof(cdata.mn)); + 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); + 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: src/sbin/nvmectl/firmware.c diff -u /dev/null src/sbin/nvmectl/firmware.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/firmware.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,333 @@ +/* $NetBSD: firmware.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: firmware.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/firmware.c 258071 2013-11-12 21:14:19Z jimharris $"); +#endif +#endif + +#include <sys/param.h> +#include <sys/ioccom.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.h" + +#ifdef FIRMWARE_USAGE +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, 0xffffffff, &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) +{ + 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 = malloc(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.opcode = NVM_ADMIN_FW_DOWNLOAD; + pt.cmd.cdw10 = (size / sizeof(uint32_t)) - 1; + pt.cmd.cdw11 = (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 commit_action) +{ + struct nvme_pt_command pt; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opcode = NVM_ADMIN_FW_COMMIT; + pt.cmd.cdw10 = (commit_action << 3) | slot; + pt.is_read = 0; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(1, "firmware activate request failed"); + + if (NVME_CQE_SCT(pt.cpl.flags) == NVME_CQE_SCT_COMMAND && + NVME_CQE_SC(pt.cpl.flags) == NVME_CQE_SC_FW_REQ_RESET) + return 1; + + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "firmware activate request returned error"); + + return 0; +} + +static void +firmware_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, FIRMWARE_USAGE); + exit(1); +} + +void +firmware(int argc, char *argv[]) +{ + u_int slot = 0; + int fd = -1; + int a_flag, s_flag, f_flag; + int commit_action, reboot_required; + int ch, + char *p, *image = NULL; + char *controller = NULL, prompt[64]; + void *buf = NULL; + int32_t size = 0; + struct nvm_identify_controller cdata; + + a_flag = s_flag = f_flag = false; + + while ((ch = getopt(argc, argv, "af:s:")) != -1) { + switch (ch) { + 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); + firmware_usage(); + } else if (slot == 0) { + fprintf(stderr, + "0 is not a valid slot number. " + "Slot numbers start at 1.\n"); + firmware_usage(); + } else if (slot > 7) { + fprintf(stderr, + "Slot number %s specified which is " + "greater than max allowed slot number of " + "7.\n", optarg); + firmware_usage(); + } + s_flag = true; + break; + case 'f': + image = optarg; + f_flag = true; + break; + } + } + + /* Check that a controller (and not a namespace) was specified. */ + if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) != NULL) + firmware_usage(); + + if (!f_flag && !a_flag) { + fprintf(stderr, + "Neither a replace ([-f path_to_firmware]) nor " + "activate ([-a]) firmware image action\n" + "was specified.\n"); + firmware_usage(); + } + + if (!f_flag && a_flag && slot == 0) { + fprintf(stderr, + "Slot number to activate not specified.\n"); + firmware_usage(); + } + + controller = argv[optind]; + open_dev(controller, &fd, 1, 1); + read_controller_data(fd, &cdata); + + if ((cdata.oacs & NVME_ID_CTRLR_OACS_FW) == 0) + errx(1, + "controller does not support firmware activate/download"); + + if (f_flag && slot == 1 && (cdata.frmw & NVME_ID_CTRLR_FRMW_SLOT1_RO)) + errx(1, "slot %d is marked as read only", slot); + + if (slot > __SHIFTOUT(cdata.frmw, NVME_ID_CTRLR_FRMW_NSLOT)) + errx(1, + "slot %d specified but controller only supports %d slots", + slot, + (uint8_t)__SHIFTOUT(cdata.frmw, NVME_ID_CTRLR_FRMW_NSLOT)); + + if (a_flag && !f_flag && !slot_has_valid_firmware(fd, 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); + + if (f_flag) + read_image_file(image, &buf, &size); + + if (f_flag && a_flag) + 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) + 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) + 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); + + 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) { + update_firmware(fd, buf, size); + if (a_flag) + commit_action = NVME_COMMIT_ACTION_REPLACE_ACTIVATE; + else + commit_action = NVME_COMMIT_ACTION_REPLACE_NO_ACTIVATE; + } else { + commit_action = NVME_COMMIT_ACTION_ACTIVATE_RESET; + } + + reboot_required = activate_firmware(fd, slot, commit_action); + + if (a_flag) { + 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); + } + } + + close(fd); + exit(0); +} +#endif Index: src/sbin/nvmectl/identify.c diff -u /dev/null src/sbin/nvmectl/identify.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/identify.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,314 @@ +/* $NetBSD: identify.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: identify.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/identify.c 253476 2013-07-19 21:40:57Z jimharris $"); +#endif +#endif + +#include <sys/param.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.h" + +static void +print_controller(struct nvm_identify_controller *cdata) +{ + uint8_t str[128]; + + printf("Controller Capabilities/Features\n"); + printf("================================\n"); + printf("Vendor ID: %04x\n", cdata->vid); + printf("Subsystem Vendor ID: %04x\n", cdata->ssvid); + nvme_strvis(str, sizeof(str), cdata->sn, sizeof(cdata->sn)); + printf("Serial Number: %s\n", str); + nvme_strvis(str, sizeof(str), cdata->mn, sizeof(cdata->mn)); + printf("Model Number: %s\n", str); + nvme_strvis(str, sizeof(str), cdata->fr, sizeof(cdata->fr)); + printf("Firmware Version: %s\n", str); + printf("Recommended Arb Burst: %d\n", cdata->rab); + printf("IEEE OUI Identifier: %02x %02x %02x\n", + cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]); + printf("Multi-Interface Cap: %02x\n", cdata->cmic); + /* TODO: Use CAP.MPSMIN to determine true memory page size. */ + printf("Max Data Transfer Size: "); + if (cdata->mdts == 0) + printf("Unlimited\n"); + else + printf("%ld\n", sysconf(_SC_PAGESIZE) * (1 << cdata->mdts)); + printf("\n"); + + printf("Admin Command Set Attributes\n"); + printf("============================\n"); + printf("Security Send/Receive: %s\n", + (cdata->oacs & NVME_ID_CTRLR_OACS_SECURITY) ? + "Supported" : "Not Supported"); + printf("Format NVM: %s\n", + (cdata->oacs & NVME_ID_CTRLR_OACS_FORMAT) ? + "Supported" : "Not Supported"); + printf("Firmware Activate/Download: %s\n", + (cdata->oacs & NVME_ID_CTRLR_OACS_FW) ? + "Supported" : "Not Supported"); + printf("Abort Command Limit: %d\n", cdata->acl+1); + printf("Async Event Request Limit: %d\n", cdata->aerl+1); + printf("Number of Firmware Slots: "); + if (cdata->oacs & NVME_ID_CTRLR_OACS_FW) + printf("%d\n", + (uint8_t)__SHIFTOUT(cdata->frmw, NVME_ID_CTRLR_FRMW_NSLOT)); + else + printf("N/A\n"); + printf("Firmware Slot 1 Read-Only: "); + if (cdata->oacs & NVME_ID_CTRLR_OACS_FW) + printf("%s\n", (cdata->frmw & NVME_ID_CTRLR_FRMW_SLOT1_RO) ? + "Yes" : "No"); + else + printf("N/A\n"); + printf("Per-Namespace SMART Log: %s\n", + (cdata->lpa & NVME_ID_CTRLR_LPA_NS_SMART) ? "Yes" : "No"); + printf("Error Log Page Entries: %d\n", cdata->elpe+1); + printf("Number of Power States: %d\n", cdata->npss+1); + printf("\n"); + + printf("NVM Command Set Attributes\n"); + printf("==========================\n"); + printf("Submission Queue Entry Size\n"); + printf(" Max: %d\n", + 1 << __SHIFTOUT(cdata->sqes, NVME_ID_CTRLR_SQES_MAX)); + printf(" Min: %d\n", + 1 << __SHIFTOUT(cdata->sqes, NVME_ID_CTRLR_SQES_MIN)); + printf("Completion Queue Entry Size\n"); + printf(" Max: %d\n", + 1 << __SHIFTOUT(cdata->cqes, NVME_ID_CTRLR_CQES_MAX)); + printf(" Min: %d\n", + 1 << __SHIFTOUT(cdata->cqes, NVME_ID_CTRLR_CQES_MIN)); + printf("Number of Namespaces: %d\n", cdata->nn); + printf("Compare Command: %s\n", + (cdata->oncs & NVME_ID_CTRLR_ONCS_COMPARE) ? + "Supported" : "Not Supported"); + printf("Write Uncorrectable Command: %s\n", + (cdata->oncs & NVME_ID_CTRLR_ONCS_WRITE_UNC) ? + "Supported" : "Not Supported"); + printf("Dataset Management Command: %s\n", + (cdata->oncs & NVME_ID_CTRLR_ONCS_DSM) ? + "Supported" : "Not Supported"); + printf("Write Zeroes Command: %s\n", + (cdata->oncs & NVME_ID_CTRLR_ONCS_WRITE_ZERO) ? + "Supported" : "Not Supported"); + printf("Set Features Command: %s\n", + (cdata->oncs & NVME_ID_CTRLR_ONCS_SET_FEATURES) ? + "Supported" : "Not Supported"); + printf("Reservation: %s\n", + (cdata->oncs & NVME_ID_CTRLR_ONCS_RESERVATION) ? + "Supported" : "Not Supported"); + printf("Volatile Write Cache: %s\n", + (cdata->vwc & NVME_ID_CTRLR_VWC_PRESENT) ? + "Present" : "Not Present"); +} + +static void +print_namespace(struct nvm_identify_namespace *nsdata) +{ + uint32_t i; + + 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", + (nsdata->nsfeat & NVME_ID_NS_NSFEAT_THIN_PROV) ? + "Supported" : "Not Supported"); + printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1); + printf("Current LBA Format: LBA Format #%02d\n", + NVME_ID_NS_FLBAS(nsdata->flbas)); + for (i = 0; i <= nsdata->nlbaf; i++) + printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n", + i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms); +} + +static void +identify_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, IDENTIFY_USAGE); + exit(1); +} + +static void +identify_ctrlr(int argc, char *argv[]) +{ + struct nvm_identify_controller cdata; + int ch, fd, hexflag = 0, hexlength; + int verboseflag = 0; + + while ((ch = getopt(argc, argv, "vx")) != -1) { + switch (ch) { + case 'v': + verboseflag = 1; + break; + case 'x': + hexflag = 1; + break; + default: + identify_usage(); + } + } + + /* Check that a controller was specified. */ + if (optind >= argc) + identify_usage(); + + open_dev(argv[optind], &fd, 1, 1); + read_controller_data(fd, &cdata); + close(fd); + + if (hexflag == 1) { + if (verboseflag == 1) + hexlength = sizeof(struct nvm_identify_controller); + else + hexlength = offsetof(struct nvm_identify_controller, + _reserved7); + print_hex(&cdata, hexlength); + exit(0); + } + + if (verboseflag == 1) { + fprintf(stderr, "-v not currently supported without -x\n"); + identify_usage(); + } + + print_controller(&cdata); + exit(0); +} + +static void +identify_ns(int argc, char *argv[]) +{ + struct nvm_identify_namespace nsdata; + char path[64]; + int ch, fd, hexflag = 0, hexlength, nsid; + int verboseflag = 0; + + while ((ch = getopt(argc, argv, "vx")) != -1) { + switch (ch) { + case 'v': + verboseflag = 1; + break; + case 'x': + hexflag = 1; + break; + default: + identify_usage(); + } + } + + /* Check that a namespace was specified. */ + if (optind >= argc) + identify_usage(); + + /* + * 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); + 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); + open_dev(path, &fd, 1, 1); + read_namespace_data(fd, nsid, &nsdata); + close(fd); + + if (hexflag == 1) { + if (verboseflag == 1) + hexlength = sizeof(struct nvm_identify_namespace); + else + hexlength = offsetof(struct nvm_identify_namespace, + _reserved2); + print_hex(&nsdata, hexlength); + exit(0); + } + + if (verboseflag == 1) { + fprintf(stderr, "-v not currently supported without -x\n"); + identify_usage(); + } + + print_namespace(&nsdata); + exit(0); +} + +void +identify(int argc, char *argv[]) +{ + char *target; + + if (argc < 2) + identify_usage(); + + while (getopt(argc, argv, "vx") != -1) ; + + /* Check that a controller or namespace was specified. */ + if (optind >= argc) + identify_usage(); + + 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(argc, argv); + else + identify_ns(argc, argv); +} Index: src/sbin/nvmectl/logpage.c diff -u /dev/null src/sbin/nvmectl/logpage.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/logpage.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,377 @@ +/* $NetBSD: logpage.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: logpage.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 285796 2015-07-22 16:10:29Z jimharris $"); +#endif +#endif + +#include <sys/param.h> +#include <sys/ioccom.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.h" + +#define DEFAULT_SIZE (4096) +#define MAX_FW_SLOTS (7) + +typedef void (*print_fn_t)(void *buf, uint32_t size); + +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, int nsid, void *payload, + uint32_t payload_size) +{ + struct nvme_pt_command pt; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opcode = NVM_ADMIN_GET_LOG_PG; + pt.cmd.nsid = nsid; + pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; + pt.cmd.cdw10 |= log_page; + 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"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "get log page request returned error"); +} + +static void +print_log_error(void *buf, uint32_t size) +{ + int i, nentries; + 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; + + 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", + (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_PHASE)); + printf(" Status code: %d\n", + (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SC_MASK)); + printf(" Status code type: %d\n", + (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SCT_MASK)); + printf(" More: %d\n", + (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_M)); + printf(" DNR: %d\n", + (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_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); + printf(" Command specific info: %ju\n", + entry->command_specific); + } +} + +static void +print_log_health(void *buf, uint32_t size __unused) +{ + struct nvme_health_information_page *health = buf; + float composite_temperature = health->composite_temperature; + + printf("SMART/Health Information Log\n"); + printf("============================\n"); + + printf("Critical Warning State: 0x%02x\n", + health->critical_warning); + printf(" Available spare: %d\n", + (uint8_t)__SHIFTOUT(health->critical_warning, + NVME_HEALTH_PAGE_CW_AVAIL_SPARE)); + printf(" Temperature: %d\n", + (uint8_t)__SHIFTOUT(health->critical_warning, + NVME_HEALTH_PAGE_CW_TEMPERTURE)); + printf(" Device reliability: %d\n", + (uint8_t)__SHIFTOUT(health->critical_warning, + NVME_HEALTH_PAGE_CW_DEVICE_RELIABLITY)); + printf(" Read only: %d\n", + (uint8_t)__SHIFTOUT(health->critical_warning, + NVME_HEALTH_PAGE_CW_READ_ONLY)); + printf(" Volatile memory backup: %d\n", + (uint8_t)__SHIFTOUT(health->critical_warning, + NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP)); + printf("Temperature: %u K, %2.2f C, %3.2f F\n", + health->composite_temperature, + composite_temperature - (float)273.15, + (composite_temperature * (float)9/5) - (float)459.67); + 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); + + /* + * TODO: These are pretty ugly in hex. Is there a library that + * will convert 128-bit unsigned values to decimal? + */ + printf("Data units (512 byte) read: 0x%016jx%016jx\n", + health->data_units_read[1], + health->data_units_read[0]); + printf("Data units (512 byte) written: 0x%016jx%016jx\n", + health->data_units_written[1], + health->data_units_written[0]); + printf("Host read commands: 0x%016jx%016jx\n", + health->host_read_commands[1], + health->host_read_commands[0]); + printf("Host write commands: 0x%016jx%016jx\n", + health->host_write_commands[1], + health->host_write_commands[0]); + printf("Controller busy time (minutes): 0x%016jx%016jx\n", + health->controller_busy_time[1], + health->controller_busy_time[0]); + printf("Power cycles: 0x%016jx%016jx\n", + health->power_cycles[1], + health->power_cycles[0]); + printf("Power on hours: 0x%016jx%016jx\n", + health->power_on_hours[1], + health->power_on_hours[0]); + printf("Unsafe shutdowns: 0x%016jx%016jx\n", + health->unsafe_shutdowns[1], + health->unsafe_shutdowns[0]); + printf("Media errors: 0x%016jx%016jx\n", + health->media_errors[1], + health->media_errors[0]); + printf("No. error info log entries: 0x%016jx%016jx\n", + health->num_error_info_log_entries[1], + health->num_error_info_log_entries[0]); +} + +static void +print_log_firmware(void *buf, uint32_t size __unused) +{ + u_int i; + const char *status; + struct nvme_firmware_page *fw = buf; + + printf("Firmware Slot Log\n"); + printf("=================\n"); + + for (i = 0; i < MAX_FW_SLOTS; i++) { + printf("Slot %d: ", i + 1); + if (__SHIFTOUT(fw->afi, NVME_FW_PAGE_AFI_SLOT) == i + 1) + status = " Active"; + else + status = "Inactive"; + + if (fw->revision[i] == 0LLU) + printf("Empty\n"); + else + if (isprint(*(uint8_t *)&fw->revision[i])) + printf("[%s] %.8s\n", status, + (char *)&fw->revision[i]); + else + printf("[%s] %016jx\n", status, + fw->revision[i]); + } +} + +static struct logpage_function { + uint8_t log_page; + print_fn_t fn; +} logfuncs[] = { + {NVME_LOG_ERROR, print_log_error }, + {NVME_LOG_HEALTH_INFORMATION, print_log_health }, + {NVME_LOG_FIRMWARE_SLOT, print_log_firmware }, + {0, NULL }, +}; + +static void +logpage_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, LOGPAGE_USAGE); + exit(1); +} + +void +logpage(int argc, char *argv[]) +{ + int fd, nsid; + int log_page = 0, pageflag = false; + int hexflag = false, ns_specified; + int ch; + char *p; + char cname[64]; + uint32_t size; + void *buf; + struct logpage_function *f; + struct nvm_identify_controller cdata; + print_fn_t print_fn; + + while ((ch = getopt(argc, argv, "p:x")) != -1) { + switch (ch) { + case 'p': + /* 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); + logpage_usage(); + /* TODO: Define valid log page id ranges in nvme.h? */ + } else if (log_page == 0 || + (log_page >= 0x04 && log_page <= 0x7F) || + (log_page >= 0x80 && log_page <= 0xBF)) { + fprintf(stderr, + "\"%s\" not valid log page id.\n", + optarg); + logpage_usage(); + } + pageflag = true; + break; + case 'x': + hexflag = true; + break; + } + } + + if (!pageflag) { + printf("Missing page_id (-p).\n"); + logpage_usage(); + } + + /* Check that a controller and/or namespace was specified. */ + if (optind >= argc) + logpage_usage(); + + if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { + ns_specified = true; + parse_ns_str(argv[optind], cname, &nsid); + open_dev(cname, &fd, 1, 1); + } else { + ns_specified = false; + nsid = 0xffffffff; + open_dev(argv[optind], &fd, 1, 1); + } + + read_controller_data(fd, &cdata); + + /* + * 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) + errx(1, "log page %d valid only at controller level", + log_page); + if (!(cdata.lpa & NVME_ID_CTRLR_LPA_NS_SMART)) + errx(1, + "controller does not support per namespace " + "smart/health information"); + } + + print_fn = print_hex; + if (!hexflag) { + /* + * 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). + */ + f = logfuncs; + while (f->log_page > 0) { + if (log_page == f->log_page) { + print_fn = f->fn; + break; + } + f++; + } + } + + /* Read the log page */ + switch (log_page) { + case NVME_LOG_ERROR: + size = sizeof(struct nvme_error_information_entry); + size *= (cdata.elpe + 1); + break; + case NVME_LOG_HEALTH_INFORMATION: + size = sizeof(struct nvme_health_information_page); + break; + case NVME_LOG_FIRMWARE_SLOT: + size = sizeof(struct nvme_firmware_page); + break; + default: + size = DEFAULT_SIZE; + break; + } + + buf = get_log_buffer(size); + read_logpage(fd, log_page, nsid, buf, size); + print_fn(buf, size); + + close(fd); + exit(0); +} Index: src/sbin/nvmectl/nvme.h diff -u /dev/null src/sbin/nvmectl/nvme.h:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/nvme.h Sat Jun 4 16:29:35 2016 @@ -0,0 +1,143 @@ +/* $NetBSD: nvme.h,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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: head/sys/dev/nvme/nvme.h 296617 2016-03-10 17:13:10Z mav $ + */ + +#ifndef __NVME_H__ +#define __NVME_H__ + +#define NVME_MAX_XFER_SIZE MAXPHYS + +/* Get/Set Features */ +#define NVME_FEAT_ARBITRATION 0x01 +#define NVME_FEAT_POWER_MANAGEMENT 0x02 +#define NVME_FEAT_LBA_RANGE_TYPE 0x03 +#define NVME_FEAT_TEMPERATURE_THRESHOLD 0x04 +#define NVME_FEAT_ERROR_RECOVERY 0x05 +#define NVME_FEAT_VOLATILE_WRITE_CACHE 0x06 +#define NVME_FEAT_NUMBER_OF_QUEUES 0x07 +#define NVME_FEAT_INTERRUPT_COALESCING 0x08 +#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION 0x09 +#define NVME_FEAT_WRITE_ATOMICITY_NORMAL 0x0a +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION 0x0b +#define NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION 0x0c +#define NVME_FEAT_HOST_MEMORY_BUFFER 0x0d +/* NVM Command Set specific */ +#define NVME_FEAT_SOFTWARE_PROGRESS_MARKER 0x80 +#define NVME_FEAT_HOST_IDENTIFIER 0x81 +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK 0x82 +#define NVME_FEAT_RESERVATION_PERSISTANCE 0x83 + +/* Get Log Page */ +#define NVME_LOG_ERROR 0x01 +#define NVME_LOG_HEALTH_INFORMATION 0x02 +#define NVME_LOG_FIRMWARE_SLOT 0x03 +#define NVME_LOG_CHANGED_NAMESPACE_LIST 0x04 +#define NVME_LOG_COMMAND_EFFECTS_LOG 0x05 +#define NVME_LOG_RESERVATION_NOTIFICATION 0x80 + +/* Error Information Log (Log Identifier 01h) */ +struct nvme_error_information_entry { + uint64_t error_count; + uint16_t sqid; + uint16_t cid; + uint16_t status; + uint16_t error_location; + uint64_t lba; + uint32_t nsid; + uint8_t vendor_specific; + uint8_t _reserved1[3]; + uint64_t command_specific; + uint8_t reserved[24]; +} __packed __aligned(4); + +/* SMART / Health Information Log (Log Identifier 02h) */ +struct nvme_health_information_page { + uint8_t critical_warning; +#define NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP __BIT(4) +#define NVME_HEALTH_PAGE_CW_READ_ONLY __BIT(3) +#define NVME_HEALTH_PAGE_CW_DEVICE_RELIABLITY __BIT(2) +#define NVME_HEALTH_PAGE_CW_TEMPERTURE __BIT(1) +#define NVME_HEALTH_PAGE_CW_AVAIL_SPARE __BIT(0) + uint16_t composite_temperature; + uint8_t available_spare; + uint8_t available_spare_threshold; + uint8_t percentage_used; + + uint8_t _reserved1[26]; + + uint64_t data_units_read[2]; + uint64_t data_units_written[2]; + uint64_t host_read_commands[2]; + uint64_t host_write_commands[2]; + uint64_t controller_busy_time[2]; + uint64_t power_cycles[2]; + uint64_t power_on_hours[2]; + uint64_t unsafe_shutdowns[2]; + uint64_t media_errors[2]; + uint64_t num_error_info_log_entries[2]; + uint32_t warning_composite_temperature_time; + uint32_t critical_composite_temperature_time; + uint16_t temperature_sensor[8]; + + uint8_t reserved[296]; +} __packed __aligned(4); + +/* Firmware Commit */ +#define NVME_COMMIT_ACTION_REPLACE_NO_ACTIVATE 0 +#define NVME_COMMIT_ACTION_REPLACE_ACTIVATE 1 +#define NVME_COMMIT_ACTION_ACTIVATE_RESET 2 +#define NVME_COMMIT_ACTION_ACTIVATE_NO_RESET 3 + +/* Firmware Slot Information (Log Identifier 03h) */ +struct nvme_firmware_page { + uint8_t afi; +#define NVME_FW_PAGE_AFI_SLOT_RST __BITS(4, 6) +#define NVME_FW_PAGE_AFI_SLOT __BITS(0, 2) + uint8_t _reserved1[7]; + + uint64_t revision[7]; /* revisions for 7 slots */ + + uint8_t reserved[448]; +} __packed __aligned(4); + +/* Commands Supported and Effects (Log Identifier 05h) */ +struct nvme_command_effeects_page { + uint32_t acs[256]; +#define NVME_CE_PAGE_CS_CSE __BITS(16, 18) +#define NVME_CE_PAGE_CS_CCC __BIT(4) +#define NVME_CE_PAGE_CS_NIC __BIT(3) +#define NVME_CE_PAGE_CS_NCC __BIT(2) +#define NVME_CE_PAGE_CS_LBCC __BIT(1) +#define NVME_CE_PAGE_CS_CSUPP __BIT(0) + uint32_t iocs[256]; + + uint8_t reserved[2048]; +} __packed __aligned(4); + +#endif /* __NVME_H__ */ Index: src/sbin/nvmectl/nvmectl.8 diff -u /dev/null src/sbin/nvmectl/nvmectl.8:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/nvmectl.8 Sat Jun 4 16:29:35 2016 @@ -0,0 +1,147 @@ +.\" $NetBSD: nvmectl.8,v 1.1 2016/06/04 16:29:35 nonaka Exp $ +.\" +.\" Copyright (c) 2012 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, +.\" without modification. +.\" 2. Redistributions in binary form must reproduce at minimum a disclaimer +.\" substantially similar to the "NO WARRANTY" disclaimer below +.\" ("Disclaimer") and any redistribution must be conditioned upon +.\" including a substantially similar Disclaimer requirement for further +.\" binary redistribution. +.\" +.\" NO WARRANTY +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +.\" HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. +.\" +.\" nvmecontrol man page. +.\" +.\" Author: Jim Harris <jimhar...@freebsd.org> +.\" +.\" $FreeBSD: head/sbin/nvmecontrol/nvmecontrol.8 299151 2016-05-06 03:11:34Z pfg $ +.\" +.Dd May 19, 2016 +.Dt NVMECTL 8 +.Os +.Sh NAME +.Nm nvmectl +.Nd NVM Express control utility +.Sh SYNOPSIS +.Nm +.Ic devlist +.Nm +.Ic identify +.Op Fl v +.Op Fl x +.Aq device id +.\".Nm +.\".Ic perftest +.\".Aq Fl n Ar num_threads +.\".Aq Fl o Ar read|write +.\".Op Fl p +.\".Aq Fl s Ar size_in_bytes +.\".Aq Fl t Ar time_in_sec +.\".Aq namespace id +.\".Nm +.\".Ic reset +.\".Aq controller id +.Nm +.Ic logpage +.Aq Fl p Ar page_id +.Op Fl x +.Aq device id +.Aq namespace id +.\".Nm +.\".Ic firmware +.\".Op Fl s Ar slot +.\".Op Fl f Ar path_to_firmware +.\".Op Fl a +.\".Aq device id +.Nm +.Ic power +.Op Fl l +.Op Fl p power_state +.Op fl w workload_hint +.Sh DESCRIPTION +NVM Express (NVMe) is a storage protocol standard, for SSDs and other +high-speed storage devices over PCI Express. +.Sh EXAMPLES +.Dl nvmectl devlist +.Pp +Display a list of NVMe controllers and namespaces along with their device nodes. +.Pp +.Dl nvmectl identify nvme0 +.Pp +Display a human-readable summary of the nvme0 IDENTIFY_CONTROLLER data. +.Pp +.Dl nvmectl identify -x -v nvme0ns1 +.Pp +Display an hexadecimal dump of the nvme0 IDENTIFY_NAMESPACE data for namespace +1. +.\".Pp +.\".Dl nvmectl perftest -n 32 -o read -s 512 -t 30 nvme0ns1 +.\".Pp +.\"Run a performance test on nvme0ns1 using 32 kernel threads for 30 seconds. Each +.\"thread will issue a single 512 byte read command. Results are printed to +.\"stdout when 30 seconds expires. +.\".Pp +.\".Dl nvmectl reset nvme0 +.\".Pp +.\"Perform a controller-level reset of the nvme0 controller. +.Pp +.Dl nvmectl logpage -p 1 nvme0 +.Pp +Display a human-readable summary of the nvme0 controller's Error Information Log. +Log pages defined by the NVMe specification include Error Information Log (ID=1), +SMART/Health Information Log (ID=2), and Firmware Slot Log (ID=3). +.Pp +.Dl nvmectl logpage -p 1 -x nvme0 +.Pp +Display a hexadecimal dump of the nvme0 controller's Error Information Log. +.\".Pp +.\".Dl nvmectl firmware -s 2 -f /tmp/nvme_firmware nvme0 +.\".Pp +.\"Download the firmware image contained in "/tmp/nvme_firmware" to slot 2 of the +.\"nvme0 controller, but do not activate the image. +.\".Pp +.\".Dl nvmectl firmware -s 4 -a nvme0 +.\".Pp +.\"Activate the firmware in slot 4 of the nvme0 controller on the next reset. +.\".Pp +.\".Dl nvmectl firmware -s 7 -f /tmp/nvme_firmware -a nvme0 +.\".Pp +.\"Download the firmware image contained in "/tmp/nvme_firmware" to slot 7 of the +.\"nvme0 controller and activate it on the next reset. +.Pp +.Dl nvmectl power -l nvme0 +.Pp +List all the current power modes. +.Pp +.Dl nvmectl power -p 3 nvme0 +.Pp +Set the current power mode. +.Pp +.Dl nvmectl power nvme0 +.Pp +Get the current power mode. +.Sh AUTHORS +.An -nosplit +nvmecontrol was developed by Intel and originally written by +.An Jim Harris Aq Mt jimhar...@freebsd.org . +.Pp +This man page was written by +.An Jim Harris Aq Mt jimhar...@freebsd.org . Index: src/sbin/nvmectl/nvmectl.c diff -u /dev/null src/sbin/nvmectl/nvmectl.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/nvmectl.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,287 @@ +/* $NetBSD: nvmectl.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: nvmectl.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 295087 2016-01-30 22:48:06Z imp $"); +#endif +#endif + +#include <sys/param.h> +#include <sys/ioccom.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.h" + +typedef void (*nvme_fn_t)(int argc, char *argv[]); + +static struct nvme_function { + const char *name; + nvme_fn_t fn; + const char *usage; +} funcs[] = { + {"devlist", devlist, DEVLIST_USAGE}, + {"identify", identify, IDENTIFY_USAGE}, +#ifdef PERFTEST_USAGE + {"perftest", perftest, PERFTEST_USAGE}, +#endif +#ifdef RESET_USAGE + {"reset", reset, RESET_USAGE}, +#endif + {"logpage", logpage, LOGPAGE_USAGE}, +#ifdef FIRMWARE_USAGE + {"firmware", firmware, FIRMWARE_USAGE}, +#endif + {"power", power, POWER_USAGE}, + {NULL, NULL, NULL}, +}; + +static void +usage(void) +{ + struct nvme_function *f; + + f = funcs; + fprintf(stderr, "usage:\n"); + while (f->name != NULL) { + fprintf(stderr, "%s", f->usage); + f++; + } + exit(1); +} + +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 nvm_identify_controller *cdata) +{ + struct nvme_pt_command pt; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opcode = NVM_ADMIN_IDENTIFY; + pt.cmd.cdw10 = 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"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "identify request returned error"); +} + +void +read_namespace_data(int fd, int nsid, struct nvm_identify_namespace *nsdata) +{ + struct nvme_pt_command pt; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opcode = NVM_ADMIN_IDENTIFY; + pt.cmd.nsid = 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"); + + 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, int *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); +} + +void +nvme_strvis(u_char *dst, int dlen, const u_char *src, int slen) +{ +#define STRVIS_ISWHITE(x) ((x) == ' ' || (x) == '\0' || (x) == (u_char)'\377') + /* Trim leading and trailing blanks and NULs. */ + while (slen > 0 && STRVIS_ISWHITE(src[0])) + ++src, --slen; + while (slen > 0 && STRVIS_ISWHITE(src[slen - 1])) + --slen; + + while (slen > 0) { + if (*src < 0x20 || *src >= 0x80) { + /* non-printable characters */ + dlen -= 4; + if (dlen < 1) + break; + *dst++ = '\\'; + *dst++ = ((*src & 0300) >> 6) + '0'; + *dst++ = ((*src & 0070) >> 3) + '0'; + *dst++ = ((*src & 0007) >> 0) + '0'; + } else if (*src == '\\') { + /* quote characters */ + dlen -= 2; + if (dlen < 1) + break; + *dst++ = '\\'; + *dst++ = '\\'; + } else { + /* normal characters */ + if (--dlen < 1) + break; + *dst++ = *src; + } + ++src, --slen; + } + + *dst++ = 0; +} + +int +main(int argc, char *argv[]) +{ + struct nvme_function *f; + + if (argc < 2) + usage(); + + f = funcs; + while (f->name != NULL) { + if (strcmp(argv[1], f->name) == 0) + f->fn(argc-1, &argv[1]); + f++; + } + + usage(); + + return (0); +} Index: src/sbin/nvmectl/nvmectl.h diff -u /dev/null src/sbin/nvmectl/nvmectl.h:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/nvmectl.h Sat Jun 4 16:29:35 2016 @@ -0,0 +1,94 @@ +/* $NetBSD: nvmectl.h,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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: head/sbin/nvmecontrol/nvmecontrol.h 295087 2016-01-30 22:48:06Z imp $ + */ + +#ifndef __NVMECTL_H__ +#define __NVMECTL_H__ + +#include <sys/ioctl.h> + +#include <dev/ic/nvmeio.h> +#include "nvme.h" + +#define NVME_CTRLR_PREFIX "nvme" +#define NVME_NS_PREFIX "ns" + +#define DEVLIST_USAGE \ +" nvmectl devlist\n" + +#define IDENTIFY_USAGE \ +" nvmectl identify [-x [-v]] <controller id|namespace id>\n" + +#if 0 +#define PERFTEST_USAGE \ +" nvmectl perftest <-n num_threads> <-o read|write>\n" \ +" <-s size_in_bytes> <-t time_in_seconds>\n" \ +" <-i intr|wait> [-f refthread] [-p]\n" \ +" <namespace id>\n" +#endif + +#if 0 +#define RESET_USAGE \ +" nvmectl reset <controller id>\n" +#endif + +#define LOGPAGE_USAGE \ +" nvmectl logpage <-p page_id> [-x] <controller id|namespace id>\n" \ + +#if 0 +#define FIRMWARE_USAGE \ +" nvmectl firmware [-s slot] [-f path_to_firmware] [-a] <controller id>\n" +#endif + +#define POWER_USAGE \ +" nvmectl power [-l] [-p new-state [-w workload-hint]] <controller id>\n" + +void devlist(int, char *[]); +void identify(int, char *[]); +#ifdef PERFTEST_USAGE +void perftest(int, char *[]); +#endif +#ifdef RESET_USAGE +void reset(int, char *[]); +#endif +void logpage(int, char *[]); +#ifdef FIRMWARE_USAGE +void firmware(int, char *[]); +#endif +void power(int, char *[]); + +int open_dev(const char *, int *, int, int); +void parse_ns_str(const char *, char *, int *); +void read_controller_data(int, struct nvm_identify_controller *); +void read_namespace_data(int, int, struct nvm_identify_namespace *); +void print_hex(void *, uint32_t); +void read_logpage(int, uint8_t, int, void *, uint32_t); +void nvme_strvis(uint8_t *, int, const uint8_t *, int); + +#endif /* __NVMECTL_H__ */ Index: src/sbin/nvmectl/perftest.c diff -u /dev/null src/sbin/nvmectl/perftest.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/perftest.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,185 @@ +/* $NetBSD: perftest.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/perftest.c 257531 2013-11-01 22:05:29Z jimharris $"); +#endif +#endif + +#include <sys/param.h> +#include <sys/ioccom.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.h" + +#ifdef PERFTEST_USAGE +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_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, PERFTEST_USAGE); + exit(1); +} + +void +perftest(int argc, char *argv[]) +{ + struct nvme_io_test io_test; + int fd; + int ch; + 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 ((ch = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) { + switch (ch) { + 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); + perftest_usage(); + } else if (io_test.num_threads == 0 || + io_test.num_threads > 128) { + fprintf(stderr, + "\"%s\" not valid number of threads.\n", + optarg); + perftest_usage(); + } + 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); + perftest_usage(); + } + 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); + perftest_usage(); + } + 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); + perftest_usage(); + } + break; + } + } + + if (!nflag || !oflag || !sflag || !tflag || optind >= argc) + perftest_usage(); + + open_dev(argv[optind], &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); + exit(0); +} +#endif Index: src/sbin/nvmectl/power.c diff -u /dev/null src/sbin/nvmectl/power.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/power.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,194 @@ +/* $NetBSD: power.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: power.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/power.c 296672 2016-03-11 17:25:18Z dim $"); +#endif +#endif + +#include <sys/param.h> +#include <sys/ioccom.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.h" + +static void +power_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, POWER_USAGE); + exit(1); +} + +static void +power_list_one(int i, struct nvm_identify_psd *psd) +{ + int mpower, apower, ipower; + + mpower = psd->mp; + if (!(psd->flags & NVME_PSD_MPS)) + mpower *= 100; + ipower = psd->idlp; + if (__SHIFTOUT(psd->ips, NVME_PSD_IPS_MASK) == 1) + ipower *= 100; + apower = psd->actp; + if (__SHIFTOUT(psd->ap, NVME_PSD_APS_MASK) == 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, + (psd->flags & NVME_PSD_NOPS) ? '*' : ' ', + psd->enlat / 1000, psd->enlat % 1000, + psd->exlat / 1000, psd->exlat % 1000, + (uint8_t)(psd->rrt & NVME_PSD_RRT_MASK), + (uint8_t)(psd->rrl & NVME_PSD_RRL_MASK), + (uint8_t)(psd->rwt & NVME_PSD_RWT_MASK), + (uint8_t)(psd->rwl & NVME_PSD_RWL_MASK), + ipower / 10000, ipower % 10000, apower / 10000, apower % 10000, + (uint16_t)__SHIFTOUT(psd->ap, NVME_PSD_APW_MASK)); +} + +static void +power_list(struct nvm_identify_controller *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->psd[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.opcode = NVM_ADMIN_SET_FEATURES; + pt.cmd.cdw10 = NVME_FEAT_POWER_MANAGEMENT | p; + pt.cmd.cdw11 = 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.opcode = NVM_ADMIN_GET_FEATURES; + pt.cmd.cdw10 = 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); +} + +void +power(int argc, char *argv[]) +{ + struct nvm_identify_controller cdata; + int ch, listflag = 0, powerflag = 0, power_val = 0, fd; + int workload = 0; + char *end; + + while ((ch = getopt(argc, argv, "lp:w:")) != -1) { + switch (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); + power_usage(); + } + break; + case 'w': + workload = strtol(optarg, &end, 0); + if (*end != '\0') { + fprintf(stderr, "Invalid workload hint: %s\n", optarg); + power_usage(); + } + break; + default: + power_usage(); + } + } + + /* Check that a controller was specified. */ + if (optind >= argc) + power_usage(); + + if (listflag && powerflag) { + fprintf(stderr, "Can't set power and list power states\n"); + power_usage(); + } + + open_dev(argv[optind], &fd, 1, 1); + read_controller_data(fd, &cdata); + + if (listflag) { + power_list(&cdata); + goto out; + } + + if (powerflag) { + power_set(fd, power_val, workload, 0); + goto out; + } + power_show(fd); + +out: + close(fd); + exit(0); +} Index: src/sbin/nvmectl/reset.c diff -u /dev/null src/sbin/nvmectl/reset.c:1.1 --- /dev/null Sat Jun 4 16:29:35 2016 +++ src/sbin/nvmectl/reset.c Sat Jun 4 16:29:35 2016 @@ -0,0 +1,80 @@ +/* $NetBSD: reset.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $ */ + +/*- + * 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 <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: reset.c,v 1.1 2016/06/04 16:29:35 nonaka Exp $"); +#if 0 +__FBSDID("$FreeBSD: head/sbin/nvmecontrol/reset.c 253109 2013-07-09 21:14:15Z jimharris $"); +#endif +#endif + +#include <sys/param.h> +#include <sys/ioccom.h> + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "nvmectl.h" + +#ifdef RESET_USAGE +static void +reset_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, RESET_USAGE); + exit(1); +} + +void +reset(int argc, char *argv[]) +{ + int ch, fd; + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + reset_usage(); + } + } + + /* Check that a controller was specified. */ + if (optind >= argc) + reset_usage(); + + 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); +} +#endif