Index: head/sbin/nvmecontrol/devlist.c =================================================================== --- head/sbin/nvmecontrol/devlist.c (revision 341657) +++ head/sbin/nvmecontrol/devlist.c (revision 341658) @@ -1,123 +1,123 @@ /*- * 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" #define DEVLIST_USAGE \ "devlist\n" static inline uint32_t ns_get_sector_size(struct nvme_namespace_data *nsdata) { uint8_t flbas_fmt, lbads; flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & NVME_NS_DATA_FLBAS_FORMAT_MASK; lbads = (nsdata->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & NVME_NS_DATA_LBAF_LBADS_MASK; return (1 << lbads); } static void -devlist(struct nvme_function *nf, int argc, char *argv[]) +devlist(const struct nvme_function *nf, 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: usage(nf); } } 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); } NVME_COMMAND(top, devlist, devlist, DEVLIST_USAGE); Index: head/sbin/nvmecontrol/firmware.c =================================================================== --- head/sbin/nvmecontrol/firmware.c (revision 341657) +++ head/sbin/nvmecontrol/firmware.c (revision 341658) @@ -1,337 +1,337 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define FIRMWARE_USAGE \ "firmware [-s slot] [-f path_to_firmware] [-a] \n" static int slot_has_valid_firmware(int fd, int slot) { struct nvme_firmware_page fw; int has_fw = false; read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, NVME_GLOBAL_NAMESPACE_TAG, &fw, sizeof(fw)); if (fw.revision[slot-1] != 0LLU) has_fw = true; return (has_fw); } static void read_image_file(char *path, void **buf, int32_t *size) { struct stat sb; int32_t filesize; int fd; *size = 0; *buf = NULL; if ((fd = open(path, O_RDONLY)) < 0) err(1, "unable to open '%s'", path); if (fstat(fd, &sb) < 0) err(1, "unable to stat '%s'", path); /* * The NVMe spec does not explicitly state a maximum firmware image * size, although one can be inferred from the dword size limitation * for the size and offset fields in the Firmware Image Download * command. * * Technically, the max is UINT32_MAX * sizeof(uint32_t), since the * size and offsets are specified in terms of dwords (not bytes), but * realistically INT32_MAX is sufficient here and simplifies matters * a bit. */ if (sb.st_size > INT32_MAX) errx(1, "size of file '%s' is too large (%jd bytes)", path, (intmax_t)sb.st_size); filesize = (int32_t)sb.st_size; if ((*buf = malloc(filesize)) == NULL) errx(1, "unable to malloc %d bytes", filesize); if ((*size = read(fd, *buf, filesize)) < 0) err(1, "error reading '%s'", path); /* XXX assuming no short reads */ if (*size != filesize) errx(1, "error reading '%s' (read %d bytes, requested %d bytes)", path, *size, filesize); } static void update_firmware(int fd, uint8_t *payload, int32_t payload_size) { struct nvme_pt_command pt; int32_t off, resid, size; void *chunk; off = 0; resid = payload_size; if ((chunk = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE)) == NULL) errx(1, "unable to malloc %d bytes", NVME_MAX_XFER_SIZE); while (resid > 0) { size = (resid >= NVME_MAX_XFER_SIZE) ? NVME_MAX_XFER_SIZE : resid; memcpy(chunk, payload + off, size); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD; pt.cmd.cdw10 = htole32((size / sizeof(uint32_t)) - 1); pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); pt.buf = chunk; pt.len = size; pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "firmware download request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "firmware download request returned error"); resid -= size; off += size; } } static int activate_firmware(int fd, int slot, int activate_action) { struct nvme_pt_command pt; uint16_t sct, sc; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FIRMWARE_ACTIVATE; pt.cmd.cdw10 = htole32((activate_action << 3) | slot); pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "firmware activate request failed"); sct = NVME_STATUS_GET_SCT(pt.cpl.status); sc = NVME_STATUS_GET_SC(pt.cpl.status); if (sct == NVME_SCT_COMMAND_SPECIFIC && sc == NVME_SC_FIRMWARE_REQUIRES_RESET) return 1; if (nvme_completion_is_error(&pt.cpl)) errx(1, "firmware activate request returned error"); return 0; } static void -firmware(struct nvme_function *nf, int argc, char *argv[]) +firmware(const struct nvme_function *nf, int argc, char *argv[]) { int fd = -1, slot = 0; int a_flag, s_flag, f_flag; int activate_action, reboot_required; int opt; char *p, *image = NULL; char *controller = NULL, prompt[64]; void *buf = NULL; int32_t size = 0; uint16_t oacs_fw; uint8_t fw_slot1_ro, fw_num_slots; struct nvme_controller_data cdata; a_flag = s_flag = f_flag = false; while ((opt = getopt(argc, argv, "af:s:")) != -1) { switch (opt) { case 'a': a_flag = true; break; case 's': slot = strtol(optarg, &p, 0); if (p != NULL && *p != '\0') { fprintf(stderr, "\"%s\" not valid slot.\n", optarg); usage(nf); } else if (slot == 0) { fprintf(stderr, "0 is not a valid slot number. " "Slot numbers start at 1.\n"); usage(nf); } else if (slot > 7) { fprintf(stderr, "Slot number %s specified which is " "greater than max allowed slot number of " "7.\n", optarg); usage(nf); } 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) usage(nf); if (!f_flag && !a_flag) { fprintf(stderr, "Neither a replace ([-f path_to_firmware]) nor " "activate ([-a]) firmware image action\n" "was specified.\n"); usage(nf); } if (!f_flag && a_flag && slot == 0) { fprintf(stderr, "Slot number to activate not specified.\n"); usage(nf); } controller = argv[optind]; open_dev(controller, &fd, 1, 1); read_controller_data(fd, &cdata); oacs_fw = (cdata.oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; if (oacs_fw == 0) errx(1, "controller does not support firmware activate/download"); fw_slot1_ro = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) & NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK; if (f_flag && slot == 1 && fw_slot1_ro) errx(1, "slot %d is marked as read only", slot); fw_num_slots = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; if (slot > fw_num_slots) errx(1, "slot %d specified but controller only supports %d slots", slot, fw_num_slots); 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) activate_action = NVME_AA_REPLACE_ACTIVATE; else activate_action = NVME_AA_REPLACE_NO_ACTIVATE; } else { activate_action = NVME_AA_ACTIVATE; } reboot_required = activate_firmware(fd, slot, activate_action); 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); } NVME_COMMAND(top, firmware, firmware, FIRMWARE_USAGE); Index: head/sbin/nvmecontrol/format.c =================================================================== --- head/sbin/nvmecontrol/format.c (revision 341657) +++ head/sbin/nvmecontrol/format.c (revision 341658) @@ -1,184 +1,184 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2018 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define FORMAT_USAGE \ "format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] \n" static void -format(struct nvme_function *nf, int argc, char *argv[]) +format(const struct nvme_function *nf, int argc, char *argv[]) { struct nvme_controller_data cd; struct nvme_namespace_data nsd; struct nvme_pt_command pt; char path[64]; char *target; uint32_t nsid; int ch, fd; int lbaf = -1, mset = -1, pi = -1, pil = -1, ses = 0; if (argc < 2) usage(nf); while ((ch = getopt(argc, argv, "f:m:p:l:EC")) != -1) { switch ((char)ch) { case 'f': lbaf = strtol(optarg, NULL, 0); break; case 'm': mset = strtol(optarg, NULL, 0); break; case 'p': pi = strtol(optarg, NULL, 0); break; case 'l': pil = strtol(optarg, NULL, 0); break; case 'E': if (ses == 2) errx(1, "-E and -C are mutually exclusive"); ses = 1; break; case 'C': if (ses == 1) errx(1, "-E and -C are mutually exclusive"); ses = 2; break; default: usage(nf); } } /* Check that a controller or namespace was specified. */ if (optind >= argc) usage(nf); target = argv[optind]; /* * Check if the specified device node exists before continuing. * This is a cleaner check for cases where the correct controller * is specified, but an invalid namespace on that controller. */ open_dev(target, &fd, 1, 1); /* * If device node contains "ns", we consider it a namespace, * otherwise, consider it a controller. */ if (strstr(target, NVME_NS_PREFIX) == NULL) { nsid = NVME_GLOBAL_NAMESPACE_TAG; } else { /* * We send FORMAT commands to the controller, not the namespace, * since it is an admin cmd. The namespace ID will be specified * in the command itself. So parse the namespace's device node * string to get the controller substring and namespace ID. */ close(fd); parse_ns_str(target, path, &nsid); open_dev(path, &fd, 1, 1); } /* Check that controller can execute this command. */ read_controller_data(fd, &cd); if (((cd.oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) & NVME_CTRLR_DATA_OACS_FORMAT_MASK) == 0) errx(1, "controller does not support format"); if (((cd.fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) & NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == 2) errx(1, "controller does not support cryptographic erase"); if (nsid != NVME_GLOBAL_NAMESPACE_TAG) { if (((cd.fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) & NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == 0) errx(1, "controller does not support per-NS format"); if (((cd.fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) & NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != 0) errx(1, "controller does not support per-NS erase"); /* Try to keep previous namespace parameters. */ read_namespace_data(fd, nsid, &nsd); if (lbaf < 0) lbaf = (nsd.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & NVME_NS_DATA_FLBAS_FORMAT_MASK; if (lbaf > nsd.nlbaf) errx(1, "LBA format is out of range"); if (mset < 0) mset = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT) & NVME_NS_DATA_FLBAS_EXTENDED_MASK; if (pi < 0) pi = (nsd.dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) & NVME_NS_DATA_DPS_MD_START_MASK; if (pil < 0) pil = (nsd.dps >> NVME_NS_DATA_DPS_PIT_SHIFT) & NVME_NS_DATA_DPS_PIT_MASK; } else { /* We have no previous parameters, so default to zeroes. */ if (lbaf < 0) lbaf = 0; if (mset < 0) mset = 0; if (pi < 0) pi = 0; if (pil < 0) pil = 0; } memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FORMAT_NVM; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32((ses << 9) + (pil << 8) + (pi << 5) + (mset << 4) + lbaf); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "format request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "format request returned error"); close(fd); exit(0); } NVME_COMMAND(top, format, format, FORMAT_USAGE); Index: head/sbin/nvmecontrol/identify.c =================================================================== --- head/sbin/nvmecontrol/identify.c (revision 341657) +++ head/sbin/nvmecontrol/identify.c (revision 341658) @@ -1,292 +1,292 @@ /*- * 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 "nvmecontrol.h" #include "nvmecontrol_ext.h" #define IDENTIFY_USAGE \ "identify [-x [-v]] \n" static void print_namespace(struct nvme_namespace_data *nsdata) { uint32_t i; uint32_t lbaf, lbads, ms, rp; uint8_t thin_prov, ptype; uint8_t flbas_fmt; thin_prov = (nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_THIN_PROV_SHIFT) & NVME_NS_DATA_NSFEAT_THIN_PROV_MASK; flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & NVME_NS_DATA_FLBAS_FORMAT_MASK; printf("Size (in LBAs): %lld (%lldM)\n", (long long)nsdata->nsze, (long long)nsdata->nsze / 1024 / 1024); printf("Capacity (in LBAs): %lld (%lldM)\n", (long long)nsdata->ncap, (long long)nsdata->ncap / 1024 / 1024); printf("Utilization (in LBAs): %lld (%lldM)\n", (long long)nsdata->nuse, (long long)nsdata->nuse / 1024 / 1024); printf("Thin Provisioning: %s\n", thin_prov ? "Supported" : "Not Supported"); printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1); printf("Current LBA Format: LBA Format #%02d\n", flbas_fmt); printf("Data Protection Caps: %s%s%s%s%s%s\n", (nsdata->dpc == 0) ? "Not Supported" : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_END_SHIFT) & NVME_NS_DATA_DPC_MD_END_MASK) ? "Last Bytes, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_START_SHIFT) & NVME_NS_DATA_DPC_MD_START_MASK) ? "First Bytes, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT3_SHIFT) & NVME_NS_DATA_DPC_PIT3_MASK) ? "Type 3, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_SHIFT) & NVME_NS_DATA_DPC_PIT2_MASK) ? "Type 2, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_MASK) & NVME_NS_DATA_DPC_PIT1_MASK) ? "Type 1" : ""); printf("Data Protection Settings: "); ptype = (nsdata->dps >> NVME_NS_DATA_DPS_PIT_SHIFT) & NVME_NS_DATA_DPS_PIT_MASK; if (ptype) { printf("Type %d, %s Bytes\n", ptype, ((nsdata->dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) & NVME_NS_DATA_DPS_MD_START_MASK) ? "First" : "Last"); } else { printf("Not Enabled\n"); } printf("Multi-Path I/O Capabilities: %s%s\n", (nsdata->nmic == 0) ? "Not Supported" : "", ((nsdata->nmic >> NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) & NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK) ? "May be shared" : ""); printf("Reservation Capabilities: %s%s%s%s%s%s%s%s%s\n", (nsdata->rescap == 0) ? "Not Supported" : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_IEKEY13_SHIFT) & NVME_NS_DATA_RESCAP_IEKEY13_MASK) ? "IEKEY13, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_AR_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_AR_MASK) ? "EX_AC_AR, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_AR_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_AR_MASK) ? "WR_EX_AR, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_RO_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_RO_MASK) ? "EX_AC_RO, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_RO_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_RO_MASK) ? "WR_EX_RO, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_MASK) ? "EX_AC, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_MASK) ? "WR_EX, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_PTPL_SHIFT) & NVME_NS_DATA_RESCAP_PTPL_MASK) ? "PTPL" : ""); printf("Format Progress Indicator: "); if ((nsdata->fpi >> NVME_NS_DATA_FPI_SUPP_SHIFT) & NVME_NS_DATA_FPI_SUPP_MASK) { printf("%u%% remains\n", (nsdata->fpi >> NVME_NS_DATA_FPI_PERC_SHIFT) & NVME_NS_DATA_FPI_PERC_MASK); } else printf("Not Supported\n"); printf("Optimal I/O Boundary (LBAs): %u\n", nsdata->noiob); printf("Globally Unique Identifier: "); for (i = 0; i < sizeof(nsdata->nguid); i++) printf("%02x", nsdata->nguid[i]); printf("\n"); printf("IEEE EUI64: "); for (i = 0; i < sizeof(nsdata->eui64); i++) printf("%02x", nsdata->eui64[i]); printf("\n"); for (i = 0; i <= nsdata->nlbaf; i++) { lbaf = nsdata->lbaf[i]; lbads = (lbaf >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & NVME_NS_DATA_LBAF_LBADS_MASK; ms = (lbaf >> NVME_NS_DATA_LBAF_MS_SHIFT) & NVME_NS_DATA_LBAF_MS_MASK; rp = (lbaf >> NVME_NS_DATA_LBAF_RP_SHIFT) & NVME_NS_DATA_LBAF_RP_MASK; printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d" " Performance: %s\n", i, 1 << lbads, ms, (rp == 0) ? "Best" : (rp == 1) ? "Better" : (rp == 2) ? "Good" : "Degraded"); } } static void -identify_ctrlr(struct nvme_function *nf, int argc, char *argv[]) +identify_ctrlr(const struct nvme_function *nf, int argc, char *argv[]) { struct nvme_controller_data cdata; int ch, fd, hexflag = 0, hexlength; int verboseflag = 0; while ((ch = getopt(argc, argv, "vx")) != -1) { switch ((char)ch) { case 'v': verboseflag = 1; break; case 'x': hexflag = 1; break; default: usage(nf); } } /* Check that a controller was specified. */ if (optind >= argc) usage(nf); open_dev(argv[optind], &fd, 1, 1); read_controller_data(fd, &cdata); close(fd); if (hexflag == 1) { if (verboseflag == 1) hexlength = sizeof(struct nvme_controller_data); else hexlength = offsetof(struct nvme_controller_data, reserved8); print_hex(&cdata, hexlength); exit(0); } if (verboseflag == 1) { fprintf(stderr, "-v not currently supported without -x\n"); usage(nf); } nvme_print_controller(&cdata); exit(0); } static void -identify_ns(struct nvme_function *nf,int argc, char *argv[]) +identify_ns(const struct nvme_function *nf,int argc, char *argv[]) { struct nvme_namespace_data nsdata; char path[64]; int ch, fd, hexflag = 0, hexlength; int verboseflag = 0; uint32_t nsid; while ((ch = getopt(argc, argv, "vx")) != -1) { switch ((char)ch) { case 'v': verboseflag = 1; break; case 'x': hexflag = 1; break; default: usage(nf); } } /* Check that a namespace was specified. */ if (optind >= argc) usage(nf); /* * Check if the specified device node exists before continuing. * This is a cleaner check for cases where the correct controller * is specified, but an invalid namespace on that controller. */ open_dev(argv[optind], &fd, 1, 1); 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 nvme_namespace_data); else hexlength = offsetof(struct nvme_namespace_data, reserved6); print_hex(&nsdata, hexlength); exit(0); } if (verboseflag == 1) { fprintf(stderr, "-v not currently supported without -x\n"); usage(nf); } print_namespace(&nsdata); exit(0); } static void -identify(struct nvme_function *nf, int argc, char *argv[]) +identify(const struct nvme_function *nf, int argc, char *argv[]) { char *target; if (argc < 2) usage(nf); while (getopt(argc, argv, "vx") != -1) ; /* Check that a controller or namespace was specified. */ if (optind >= argc) usage(nf); target = argv[optind]; optreset = 1; optind = 1; /* * If device node contains "ns", we consider it a namespace, * otherwise, consider it a controller. */ if (strstr(target, NVME_NS_PREFIX) == NULL) identify_ctrlr(nf, argc, argv); else identify_ns(nf, argc, argv); } NVME_COMMAND(top, identify, identify, IDENTIFY_USAGE); Index: head/sbin/nvmecontrol/logpage.c =================================================================== --- head/sbin/nvmecontrol/logpage.c (revision 341657) +++ head/sbin/nvmecontrol/logpage.c (revision 341658) @@ -1,467 +1,467 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define LOGPAGE_USAGE \ "logpage <-p page_id> [-b] [-v vendor] [-x] \n" \ #define MAX_FW_SLOTS (7) SET_CONCAT_DEF(logpage, struct logpage_function); const char * kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) { static char bad[32]; size_t i; for (i = 0; i < kv_count; i++, kv++) if (kv->key == key) return kv->name; snprintf(bad, sizeof(bad), "Attribute %#x", key); return bad; } static void print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { print_hex(data, length); } static void print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { write(STDOUT_FILENO, data, length); } static void * get_log_buffer(uint32_t size) { void *buf; if ((buf = malloc(size)) == NULL) errx(1, "unable to malloc %u bytes", size); memset(buf, 0, size); return (buf); } void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size) { struct nvme_pt_command pt; struct nvme_error_information_entry *err_entry; int i, err_pages; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; pt.cmd.cdw10 |= log_page; pt.cmd.cdw10 = htole32(pt.cmd.cdw10); pt.buf = payload; pt.len = payload_size; pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "get log page request failed"); /* Convert data to host endian */ switch (log_page) { case NVME_LOG_ERROR: err_entry = (struct nvme_error_information_entry *)payload; err_pages = payload_size / sizeof(struct nvme_error_information_entry); for (i = 0; i < err_pages; i++) nvme_error_information_entry_swapbytes(err_entry++); break; case NVME_LOG_HEALTH_INFORMATION: nvme_health_information_page_swapbytes( (struct nvme_health_information_page *)payload); break; case NVME_LOG_FIRMWARE_SLOT: nvme_firmware_page_swapbytes( (struct nvme_firmware_page *)payload); break; case INTEL_LOG_TEMP_STATS: intel_log_temp_stats_swapbytes( (struct intel_log_temp_stats *)payload); break; default: break; } if (nvme_completion_is_error(&pt.cpl)) errx(1, "get log page request returned error"); } static void print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) { int i, nentries; uint16_t status; uint8_t p, sc, sct, m, dnr; struct nvme_error_information_entry *entry = buf; printf("Error Information Log\n"); printf("=====================\n"); if (entry->error_count == 0) { printf("No error entries found\n"); return; } nentries = size/sizeof(struct nvme_error_information_entry); for (i = 0; i < nentries; i++, entry++) { if (entry->error_count == 0) break; status = entry->status; p = NVME_STATUS_GET_P(status); sc = NVME_STATUS_GET_SC(status); sct = NVME_STATUS_GET_SCT(status); m = NVME_STATUS_GET_M(status); dnr = NVME_STATUS_GET_DNR(status); printf("Entry %02d\n", i + 1); printf("=========\n"); printf(" Error count: %ju\n", entry->error_count); printf(" Submission queue ID: %u\n", entry->sqid); printf(" Command ID: %u\n", entry->cid); /* TODO: Export nvme_status_string structures from kernel? */ printf(" Status:\n"); printf(" Phase tag: %d\n", p); printf(" Status code: %d\n", sc); printf(" Status code type: %d\n", sct); printf(" More: %d\n", m); printf(" DNR: %d\n", dnr); printf(" Error location: %u\n", entry->error_location); printf(" LBA: %ju\n", entry->lba); printf(" Namespace ID: %u\n", entry->nsid); printf(" Vendor specific info: %u\n", entry->vendor_specific); } } void print_temp(uint16_t t) { printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); } static void print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_health_information_page *health = buf; char cbuf[UINT128_DIG + 1]; uint8_t warning; int i; warning = health->critical_warning; printf("SMART/Health Information Log\n"); printf("============================\n"); printf("Critical Warning State: 0x%02x\n", warning); printf(" Available spare: %d\n", !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE)); printf(" Temperature: %d\n", !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE)); printf(" Device reliability: %d\n", !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY)); printf(" Read only: %d\n", !!(warning & NVME_CRIT_WARN_ST_READ_ONLY)); printf(" Volatile memory backup: %d\n", !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP)); printf("Temperature: "); print_temp(health->temperature); printf("Available spare: %u\n", health->available_spare); printf("Available spare threshold: %u\n", health->available_spare_threshold); printf("Percentage used: %u\n", health->percentage_used); printf("Data units (512,000 byte) read: %s\n", uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); printf("Data units written: %s\n", uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); printf("Host read commands: %s\n", uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); printf("Host write commands: %s\n", uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); printf("Controller busy time (minutes): %s\n", uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); printf("Power cycles: %s\n", uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); printf("Power on hours: %s\n", uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); printf("Unsafe shutdowns: %s\n", uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); printf("Media errors: %s\n", uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); printf("No. error info log entries: %s\n", uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); printf("Error Temp Composite Time: %d\n", health->error_temp_time); for (i = 0; i < 8; i++) { if (health->temp_sensor[i] == 0) continue; printf("Temperature Sensor %d: ", i + 1); print_temp(health->temp_sensor[i]); } } static void print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused) { int i, slots; const char *status; struct nvme_firmware_page *fw = buf; uint8_t afi_slot; uint16_t oacs_fw; uint8_t fw_num_slots; afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT; afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK; oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; printf("Firmware Slot Log\n"); printf("=================\n"); if (oacs_fw == 0) slots = 1; else slots = MIN(fw_num_slots, MAX_FW_SLOTS); for (i = 0; i < slots; i++) { printf("Slot %d: ", i + 1); if (afi_slot == i + 1) status = " Active"; else status = "Inactive"; if (fw->revision[i] == 0LLU) printf("Empty\n"); else if (isprint(*(char *)&fw->revision[i])) printf("[%s] %.8s\n", status, (char *)&fw->revision[i]); else printf("[%s] %016jx\n", status, fw->revision[i]); } } /* * Table of log page printer / sizing. * * Make sure you keep all the pages of one vendor together so -v help * lists all the vendors pages. */ NVME_LOGPAGE(error, NVME_LOG_ERROR, NULL, "Drive Error Log", print_log_error, 0); NVME_LOGPAGE(health, NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", print_log_health, sizeof(struct nvme_health_information_page)); NVME_LOGPAGE(fw, NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", print_log_firmware, sizeof(struct nvme_firmware_page)); static void logpage_help(void) { - struct logpage_function **f; + const struct logpage_function * const *f; const char *v; fprintf(stderr, "\n"); fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); fprintf(stderr, "-------- ---------- ----------\n"); for (f = logpage_begin(); f < logpage_limit(); f++) { v = (*f)->vendor == NULL ? "-" : (*f)->vendor; fprintf(stderr, "0x%02x %-10s %s\n", (*f)->log_page, v, (*f)->name); } exit(1); } static void -logpage(struct nvme_function *nf, int argc, char *argv[]) +logpage(const struct nvme_function *nf, int argc, char *argv[]) { int fd; int log_page = 0, pageflag = false; int binflag = false, hexflag = false, ns_specified; int opt; char *p; char cname[64]; uint32_t nsid, size; void *buf; const char *vendor = NULL; - struct logpage_function **f; + const struct logpage_function * const *f; struct nvme_controller_data cdata; print_fn_t print_fn; uint8_t ns_smart; while ((opt = getopt(argc, argv, "bp:xv:")) != -1) { switch (opt) { case 'b': binflag = true; break; case 'p': if (strcmp(optarg, "help") == 0) logpage_help(); /* TODO: Add human-readable ASCII page IDs */ log_page = strtol(optarg, &p, 0); if (p != NULL && *p != '\0') { fprintf(stderr, "\"%s\" not valid log page id.\n", optarg); usage(nf); } pageflag = true; break; case 'x': hexflag = true; break; case 'v': if (strcmp(optarg, "help") == 0) logpage_help(); vendor = optarg; break; } } if (!pageflag) { printf("Missing page_id (-p).\n"); usage(nf); } /* Check that a controller and/or namespace was specified. */ if (optind >= argc) usage(nf); 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 = NVME_GLOBAL_NAMESPACE_TAG; open_dev(argv[optind], &fd, 1, 1); } read_controller_data(fd, &cdata); ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & NVME_CTRLR_DATA_LPA_NS_SMART_MASK; /* * The log page attribtues indicate whether or not the controller * supports the SMART/Health information log page on a per * namespace basis. */ if (ns_specified) { if (log_page != NVME_LOG_HEALTH_INFORMATION) errx(1, "log page %d valid only at controller level", log_page); if (ns_smart == 0) errx(1, "controller does not support per namespace " "smart/health information"); } print_fn = print_log_hex; size = DEFAULT_SIZE; if (binflag) print_fn = print_bin; if (!binflag && !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). If there was a vendor specified by the user, and * the page is vendor specific, don't match the print function * unless the vendors match. */ for (f = logpage_begin(); f < logpage_limit(); f++) { if ((*f)->vendor != NULL && vendor != NULL && strcmp((*f)->vendor, vendor) != 0) continue; if (log_page != (*f)->log_page) continue; print_fn = (*f)->print_fn; size = (*f)->size; break; } } if (log_page == NVME_LOG_ERROR) { size = sizeof(struct nvme_error_information_entry); size *= (cdata.elpe + 1); } /* Read the log page */ buf = get_log_buffer(size); read_logpage(fd, log_page, nsid, buf, size); print_fn(&cdata, buf, size); close(fd); exit(0); } NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE); Index: head/sbin/nvmecontrol/ns.c =================================================================== --- head/sbin/nvmecontrol/ns.c (revision 341657) +++ head/sbin/nvmecontrol/ns.c (revision 341658) @@ -1,446 +1,446 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Netflix, Inc * Copyright (C) 2018 Alexander Motin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "nvmecontrol.h" NVME_CMD_DECLARE(ns, struct nvme_function); #define NS_USAGE \ "ns (create|delete|attach|detach)\n" /* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */ #define NSCREATE_USAGE \ "ns create -s size [-c cap] [-f fmt] [-m mset] [-n nmic] [-p pi] [-l pil] nvmeN\n" #define NSDELETE_USAGE \ "ns delete -n nsid nvmeN\n" #define NSATTACH_USAGE \ "ns attach -n nsid [-c ctrlrid] nvmeN \n" #define NSDETACH_USAGE \ "ns detach -n nsid [-c ctrlrid] nvmeN\n" -void nscreate(struct nvme_function *nf, int argc, char *argv[]); -void nsdelete(struct nvme_function *nf, int argc, char *argv[]); -void nsattach(struct nvme_function *nf, int argc, char *argv[]); -void nsdetach(struct nvme_function *nf, int argc, char *argv[]); +static void nscreate(const struct nvme_function *nf, int argc, char *argv[]); +static void nsdelete(const struct nvme_function *nf, int argc, char *argv[]); +static void nsattach(const struct nvme_function *nf, int argc, char *argv[]); +static void nsdetach(const struct nvme_function *nf, int argc, char *argv[]); NVME_COMMAND(ns, create, nscreate, NSCREATE_USAGE); NVME_COMMAND(ns, delete, nsdelete, NSDELETE_USAGE); NVME_COMMAND(ns, attach, nsattach, NSATTACH_USAGE); NVME_COMMAND(ns, detach, nsdetach, NSDETACH_USAGE); 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(struct nvme_function *nf, int argc, char *argv[]) +static void +nscreate(const struct nvme_function *nf, 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) usage(nf); while ((ch = getopt(argc, argv, "s:c:f:m:n:p:l:")) != -1) { switch (ch) { case 's': nsze = strtol(optarg, (char **)NULL, 0); break; case 'c': cap = strtol(optarg, (char **)NULL, 0); break; case 'f': lbaf = strtol(optarg, (char **)NULL, 0); break; case 'm': mset = strtol(optarg, NULL, 0); break; case 'n': nmic = strtol(optarg, NULL, 0); break; case 'p': pi = strtol(optarg, NULL, 0); break; case 'l': pil = strtol(optarg, NULL, 0); break; default: usage(nf); } } if (optind >= argc) usage(nf); if (cap == -1) cap = nsze; if (nsze == -1 || cap == -1) usage(nf); 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 = 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(struct nvme_function *nf, int argc, char *argv[]) +static void +nsdelete(const struct nvme_function *nf, 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) usage(nf); while ((ch = getopt(argc, argv, "n:")) != -1) { switch ((char)ch) { case 'n': nsid = strtol(optarg, (char **)NULL, 0); break; default: usage(nf); } } if (optind >= argc || nsid == -2) usage(nf); 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 = 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(struct nvme_function *nf, int argc, char *argv[]) +static void +nsattach(const struct nvme_function *nf, 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) usage(nf); while ((ch = getopt(argc, argv, "n:c:")) != -1) { switch (ch) { case 'n': nsid = strtol(optarg, (char **)NULL, 0); break; case 'c': ctrlrid = strtol(optarg, (char **)NULL, 0); break; default: usage(nf); } } if (optind >= argc) usage(nf); if (nsid == -1 ) usage(nf); 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 = 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 = 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(struct nvme_function *nf, int argc, char *argv[]) +static void +nsdetach(const struct nvme_function *nf, 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) usage(nf); while ((ch = getopt(argc, argv, "n:c:")) != -1) { switch (ch) { case 'n': nsid = strtol(optarg, (char **)NULL, 0); break; case 'c': ctrlrid = strtol(optarg, (char **)NULL, 0); break; default: usage(nf); } } if (optind >= argc) usage(nf); if (nsid == -1) usage(nf); 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 = 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 = 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); } static void -ns(struct nvme_function *nf __unused, int argc, char *argv[]) +ns(const struct nvme_function *nf __unused, int argc, char *argv[]) { DISPATCH(argc, argv, ns); } NVME_COMMAND(top, ns, ns, NS_USAGE); Index: head/sbin/nvmecontrol/nvmecontrol.c =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.c (revision 341657) +++ head/sbin/nvmecontrol/nvmecontrol.c (revision 341658) @@ -1,351 +1,351 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" SET_CONCAT_DEF(top, struct nvme_function); static void print_usage(const struct nvme_function *f) { const char *cp; char ch; bool need_prefix = true; cp = f->usage; while (*cp) { ch = *cp++; if (need_prefix) { if (ch != ' ') fputs(" nvmecontrol ", stderr); else fputs(" ", stderr); } fputc(ch, stderr); need_prefix = (ch == '\n'); } if (!need_prefix) fputc('\n', stderr); } static void -gen_usage_set(struct nvme_function **f, struct nvme_function **flimit) +gen_usage_set(const struct nvme_function * const *f, const struct nvme_function * const *flimit) { fprintf(stderr, "usage:\n"); while (f < flimit) { print_usage(*f); f++; } exit(1); } void usage(const struct nvme_function *f) { fprintf(stderr, "usage:\n"); print_usage(f); exit(1); } void -dispatch_set(int argc, char *argv[], struct nvme_function **tbl, - struct nvme_function **tbl_limit) +dispatch_set(int argc, char *argv[], const struct nvme_function * const *tbl, + const struct nvme_function * const *tbl_limit) { - struct nvme_function **f = tbl; + const struct nvme_function * const *f = tbl; if (argv[1] == NULL) { gen_usage_set(tbl, tbl_limit); return; } while (f < tbl_limit) { if (strcmp(argv[1], (*f)->name) == 0) { (*f)->fn(*f, argc-1, &argv[1]); return; } f++; } fprintf(stderr, "Unknown command: %s\n", argv[1]); gen_usage_set(tbl, tbl_limit); } void set_concat_add(struct set_concat *m, void *b, void *e) { void **bp, **ep; int add_n, cur_n; if (b == NULL) return; /* * Args are really pointers to arrays of pointers, but C's * casting rules kinda suck since you can't directly cast * struct foo ** to a void **. */ bp = (void **)b; ep = (void **)e; add_n = ep - bp; cur_n = 0; if (m->begin != NULL) cur_n = m->limit - m->begin; m->begin = reallocarray(m->begin, cur_n + add_n, sizeof(void *)); if (m->begin == NULL) err(1, "expanding concat set"); memcpy(m->begin + cur_n, bp, add_n * sizeof(void *)); m->limit = m->begin + cur_n + add_n; } static void print_bytes(void *data, uint32_t length) { uint32_t i, j; uint8_t *p, *end; end = (uint8_t *)data + length; for (i = 0; i < length; i++) { p = (uint8_t *)data + (i*16); printf("%03x: ", i*16); for (j = 0; j < 16 && p < end; j++) printf("%02x ", *p++); if (p >= end) break; printf("\n"); } printf("\n"); } static void print_dwords(void *data, uint32_t length) { uint32_t *p; uint32_t i, j; p = (uint32_t *)data; length /= sizeof(uint32_t); for (i = 0; i < length; i+=8) { printf("%03x: ", i*4); for (j = 0; j < 8; j++) printf("%08x ", p[i+j]); printf("\n"); } printf("\n"); } void print_hex(void *data, uint32_t length) { if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0) print_dwords(data, length); else print_bytes(data, length); } void read_controller_data(int fd, struct nvme_controller_data *cdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(1); pt.buf = cdata; pt.len = sizeof(*cdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_controller_data_swapbytes(cdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); pt.buf = nsdata; pt.len = sizeof(*nsdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_namespace_data_swapbytes(nsdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } int open_dev(const char *str, int *fd, int show_error, int exit_on_error) { char full_path[64]; if (!strnstr(str, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) { if (show_error) warnx("controller/namespace ids must begin with '%s'", NVME_CTRLR_PREFIX); if (exit_on_error) exit(1); else return (EINVAL); } snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str); *fd = open(full_path, O_RDWR); if (*fd < 0) { if (show_error) warn("could not open %s", full_path); if (exit_on_error) exit(1); else return (errno); } return (0); } void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid) { char *nsloc; /* * Pull the namespace id from the string. +2 skips past the "ns" part * of the string. Don't search past 10 characters into the string, * otherwise we know it is malformed. */ nsloc = strnstr(ns_str, NVME_NS_PREFIX, 10); if (nsloc != NULL) *nsid = strtol(nsloc + 2, NULL, 10); if (nsloc == NULL || (*nsid == 0 && errno != 0)) errx(1, "invalid namespace ID '%s'", ns_str); /* * The controller string will include only the nvmX part of the * nvmeXnsY string. */ snprintf(ctrlr_str, nsloc - ns_str + 1, "%s", ns_str); } /* * Loads all the .so's from the specified directory. */ static void load_dir(const char *dir) { DIR *d; struct dirent *dent; char *path = NULL; void *h; d = opendir(dir); if (d == NULL) return; for (dent = readdir(d); dent != NULL; dent = readdir(d)) { if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0) continue; asprintf(&path, "%s/%s", dir, dent->d_name); if (path == NULL) err(1, "Can't malloc for path, giving up."); if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL) warnx("Can't load %s: %s", path, dlerror()); else { /* * Add in the top (for cli commands) and logpage (for * logpage parsing) linker sets. We have to do this by * hand because linker sets aren't automatically merged. */ void *begin, *limit; begin = dlsym(h, "__start_set_top"); limit = dlsym(h, "__stop_set_top"); if (begin) add_to_top(begin, limit); begin = dlsym(h, "__start_set_logpage"); limit = dlsym(h, "__stop_set_logpage"); if (begin) add_to_logpage(begin, limit); } free(path); path = NULL; } closedir(d); } int main(int argc, char *argv[]) { add_to_top(NVME_CMD_BEGIN(top), NVME_CMD_LIMIT(top)); add_to_logpage(NVME_LOGPAGE_BEGIN, NVME_LOGPAGE_LIMIT); load_dir("/lib/nvmecontrol"); load_dir("/usr/local/lib/nvmecontrol"); if (argc < 2) gen_usage_set(top_begin(), top_limit()); dispatch_set(argc, argv, top_begin(), top_limit()); return (0); } Index: head/sbin/nvmecontrol/nvmecontrol.h =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.h (revision 341657) +++ head/sbin/nvmecontrol/nvmecontrol.h (revision 341658) @@ -1,152 +1,154 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __NVMECONTROL_H__ #define __NVMECONTROL_H__ #include #include struct nvme_function; -typedef void (*nvme_fn_t)(struct nvme_function *nf, int argc, char *argv[]); +typedef void (*nvme_fn_t)(const struct nvme_function *nf, int argc, char *argv[]); struct nvme_function { const char *name; nvme_fn_t fn; const char *usage; }; #define NVME_SETNAME(set) set #define NVME_CMDSET(set, sym) DATA_SET(NVME_SETNAME(set), sym) #define NVME_COMMAND(set, nam, function, usage_str) \ static struct nvme_function function ## _nvme_cmd = \ { .name = #nam, .fn = function, .usage = usage_str }; \ NVME_CMDSET(set, function ## _nvme_cmd) #define NVME_CMD_BEGIN(set) SET_BEGIN(NVME_SETNAME(set)) #define NVME_CMD_LIMIT(set) SET_LIMIT(NVME_SETNAME(set)) #define NVME_CMD_DECLARE(set, t) SET_DECLARE(NVME_SETNAME(set), t) typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size); struct logpage_function { uint8_t log_page; const char *vendor; const char *name; print_fn_t print_fn; size_t size; }; #define NVME_LOGPAGESET(sym) DATA_SET(NVME_SETNAME(logpage), sym) #define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \ static struct logpage_function unique ## _lpf = { \ .log_page = lp, \ .vendor = vend, \ .name = nam, \ .print_fn = fn, \ .size = sz, \ } ; \ NVME_LOGPAGESET(unique ## _lpf) #define NVME_LOGPAGE_BEGIN SET_BEGIN(NVME_SETNAME(logpage)) #define NVME_LOGPAGE_LIMIT SET_LIMIT(NVME_SETNAME(logpage)) #define NVME_LOGPAGE_DECLARE(t) SET_DECLARE(NVME_SETNAME(logpage), t) #define DEFAULT_SIZE (4096) struct kv_name { uint32_t key; const char *name; }; const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key); NVME_CMD_DECLARE(top, struct nvme_function); NVME_LOGPAGE_DECLARE(struct logpage_function); struct set_concat { void **begin; void **limit; }; void set_concat_add(struct set_concat *m, void *begin, void *end); #define SET_CONCAT_DEF(set, t) \ static struct set_concat set ## _concat; \ -static inline t **set ## _begin() { return ((t **)set ## _concat.begin); } \ -static inline t **set ## _limit() { return ((t **)set ## _concat.limit); } \ +static inline const t * const *set ## _begin() { return ((const t * const *)set ## _concat.begin); } \ +static inline const t * const *set ## _limit() { return ((const t * const *)set ## _concat.limit); } \ void add_to_ ## set(t **b, t **e) \ { \ set_concat_add(&set ## _concat, b, e); \ } #define SET_CONCAT_DECL(set, t) \ void add_to_ ## set(t **b, t **e) SET_CONCAT_DECL(top, struct nvme_function); SET_CONCAT_DECL(logpage, struct logpage_function); #define NVME_CTRLR_PREFIX "nvme" #define NVME_NS_PREFIX "ns" int open_dev(const char *str, int *fd, int show_error, int exit_on_error); void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid); void read_controller_data(int fd, struct nvme_controller_data *cdata); void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata); void print_hex(void *data, uint32_t length); void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size); void print_temp(uint16_t t); void usage(const struct nvme_function *f); -void dispatch_set(int argc, char *argv[], struct nvme_function **tbl, - struct nvme_function **tbl_limit); +void dispatch_set(int argc, char *argv[], const struct nvme_function * const *tbl, + const struct nvme_function * const *tbl_limit); -#define DISPATCH(argc, argv, set) \ - dispatch_set(argc, argv, NVME_CMD_BEGIN(set), NVME_CMD_LIMIT(set)) +#define DISPATCH(argc, argv, set) \ + dispatch_set(argc, argv, \ + (const struct nvme_function * const *)NVME_CMD_BEGIN(set), \ + (const struct nvme_function * const *)NVME_CMD_LIMIT(set)) \ /* Utility Routines */ /* * 128-bit integer augments to standard values. On i386 this * doesn't exist, so we use 64-bit values. So, on 32-bit i386, * you'll get truncated values until someone implement 128bit * ints in sofware. */ #define UINT128_DIG 39 #ifdef __i386__ typedef uint64_t uint128_t; #else typedef __uint128_t uint128_t; #endif static __inline uint128_t to128(void *p) { return *(uint128_t *)p; } uint64_t le48dec(const void *pp); char * uint128_to_str(uint128_t u, char *buf, size_t buflen); #endif Index: head/sbin/nvmecontrol/perftest.c =================================================================== --- head/sbin/nvmecontrol/perftest.c (revision 341657) +++ head/sbin/nvmecontrol/perftest.c (revision 341658) @@ -1,179 +1,179 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define PERFTEST_USAGE \ "perftest <-n num_threads> <-o read|write>\n" \ " <-s size_in_bytes> <-t time_in_seconds>\n" \ " <-i intr|wait> [-f refthread] [-p]\n" \ " \n" 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(struct nvme_function *nf, int argc, char *argv[]) +perftest(const struct nvme_function *nf, int argc, char *argv[]) { struct nvme_io_test io_test; int fd; int opt; char *p; u_long ioctl_cmd = NVME_IO_TEST; bool nflag, oflag, sflag, tflag; int perthread = 0; nflag = oflag = sflag = tflag = false; memset(&io_test, 0, sizeof(io_test)); while ((opt = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) { switch (opt) { case 'f': if (!strcmp(optarg, "refthread")) io_test.flags |= NVME_TEST_FLAG_REFTHREAD; break; case 'i': if (!strcmp(optarg, "bio") || !strcmp(optarg, "wait")) ioctl_cmd = NVME_BIO_TEST; else if (!strcmp(optarg, "io") || !strcmp(optarg, "intr")) ioctl_cmd = NVME_IO_TEST; break; case 'n': nflag = true; io_test.num_threads = strtoul(optarg, &p, 0); if (p != NULL && *p != '\0') { fprintf(stderr, "\"%s\" not valid number of threads.\n", optarg); usage(nf); } else if (io_test.num_threads == 0 || io_test.num_threads > 128) { fprintf(stderr, "\"%s\" not valid number of threads.\n", optarg); usage(nf); } break; case 'o': oflag = true; if (!strcmp(optarg, "read") || !strcmp(optarg, "READ")) io_test.opc = NVME_OPC_READ; else if (!strcmp(optarg, "write") || !strcmp(optarg, "WRITE")) io_test.opc = NVME_OPC_WRITE; else { fprintf(stderr, "\"%s\" not valid opcode.\n", optarg); usage(nf); } break; case 'p': perthread = 1; break; case 's': sflag = true; io_test.size = strtoul(optarg, &p, 0); if (p == NULL || *p == '\0' || toupper(*p) == 'B') { // do nothing } else if (toupper(*p) == 'K') { io_test.size *= 1024; } else if (toupper(*p) == 'M') { io_test.size *= 1024 * 1024; } else { fprintf(stderr, "\"%s\" not valid size.\n", optarg); usage(nf); } break; case 't': tflag = true; io_test.time = strtoul(optarg, &p, 0); if (p != NULL && *p != '\0') { fprintf(stderr, "\"%s\" not valid time duration.\n", optarg); usage(nf); } break; } } if (!nflag || !oflag || !sflag || !tflag || optind >= argc) usage(nf); 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); } NVME_COMMAND(top, perftest, perftest, PERFTEST_USAGE); Index: head/sbin/nvmecontrol/power.c =================================================================== --- head/sbin/nvmecontrol/power.c (revision 341657) +++ head/sbin/nvmecontrol/power.c (revision 341658) @@ -1,192 +1,192 @@ /*- * Copyright (c) 2016 Netflix, Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" _Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY, "nvme_power_state size wrong"); #define POWER_USAGE \ "power [-l] [-p new-state [-w workload-hint]] \n" static void power_list_one(int i, struct nvme_power_state *nps) { int mpower, apower, ipower; uint8_t mps, nops, aps, apw; mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) & NVME_PWR_ST_MPS_MASK; nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) & NVME_PWR_ST_NOPS_MASK; apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) & NVME_PWR_ST_APW_MASK; aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) & NVME_PWR_ST_APS_MASK; mpower = nps->mp; if (mps == 0) mpower *= 100; ipower = nps->idlp; if (nps->ips == 1) ipower *= 100; apower = nps->actp; if (aps == 1) apower *= 100; printf("%2d: %2d.%04dW%c %3d.%03dms %3d.%03dms %2d %2d %2d %2d %2d.%04dW %2d.%04dW %d\n", i, mpower / 10000, mpower % 10000, nops ? '*' : ' ', nps->enlat / 1000, nps->enlat % 1000, nps->exlat / 1000, nps->exlat % 1000, nps->rrt, nps->rrl, nps->rwt, nps->rwl, ipower / 10000, ipower % 10000, apower / 10000, apower % 10000, apw); } static void power_list(struct nvme_controller_data *cdata) { int i; printf("\nPower States Supported: %d\n\n", cdata->npss + 1); printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workloadd\n"); printf("-- -------- --------- --------- -- -- -- -- -------- -------- --\n"); for (i = 0; i <= cdata->npss; i++) power_list_one(i, &cdata->power_state[i]); } static void power_set(int fd, int power_val, int workload, int perm) { struct nvme_pt_command pt; uint32_t p; p = perm ? (1u << 31) : 0; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_SET_FEATURES; pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT | p); pt.cmd.cdw11 = htole32(power_val | (workload << 5)); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "set feature power mgmt request returned error"); } static void power_show(int fd) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_GET_FEATURES; pt.cmd.cdw10 = htole32(NVME_FEAT_POWER_MANAGEMENT); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "set feature power mgmt request returned error"); printf("Current Power Mode is %d\n", pt.cpl.cdw0); } static void -power(struct nvme_function *nf, int argc, char *argv[]) +power(const struct nvme_function *nf, int argc, char *argv[]) { struct nvme_controller_data 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 ((char)ch) { case 'l': listflag = 1; break; case 'p': powerflag = 1; power_val = strtol(optarg, &end, 0); if (*end != '\0') { fprintf(stderr, "Invalid power state number: %s\n", optarg); usage(nf); } break; case 'w': workload = strtol(optarg, &end, 0); if (*end != '\0') { fprintf(stderr, "Invalid workload hint: %s\n", optarg); usage(nf); } break; default: usage(nf); } } /* Check that a controller was specified. */ if (optind >= argc) usage(nf); if (listflag && powerflag) { fprintf(stderr, "Can't set power and list power states\n"); usage(nf); } 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); } NVME_COMMAND(top, power, power, POWER_USAGE); Index: head/sbin/nvmecontrol/reset.c =================================================================== --- head/sbin/nvmecontrol/reset.c (revision 341657) +++ head/sbin/nvmecontrol/reset.c (revision 341658) @@ -1,70 +1,70 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define RESET_USAGE \ "reset \n" static void -reset(struct nvme_function *nf, int argc, char *argv[]) +reset(const struct nvme_function *nf, int argc, char *argv[]) { int ch, fd; while ((ch = getopt(argc, argv, "")) != -1) { switch ((char)ch) { default: usage(nf); } } /* Check that a controller was specified. */ if (optind >= argc) usage(nf); open_dev(argv[optind], &fd, 1, 1); if (ioctl(fd, NVME_RESET_CONTROLLER) < 0) err(1, "reset request to %s failed", argv[optind]); exit(0); } NVME_COMMAND(top, reset, reset, RESET_USAGE); Index: head/sbin/nvmecontrol/wdc.c =================================================================== --- head/sbin/nvmecontrol/wdc.c (revision 341657) +++ head/sbin/nvmecontrol/wdc.c (revision 341658) @@ -1,597 +1,597 @@ /*- * Copyright (c) 2017 Netflix, Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define WDC_USAGE \ "wdc (cap-diag)\n" NVME_CMD_DECLARE(wdc, struct nvme_function); #define WDC_NVME_TOC_SIZE 8 #define WDC_NVME_CAP_DIAG_OPCODE 0xe6 #define WDC_NVME_CAP_DIAG_CMD 0x0000 -static void wdc_cap_diag(struct nvme_function *nf, int argc, char *argv[]); +static void wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]); #define WDC_CAP_DIAG_USAGE "wdc cap-diag [-o path-template]\n" NVME_COMMAND(wdc, cap-diag, wdc_cap_diag, WDC_CAP_DIAG_USAGE); static void wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix) { struct nvme_controller_data cdata; char sn[NVME_SERIAL_NUMBER_LENGTH + 1]; char *walker; len -= strlen(buf); buf += strlen(buf); read_controller_data(fd, &cdata); memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH); walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1; while (walker > sn && *walker == ' ') walker--; *++walker = '\0'; snprintf(buf, len, "%s%s.bin", sn, suffix); } static void wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd, uint8_t *buffer, size_t buflen) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = opcode; pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); /* - 1 like all the others ??? */ pt.cmd.cdw11 = htole32(off / sizeof(uint32_t)); pt.cmd.cdw12 = htole32(cmd); pt.buf = buffer; pt.len = buflen; pt.is_read = 1; // printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n", // (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "wdc_get_data request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "wdc_get_data request returned error"); } static void wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode, uint32_t cmd, int len_off) { int first; int fd2; uint8_t *buf; uint32_t len, offset; size_t resid; wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix); /* XXX overwrite protection? */ fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd2 < 0) err(1, "open %s", tmpl); buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE); if (buf == NULL) errx(1, "Can't get buffer to read dump"); offset = 0; len = NVME_MAX_XFER_SIZE; first = 1; do { resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len; wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid); if (first) { len = be32dec(buf + len_off); if (len == 0) errx(1, "No data for %s", suffix); if (memcmp("E6LG", buf, 4) != 0) printf("Expected header of E6LG, found '%4.4s' instead\n", buf); printf("Dumping %d bytes of version %d.%d log to %s\n", len, buf[8], buf[9], tmpl); /* * Adjust amount to dump if total dump < 1MB, * though it likely doesn't matter to the WDC * analysis tools. */ if (resid > len) resid = len; first = 0; } if (write(fd2, buf, resid) != (ssize_t)resid) err(1, "write"); offset += resid; len -= resid; } while (len > 0); free(buf); close(fd2); } static void -wdc_cap_diag(struct nvme_function *nf, int argc, char *argv[]) +wdc_cap_diag(const struct nvme_function *nf, int argc, char *argv[]) { char path_tmpl[MAXPATHLEN]; int ch, fd; path_tmpl[0] = '\0'; while ((ch = getopt(argc, argv, "o:")) != -1) { switch ((char)ch) { case 'o': strlcpy(path_tmpl, optarg, MAXPATHLEN); break; default: usage(nf); } } /* Check that a controller was specified. */ if (optind >= argc) usage(nf); open_dev(argv[optind], &fd, 1, 1); wdc_do_dump(fd, path_tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE, WDC_NVME_CAP_DIAG_CMD, 4); close(fd); exit(1); } static void -wdc(struct nvme_function *nf __unused, int argc, char *argv[]) +wdc(const struct nvme_function *nf __unused, int argc, char *argv[]) { DISPATCH(argc, argv, wdc); } /* * HGST's 0xc1 page. This is a grab bag of additional data. Please see * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf * Appendix A for details */ typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); struct subpage_print { uint16_t key; subprint_fn_t fn; }; static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); static struct subpage_print hgst_subpage[] = { { 0x02, print_hgst_info_write_errors }, { 0x03, print_hgst_info_read_errors }, { 0x05, print_hgst_info_verify_errors }, { 0x10, print_hgst_info_self_test }, { 0x15, print_hgst_info_background_scan }, { 0x30, print_hgst_info_erase_errors }, { 0x31, print_hgst_info_erase_counts }, { 0x32, print_hgst_info_temp_history }, { 0x37, print_hgst_info_ssd_perf }, { 0x38, print_hgst_info_firmware_load }, }; /* Print a subpage that is basically just key value pairs */ static void print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, const struct kv_name *kv, size_t kv_count) { uint8_t *wsp, *esp; uint16_t ptype; uint8_t plen; uint64_t param; int i; wsp = buf; esp = wsp + size; while (wsp < esp) { ptype = le16dec(wsp); wsp += 2; wsp++; /* Flags, just ignore */ plen = *wsp++; param = 0; for (i = 0; i < plen; i++) param |= (uint64_t)*wsp++ << (i * 8); printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); } } static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Writes" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Flash Write Commands" }, { 0x8001, "HGST Special" }, }; printf("Write Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Reads" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Flash Read Commands" }, { 0x8001, "XOR Recovered" }, { 0x8002, "Total Corrected Bits" }, }; printf("Read Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Reads" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Commands Processed" }, }; printf("Verify Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) { size_t i; uint8_t *walker = buf; uint16_t code, hrs; uint32_t lba; printf("Self Test Subpage:\n"); for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ code = le16dec(walker); walker += 2; walker++; /* Ignore fixed flags */ if (*walker == 0) /* Last entry is zero length */ break; if (*walker++ != 0x10) { printf("Bad length for self test report\n"); return; } printf(" %-30s: %d\n", "Recent Test", code); printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); walker++; printf(" %-28s: %#x\n", "Self-Test Number", *walker++); hrs = le16dec(walker); walker += 2; lba = le32dec(walker); walker += 4; printf(" %-28s: %u\n", "Total Power On Hrs", hrs); printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); } } static void print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) { uint8_t *walker = buf; uint8_t status; uint16_t code, nscan, progress; uint32_t pom, nand; printf("Background Media Scan Subpage:\n"); /* Decode the header */ code = le16dec(walker); walker += 2; walker++; /* Ignore fixed flags */ if (*walker++ != 0x10) { printf("Bad length for background scan header\n"); return; } if (code != 0) { printf("Expceted code 0, found code %#x\n", code); return; } pom = le32dec(walker); walker += 4; walker++; /* Reserved */ status = *walker++; nscan = le16dec(walker); walker += 2; progress = le16dec(walker); walker += 2; walker += 6; /* Reserved */ printf(" %-30s: %d\n", "Power On Minutes", pom); printf(" %-30s: %x (%s)\n", "BMS Status", status, status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); printf(" %-30s: %d\n", "Number of BMS", nscan); printf(" %-30s: %d\n", "Progress Current BMS", progress); /* Report retirements */ if (walker - (uint8_t *)buf != 20) { printf("Coding error, offset not 20\n"); return; } size -= 20; printf(" %-30s: %d\n", "BMS retirements", size / 0x18); while (size > 0) { code = le16dec(walker); walker += 2; walker++; if (*walker++ != 0x14) { printf("Bad length parameter\n"); return; } pom = le32dec(walker); walker += 4; /* * Spec sheet says the following are hard coded, if true, just * print the NAND retirement. */ if (walker[0] == 0x41 && walker[1] == 0x0b && walker[2] == 0x01 && walker[3] == 0x00 && walker[4] == 0x00 && walker[5] == 0x00 && walker[6] == 0x00 && walker[7] == 0x00) { walker += 8; walker += 4; /* Skip reserved */ nand = le32dec(walker); walker += 4; printf(" %-30s: %d\n", "Retirement number", code); printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); } else { printf("Parameter %#x entry corrupt\n", code); walker += 16; } } } static void print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) { static struct kv_name kv[] = { { 0x0000, "Corrected Without Delay" }, { 0x0001, "Corrected Maybe Delayed" }, { 0x0002, "Re-Erase" }, { 0x0003, "Errors Corrected" }, { 0x0004, "Correct Algorithm Used" }, { 0x0005, "Bytes Processed" }, { 0x0006, "Uncorrected Errors" }, { 0x8000, "Flash Erase Commands" }, { 0x8001, "Mfg Defect Count" }, { 0x8002, "Grown Defect Count" }, { 0x8003, "Erase Count -- User" }, { 0x8004, "Erase Count -- System" }, }; printf("Erase Errors Subpage:\n"); print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); } static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) { /* My drive doesn't export this -- so not coding up */ printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); } static void print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) { uint8_t *walker = buf; uint32_t min; printf("Temperature History:\n"); printf(" %-30s: %d C\n", "Current Temperature", *walker++); printf(" %-30s: %d C\n", "Reference Temperature", *walker++); printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); min = le32dec(walker); walker += 4; printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); min = le32dec(walker); walker += 4; printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60); min = le32dec(walker); walker += 4; printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); } static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) { uint8_t *walker = buf; uint64_t val; printf("SSD Performance Subpage Type %d:\n", res); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Read Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Read Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Read Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Read Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Write Commands", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Write Blocks", val); val = le64dec(walker); walker += 8; printf(" %-30s: %ju\n", "NAND Read Before Writes", val); } static void print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) { uint8_t *walker = buf; printf("Firmware Load Subpage:\n"); printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); } static void kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) { size_t i; for (i = 0; i < nsp; i++, sp++) { if (sp->key == subtype) { sp->fn(buf, subtype, res, size); return; } } printf("No handler for page type %x\n", subtype); } static void print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { uint8_t *walker, *end, *subpage; int pages; uint16_t len; uint8_t subtype, res; printf("HGST Extra Info Log\n"); printf("===================\n"); walker = buf; pages = *walker++; walker++; len = le16dec(walker); walker += 2; end = walker + len; /* Length is exclusive of this header */ while (walker < end) { subpage = walker + 4; subtype = *walker++ & 0x3f; /* subtype */ res = *walker++; /* Reserved */ len = le16dec(walker); walker += len + 2; /* Length, not incl header */ if (walker > end) { printf("Ooops! Off the end of the list\n"); break; } kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); } } NVME_LOGPAGE(hgst_info, HGST_INFO_LOG, "hgst", "Detailed Health/SMART", print_hgst_info_log, DEFAULT_SIZE); NVME_LOGPAGE(wdc_info, HGST_INFO_LOG, "wdc", "Detailed Health/SMART", print_hgst_info_log, DEFAULT_SIZE); NVME_COMMAND(top, wdc, wdc, WDC_USAGE);