diff --git a/sbin/nvmecontrol/devlist.c b/sbin/nvmecontrol/devlist.c index 7e3dfe60fff4..b5e95b38937e 100644 --- a/sbin/nvmecontrol/devlist.c +++ b/sbin/nvmecontrol/devlist.c @@ -1,157 +1,155 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #include "comnd.h" /* Tables for command line parsing */ #define NVME_MAX_UNIT 256 static cmd_fn_t devlist; static struct options { bool human; } opt = { .human = false, }; static const struct opts devlist_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("human", 'h', arg_none, opt, human, "Show human readable disk size"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static struct cmd devlist_cmd = { .name = "devlist", .fn = devlist, .descr = "List NVMe controllers and namespaces", .ctx_size = sizeof(opt), .opts = devlist_opts, .args = NULL, }; CMD_COMMAND(devlist_cmd); /* End of tables for command line parsing */ static inline uint32_t ns_get_sector_size(struct nvme_namespace_data *nsdata) { uint8_t flbas_fmt, lbads; - flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & - NVME_NS_DATA_FLBAS_FORMAT_MASK; - lbads = (nsdata->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & - NVME_NS_DATA_LBAF_LBADS_MASK; + flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas); + lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, nsdata->lbaf[flbas_fmt]); return (1 << lbads); } static void devlist(const struct cmd *f, int argc, char *argv[]) { struct nvme_controller_data cdata; struct nvme_namespace_data nsdata; char name[64]; uint8_t mn[64]; uint8_t buf[7]; uint32_t i; uint64_t size; int ctrlr, fd, found, ret; if (arg_parse(argc, argv, f)) return; ctrlr = -1; found = 0; while (ctrlr < NVME_MAX_UNIT) { ctrlr++; sprintf(name, "%s%d", NVME_CTRLR_PREFIX, ctrlr); ret = open_dev(name, &fd, 0, 0); if (ret == EACCES) { warnx("could not open "_PATH_DEV"%s\n", name); continue; } else if (ret != 0) continue; found++; if (read_controller_data(fd, &cdata)) continue; nvme_strvis(mn, cdata.mn, sizeof(mn), NVME_MODEL_NUMBER_LENGTH); printf("%6s: %s\n", name, mn); for (i = 0; i < cdata.nn; i++) { if (read_namespace_data(fd, i + 1, &nsdata)) continue; if (nsdata.nsze == 0) continue; sprintf(name, "%s%d%s%d", NVME_CTRLR_PREFIX, ctrlr, NVME_NS_PREFIX, i + 1); size = nsdata.nsze * (uint64_t)ns_get_sector_size(&nsdata); if (opt.human) { humanize_number(buf, sizeof(buf), size, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); printf(" %10s (%s)\n", name, buf); } else { printf(" %10s (%juMB)\n", name, (uintmax_t)size / 1024 / 1024); } } close(fd); } if (found == 0) { printf("No NVMe controllers found.\n"); exit(EX_UNAVAILABLE); } exit(0); } diff --git a/sbin/nvmecontrol/firmware.c b/sbin/nvmecontrol/firmware.c index 80bbff032cf2..8fcc4ba4f46e 100644 --- a/sbin/nvmecontrol/firmware.c +++ b/sbin/nvmecontrol/firmware.c @@ -1,369 +1,366 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* Tables for command line parsing */ static cmd_fn_t firmware; #define NONE 0xffffffffu static struct options { bool activate; uint32_t slot; const char *fw_img; const char *dev; } opt = { .activate = false, .slot = NONE, .fw_img = NULL, .dev = NULL, }; static const struct opts firmware_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("activate", 'a', arg_none, opt, activate, "Attempt to activate firmware"), OPT("slot", 's', arg_uint32, opt, slot, "Slot to activate and/or download firmware to"), OPT("firmware", 'f', arg_path, opt, fw_img, "Firmware image to download"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args firmware_args[] = { { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd firmware_cmd = { .name = "firmware", .fn = firmware, .descr = "Download firmware image to controller", .ctx_size = sizeof(opt), .opts = firmware_opts, .args = firmware_args, }; CMD_COMMAND(firmware_cmd); /* End of tables for command line parsing */ static int slot_has_valid_firmware(int fd, int slot) { struct nvme_firmware_page fw; int has_fw = false; read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &fw, sizeof(fw)); if (fw.revision[slot-1][0] != '\0') has_fw = true; return (has_fw); } static void read_image_file(const char *path, void **buf, int32_t *size) { struct stat sb; int32_t filesize; int fd; *size = 0; *buf = NULL; if ((fd = open(path, O_RDONLY)) < 0) err(EX_NOINPUT, "unable to open '%s'", path); if (fstat(fd, &sb) < 0) err(EX_NOINPUT, "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(EX_USAGE, "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(EX_OSERR, "unable to malloc %d bytes", filesize); if ((*size = read(fd, *buf, filesize)) < 0) err(EX_IOERR, "error reading '%s'", path); /* XXX assuming no short reads */ if (*size != filesize) errx(EX_IOERR, "error reading '%s' (read %d bytes, requested %d bytes)", path, *size, filesize); close(fd); } static void update_firmware(int fd, uint8_t *payload, int32_t payload_size, uint8_t fwug) { struct nvme_pt_command pt; uint64_t max_xfer_size; int32_t off; uint32_t resid, size; void *chunk; off = 0; resid = payload_size; if (ioctl(fd, NVME_GET_MAX_XFER_SIZE, &max_xfer_size) < 0) err(EX_IOERR, "query max transfer size failed"); if (fwug != 0 && fwug != 0xFF) max_xfer_size = MIN(max_xfer_size, (uint64_t)fwug << 12); if ((chunk = aligned_alloc(PAGE_SIZE, max_xfer_size)) == NULL) errx(EX_OSERR, "unable to malloc %zd bytes", (size_t)max_xfer_size); while (resid > 0) { size = (resid >= max_xfer_size) ? 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(EX_IOERR, "firmware download request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "firmware download request returned error"); resid -= size; off += size; } free(chunk); } 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(EX_IOERR, "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(EX_IOERR, "firmware activate request returned error"); return 0; } static void firmware(const struct cmd *f, int argc, char *argv[]) { int fd = -1; int activate_action, reboot_required; char prompt[64]; void *buf = NULL; char *path; int32_t size = 0, nsid; uint16_t oacs_fw; uint8_t fw_slot1_ro, fw_num_slots; struct nvme_controller_data cdata; if (arg_parse(argc, argv, f)) return; if (opt.slot == 0) { fprintf(stderr, "0 is not a valid slot number. " "Slot numbers start at 1.\n"); arg_help(argc, argv, f); } else if (opt.slot > 7 && opt.slot != NONE) { fprintf(stderr, "Slot number %s specified which is " "greater than max allowed slot number of " "7.\n", optarg); arg_help(argc, argv, f); } if (!opt.activate && opt.fw_img == NULL) { fprintf(stderr, "Neither a replace ([-f path_to_firmware]) nor " "activate ([-a]) firmware image action\n" "was specified.\n"); arg_help(argc, argv, f); } if (opt.activate && opt.fw_img == NULL && opt.slot == 0) { fprintf(stderr, "Slot number to activate not specified.\n"); arg_help(argc, argv, f); } open_dev(opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } free(path); if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); - oacs_fw = (cdata.oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & - NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; + oacs_fw = NVMEV(NVME_CTRLR_DATA_OACS_FIRMWARE, cdata.oacs); if (oacs_fw == 0) errx(EX_UNAVAILABLE, "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; + fw_slot1_ro = NVMEV(NVME_CTRLR_DATA_FRMW_SLOT1_RO, cdata.frmw); if (opt.fw_img && opt.slot == 1 && fw_slot1_ro) errx(EX_UNAVAILABLE, "slot %d is marked as read only", opt.slot); - fw_num_slots = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & - NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; + fw_num_slots = NVMEV(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, cdata.frmw); if (opt.slot > fw_num_slots) errx(EX_UNAVAILABLE, "slot %d specified but controller only supports %d slots", opt.slot, fw_num_slots); if (opt.activate && opt.fw_img == NULL && !slot_has_valid_firmware(fd, opt.slot)) errx(EX_UNAVAILABLE, "slot %d does not contain valid firmware,\n" "try 'nvmecontrol logpage -p 3 %s' to get a list " "of available images\n", opt.slot, opt.dev); if (opt.fw_img) read_image_file(opt.fw_img, &buf, &size); if (opt.fw_img != NULL&& opt.activate) printf("You are about to download and activate " "firmware image (%s) to controller %s.\n" "This may damage your controller and/or " "overwrite an existing firmware image.\n", opt.fw_img, opt.dev); else if (opt.activate) printf("You are about to activate a new firmware " "image on controller %s.\n" "This may damage your controller.\n", opt.dev); else if (opt.fw_img != NULL) printf("You are about to download firmware image " "(%s) to controller %s.\n" "This may damage your controller and/or " "overwrite an existing firmware image.\n", opt.fw_img, opt.dev); printf("Are you sure you want to continue? (yes/no) "); while (1) { fgets(prompt, sizeof(prompt), stdin); if (strncasecmp(prompt, "yes", 3) == 0) break; if (strncasecmp(prompt, "no", 2) == 0) exit(EX_DATAERR); printf("Please answer \"yes\" or \"no\". "); } if (opt.fw_img != NULL) { update_firmware(fd, buf, size, cdata.fwug); if (opt.activate) activate_action = NVME_AA_REPLACE_ACTIVATE; else activate_action = NVME_AA_REPLACE_NO_ACTIVATE; } else { activate_action = NVME_AA_ACTIVATE; } reboot_required = activate_firmware(fd, opt.slot, activate_action); if (opt.activate) { if (reboot_required) { printf("New firmware image activated but requires " "conventional reset (i.e. reboot) to " "complete activation.\n"); } else { printf("New firmware image activated and will take " "effect after next controller reset.\n" "Controller reset can be initiated via " "'nvmecontrol reset %s'\n", opt.dev); } } close(fd); exit(0); } diff --git a/sbin/nvmecontrol/format.c b/sbin/nvmecontrol/format.c index 541b207b373f..a56027089ff5 100644 --- a/sbin/nvmecontrol/format.c +++ b/sbin/nvmecontrol/format.c @@ -1,221 +1,216 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2018-2019 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. * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define NONE 0xffffffffu #define SES_NONE 0 #define SES_USER 1 #define SES_CRYPTO 2 /* Tables for command line parsing */ static cmd_fn_t format; static struct options { uint32_t lbaf; uint32_t ms; uint32_t pi; uint32_t pil; uint32_t ses; bool Eflag; bool Cflag; const char *dev; } opt = { .lbaf = NONE, .ms = NONE, .pi = NONE, .pil = NONE, .ses = SES_NONE, .Eflag = false, .Cflag = false, .dev = NULL, }; static const struct opts format_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("crypto", 'C', arg_none, opt, Cflag, "Crptographic erase"), OPT("erase", 'E', arg_none, opt, Eflag, "User data erase"), OPT("lbaf", 'f', arg_uint32, opt, lbaf, "LBA Format to apply to the media"), OPT("ms", 'm', arg_uint32, opt, ms, "Metadata settings"), OPT("pi", 'p', arg_uint32, opt, pi, "Protective information"), OPT("pil", 'l', arg_uint32, opt, pil, "Protective information location"), OPT("ses", 's', arg_uint32, opt, ses, "Secure erase settings"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args format_args[] = { { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd format_cmd = { .name = "format", .fn = format, .descr = "Format/erase one or all the namespaces", .ctx_size = sizeof(opt), .opts = format_opts, .args = format_args, }; CMD_COMMAND(format_cmd); /* End of tables for command line parsing */ static void format(const struct cmd *f, int argc, char *argv[]) { struct nvme_controller_data cd; struct nvme_namespace_data nsd; struct nvme_pt_command pt; char *path; const char *target; uint32_t nsid; int lbaf, ms, pi, pil, ses, fd; if (arg_parse(argc, argv, f)) return; if ((int)opt.Eflag + opt.Cflag + (opt.ses != SES_NONE) > 1) { fprintf(stderr, "Only one of -E, -C or -s may be specified\n"); arg_help(argc, argv, f); } target = opt.dev; lbaf = opt.lbaf; ms = opt.ms; pi = opt.pi; pil = opt.pil; if (opt.Eflag) ses = SES_USER; else if (opt.Cflag) ses = SES_CRYPTO; else ses = opt.ses; open_dev(target, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid == 0) { 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); open_dev(path, &fd, 1, 1); } free(path); /* Check that controller can execute this command. */ if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) & - NVME_CTRLR_DATA_OACS_FORMAT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_FORMAT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support format"); - if (((cd.fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) & - NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == SES_CRYPTO) + if (NVMEV(NVME_CTRLR_DATA_FNA_CRYPTO_ERASE, cd.fna) == 0 && + ses == SES_CRYPTO) errx(EX_UNAVAILABLE, "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 == SES_NONE) + if (NVMEV(NVME_CTRLR_DATA_FNA_FORMAT_ALL, cd.fna) && + ses == SES_NONE) errx(EX_UNAVAILABLE, "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 != SES_NONE) + if (NVMEV(NVME_CTRLR_DATA_FNA_ERASE_ALL, cd.fna) && + ses != SES_NONE) errx(EX_UNAVAILABLE, "controller does not support per-NS erase"); /* Try to keep previous namespace parameters. */ if (read_namespace_data(fd, nsid, &nsd)) errx(EX_IOERR, "Identify request failed"); if (lbaf < 0) - lbaf = (nsd.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) - & NVME_NS_DATA_FLBAS_FORMAT_MASK; + lbaf = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsd.flbas); if (lbaf > nsd.nlbaf) errx(EX_USAGE, "LBA format is out of range"); if (ms < 0) - ms = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT) - & NVME_NS_DATA_FLBAS_EXTENDED_MASK; + ms = NVMEV(NVME_NS_DATA_FLBAS_EXTENDED, nsd.flbas); if (pi < 0) - pi = (nsd.dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) - & NVME_NS_DATA_DPS_MD_START_MASK; + pi = NVMEV(NVME_NS_DATA_DPS_MD_START, nsd.dps); if (pil < 0) - pil = (nsd.dps >> NVME_NS_DATA_DPS_PIT_SHIFT) - & NVME_NS_DATA_DPS_PIT_MASK; + pil = NVMEV(NVME_NS_DATA_DPS_PIT, nsd.dps); } else { /* We have no previous parameters, so default to zeroes. */ if (lbaf < 0) lbaf = 0; if (ms < 0) ms = 0; if (pi < 0) pi = 0; if (pil < 0) pil = 0; } memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_FORMAT_NVM; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32((ses << 9) + (pil << 8) + (pi << 5) + (ms << 4) + lbaf); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "format request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "format request returned error"); close(fd); exit(0); } diff --git a/sbin/nvmecontrol/identify.c b/sbin/nvmecontrol/identify.c index 50698e8bfc70..81aebad0e0d6 100644 --- a/sbin/nvmecontrol/identify.c +++ b/sbin/nvmecontrol/identify.c @@ -1,305 +1,294 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * Copyright (C) 2018-2019 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. * 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 #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #include "nvmecontrol_ext.h" #define NONE 0xfffffffeu static struct options { bool hex; bool verbose; const char *dev; uint32_t nsid; } opt = { .hex = false, .verbose = false, .dev = NULL, .nsid = NONE, }; void print_namespace(struct nvme_namespace_data *nsdata) { char cbuf[UINT128_DIG + 1]; uint32_t i; uint32_t lbaf, lbads, ms, rp; uint8_t thin_prov, ptype; uint8_t flbas_fmt, t; - thin_prov = (nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_THIN_PROV_SHIFT) & - NVME_NS_DATA_NSFEAT_THIN_PROV_MASK; + thin_prov = NVMEV(NVME_NS_DATA_NSFEAT_THIN_PROV, nsdata->nsfeat); - flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & - NVME_NS_DATA_FLBAS_FORMAT_MASK; + flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas); printf("Size: %lld blocks\n", (long long)nsdata->nsze); printf("Capacity: %lld blocks\n", (long long)nsdata->ncap); printf("Utilization: %lld blocks\n", (long long)nsdata->nuse); 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", flbas_fmt); - if (nsdata->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_MS_SHIFT & NVME_NS_DATA_LBAF_MS_MASK) - printf(" %s metadata\n", nsdata->flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT & - NVME_NS_DATA_FLBAS_EXTENDED_MASK ? "Extended" : "Separate"); + if (NVMEV(NVME_NS_DATA_LBAF_MS, nsdata->lbaf[flbas_fmt]) != 0) + printf(" %s metadata\n", + NVMEV(NVME_NS_DATA_FLBAS_EXTENDED, nsdata->flbas) != 0 ? + "Extended" : "Separate"); else printf("\n"); printf("Metadata Capabilities\n"); printf(" Extended: %s\n", - nsdata->mc >> NVME_NS_DATA_MC_EXTENDED_SHIFT & NVME_NS_DATA_MC_EXTENDED_MASK ? "Supported" : "Not Supported"); + NVMEV(NVME_NS_DATA_MC_EXTENDED, nsdata->mc) != 0 ? "Supported" : + "Not Supported"); printf(" Separate: %s\n", - nsdata->mc >> NVME_NS_DATA_MC_POINTER_SHIFT & NVME_NS_DATA_MC_POINTER_MASK ? "Supported" : "Not Supported"); + NVMEV(NVME_NS_DATA_MC_POINTER, nsdata->mc) != 0 ? "Supported" : + "Not Supported"); 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_PIT1_SHIFT) & - NVME_NS_DATA_DPC_PIT1_MASK) ? "Type 1" : ""); + NVMEV(NVME_NS_DATA_DPC_MD_END, nsdata->dpc) != 0 ? "Last Bytes, " : + "", + NVMEV(NVME_NS_DATA_DPC_MD_START, nsdata->dpc) != 0 ? + "First Bytes, " : "", + NVMEV(NVME_NS_DATA_DPC_PIT3, nsdata->dpc) != 0 ? "Type 3, " : "", + NVMEV(NVME_NS_DATA_DPC_PIT2, nsdata->dpc) != 0 ? "Type 2, " : "", + NVMEV(NVME_NS_DATA_DPC_PIT1, nsdata->dpc) != 0 ? "Type 1" : ""); printf("Data Protection Settings: "); - ptype = (nsdata->dps >> NVME_NS_DATA_DPS_PIT_SHIFT) & - NVME_NS_DATA_DPS_PIT_MASK; - if (ptype) { + ptype = NVMEV(NVME_NS_DATA_DPS_PIT, nsdata->dps); + if (ptype != 0) { 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"); + NVMEV(NVME_NS_DATA_DPS_MD_START, nsdata->dps) != 0 ? + "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" : ""); + NVMEV(NVME_NS_DATA_NMIC_MAY_BE_SHARED, nsdata->nmic) != 0 ? + "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" : ""); + NVMEV(NVME_NS_DATA_RESCAP_IEKEY13, nsdata->rescap) != 0 ? + "IEKEY13, " : "", + NVMEV(NVME_NS_DATA_RESCAP_EX_AC_AR, nsdata->rescap) != 0 ? + "EX_AC_AR, " : "", + NVMEV(NVME_NS_DATA_RESCAP_WR_EX_AR, nsdata->rescap) != 0 ? + "WR_EX_AR, " : "", + NVMEV(NVME_NS_DATA_RESCAP_EX_AC_RO, nsdata->rescap) != 0 ? + "EX_AC_RO, " : "", + NVMEV(NVME_NS_DATA_RESCAP_WR_EX_RO, nsdata->rescap) != 0 ? + "WR_EX_RO, " : "", + NVMEV(NVME_NS_DATA_RESCAP_EX_AC, nsdata->rescap) != 0 ? + "EX_AC, " : "", + NVMEV(NVME_NS_DATA_RESCAP_WR_EX, nsdata->rescap) != 0 ? + "WR_EX, " : "", + NVMEV(NVME_NS_DATA_RESCAP_PTPL, nsdata->rescap) != 0 ? "PTPL" : ""); printf("Format Progress Indicator: "); - if ((nsdata->fpi >> NVME_NS_DATA_FPI_SUPP_SHIFT) & - NVME_NS_DATA_FPI_SUPP_MASK) { + if (NVMEV(NVME_NS_DATA_FPI_SUPP, nsdata->fpi) != 0) { printf("%u%% remains\n", - (nsdata->fpi >> NVME_NS_DATA_FPI_PERC_SHIFT) & - NVME_NS_DATA_FPI_PERC_MASK); + NVMEV(NVME_NS_DATA_FPI_PERC, nsdata->fpi)); } else printf("Not Supported\n"); - t = (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_READ_SHIFT) & - NVME_NS_DATA_DLFEAT_READ_MASK; + t = NVMEV(NVME_NS_DATA_DLFEAT_READ, nsdata->dlfeat); printf("Deallocate Logical Block: Read %s%s%s\n", (t == NVME_NS_DATA_DLFEAT_READ_NR) ? "Not Reported" : (t == NVME_NS_DATA_DLFEAT_READ_00) ? "00h" : (t == NVME_NS_DATA_DLFEAT_READ_FF) ? "FFh" : "Unknown", - (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_DWZ_SHIFT) & - NVME_NS_DATA_DLFEAT_DWZ_MASK ? ", Write Zero" : "", - (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_GCRC_SHIFT) & - NVME_NS_DATA_DLFEAT_GCRC_MASK ? ", Guard CRC" : ""); + NVMEV(NVME_NS_DATA_DLFEAT_DWZ, nsdata->dlfeat) != 0 ? + ", Write Zero" : "", + NVMEV(NVME_NS_DATA_DLFEAT_GCRC, nsdata->dlfeat) != 0 ? + ", Guard CRC" : ""); printf("Optimal I/O Boundary: %u blocks\n", nsdata->noiob); printf("NVM Capacity: %s bytes\n", uint128_to_str(to128(nsdata->nvmcap), cbuf, sizeof(cbuf))); - if ((nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_NPVALID_SHIFT) & - NVME_NS_DATA_NSFEAT_NPVALID_MASK) { + if (NVMEV(NVME_NS_DATA_NSFEAT_NPVALID, nsdata->nsfeat) != 0) { printf("Preferred Write Granularity: %u blocks\n", nsdata->npwg + 1); printf("Preferred Write Alignment: %u blocks\n", nsdata->npwa + 1); printf("Preferred Deallocate Granul: %u blocks\n", nsdata->npdg + 1); printf("Preferred Deallocate Align: %u blocks\n", nsdata->npda + 1); printf("Optimal Write Size: %u blocks\n", nsdata->nows + 1); } 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; + lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, lbaf); if (lbads == 0) continue; - 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; + ms = NVMEV(NVME_NS_DATA_LBAF_MS, lbaf); + rp = NVMEV(NVME_NS_DATA_LBAF_RP, lbaf); 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(int fd) { struct nvme_controller_data cdata; int hexlength; if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); close(fd); if (opt.hex) { if (opt.verbose) hexlength = sizeof(struct nvme_controller_data); else hexlength = offsetof(struct nvme_controller_data, reserved8); print_hex(&cdata, hexlength); exit(0); } nvme_print_controller(&cdata); exit(0); } static void identify_ns(int fd, uint32_t nsid) { struct nvme_namespace_data nsdata; int hexlength; if (read_namespace_data(fd, nsid, &nsdata)) errx(EX_IOERR, "Identify request failed"); close(fd); if (opt.hex) { if (opt.verbose) hexlength = sizeof(struct nvme_namespace_data); else hexlength = offsetof(struct nvme_namespace_data, reserved6); print_hex(&nsdata, hexlength); exit(0); } print_namespace(&nsdata); exit(0); } static void identify(const struct cmd *f, int argc, char *argv[]) { char *path; int fd; uint32_t nsid; if (arg_parse(argc, argv, f)) return; open_dev(opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { /* * We got namespace device, but we need to 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. */ close(fd); open_dev(path, &fd, 0, 1); } free(path); if (opt.nsid != NONE) nsid = opt.nsid; if (nsid == 0) identify_ctrlr(fd); else identify_ns(fd, nsid); } static const struct opts identify_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("hex", 'x', arg_none, opt, hex, "Print identiy information in hex"), OPT("verbose", 'v', arg_none, opt, verbose, "More verbosity: print entire identify table"), OPT("nsid", 'n', arg_uint32, opt, nsid, "Namespace ID to use if not in device name"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args identify_args[] = { { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd identify_cmd = { .name = "identify", .fn = identify, .descr = "Print summary of the IDENTIFY information", .ctx_size = sizeof(opt), .opts = identify_opts, .args = identify_args, }; CMD_COMMAND(identify_cmd); diff --git a/sbin/nvmecontrol/identify_ext.c b/sbin/nvmecontrol/identify_ext.c index 72b1ff5ccd7b..cc30ba0d6349 100644 --- a/sbin/nvmecontrol/identify_ext.c +++ b/sbin/nvmecontrol/identify_ext.c @@ -1,283 +1,252 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * Copyright (C) 2018-2019 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. * 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 #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #include "nvmecontrol_ext.h" void nvme_print_controller(struct nvme_controller_data *cdata) { uint8_t str[128]; char cbuf[UINT128_DIG + 1]; uint16_t oncs, oacs; uint8_t compare, write_unc, dsm, t; uint8_t security, fmt, fw, nsmgmt; uint8_t fw_slot1_ro, fw_num_slots; uint8_t ns_smart; uint8_t sqes_max, sqes_min; uint8_t cqes_max, cqes_min; uint8_t fwug; oncs = cdata->oncs; - compare = (oncs >> NVME_CTRLR_DATA_ONCS_COMPARE_SHIFT) & - NVME_CTRLR_DATA_ONCS_COMPARE_MASK; - write_unc = (oncs >> NVME_CTRLR_DATA_ONCS_WRITE_UNC_SHIFT) & - NVME_CTRLR_DATA_ONCS_WRITE_UNC_MASK; - dsm = (oncs >> NVME_CTRLR_DATA_ONCS_DSM_SHIFT) & - NVME_CTRLR_DATA_ONCS_DSM_MASK; + compare = NVMEV(NVME_CTRLR_DATA_ONCS_COMPARE, oncs); + write_unc = NVMEV(NVME_CTRLR_DATA_ONCS_WRITE_UNC, oncs); + dsm = NVMEV(NVME_CTRLR_DATA_ONCS_DSM, oncs); oacs = cdata->oacs; - security = (oacs >> NVME_CTRLR_DATA_OACS_SECURITY_SHIFT) & - NVME_CTRLR_DATA_OACS_SECURITY_MASK; - fmt = (oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) & - NVME_CTRLR_DATA_OACS_FORMAT_MASK; - fw = (oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & - NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; - nsmgmt = (oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK; + security = NVMEV(NVME_CTRLR_DATA_OACS_SECURITY, oacs); + fmt = NVMEV(NVME_CTRLR_DATA_OACS_FORMAT, oacs); + fw = NVMEV(NVME_CTRLR_DATA_OACS_FIRMWARE, oacs); + nsmgmt = NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, oacs); - fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & - NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; - fw_slot1_ro = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) & - NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK; + fw_num_slots = NVMEV(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, cdata->frmw); + fw_slot1_ro = NVMEV(NVME_CTRLR_DATA_FRMW_SLOT1_RO, cdata->frmw); fwug = cdata->fwug; - ns_smart = (cdata->lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & - NVME_CTRLR_DATA_LPA_NS_SMART_MASK; + ns_smart = NVMEV(NVME_CTRLR_DATA_LPA_NS_SMART, cdata->lpa); - sqes_min = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MIN_SHIFT) & - NVME_CTRLR_DATA_SQES_MIN_MASK; - sqes_max = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MAX_SHIFT) & - NVME_CTRLR_DATA_SQES_MAX_MASK; + sqes_min = NVMEV(NVME_CTRLR_DATA_SQES_MIN, cdata->sqes); + sqes_max = NVMEV(NVME_CTRLR_DATA_SQES_MAX, cdata->sqes); - cqes_min = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MIN_SHIFT) & - NVME_CTRLR_DATA_CQES_MIN_MASK; - cqes_max = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MAX_SHIFT) & - NVME_CTRLR_DATA_CQES_MAX_MASK; + cqes_min = NVMEV(NVME_CTRLR_DATA_CQES_MIN, cdata->cqes); + cqes_max = NVMEV(NVME_CTRLR_DATA_CQES_MAX, cdata->cqes); printf("Controller Capabilities/Features\n"); printf("================================\n"); printf("Vendor ID: %04x\n", cdata->vid); printf("Subsystem Vendor ID: %04x\n", cdata->ssvid); nvme_strvis(str, cdata->sn, sizeof(str), NVME_SERIAL_NUMBER_LENGTH); printf("Serial Number: %s\n", str); nvme_strvis(str, cdata->mn, sizeof(str), NVME_MODEL_NUMBER_LENGTH); printf("Model Number: %s\n", str); nvme_strvis(str, cdata->fr, sizeof(str), NVME_FIRMWARE_REVISION_LENGTH); printf("Firmware Version: %s\n", str); printf("Recommended Arb Burst: %d\n", cdata->rab); printf("IEEE OUI Identifier: %02x %02x %02x\n", cdata->ieee[2], cdata->ieee[1], cdata->ieee[0]); printf("Multi-Path I/O Capabilities: %s%s%s%s%s\n", (cdata->mic == 0) ? "Not Supported" : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_ANAR_SHIFT) & - NVME_CTRLR_DATA_MIC_SRIOVVF_MASK) ? "Asymmetric, " : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_SRIOVVF_SHIFT) & - NVME_CTRLR_DATA_MIC_SRIOVVF_MASK) ? "SR-IOV VF, " : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_MCTRLRS_SHIFT) & - NVME_CTRLR_DATA_MIC_MCTRLRS_MASK) ? "Multiple controllers, " : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_MPORTS_SHIFT) & - NVME_CTRLR_DATA_MIC_MPORTS_MASK) ? "Multiple ports" : ""); + NVMEV(NVME_CTRLR_DATA_MIC_ANAR, cdata->mic) != 0 ? + "Asymmetric, " : "", + NVMEV(NVME_CTRLR_DATA_MIC_SRIOVVF, cdata->mic) != 0 ? + "SR-IOV VF, " : "", + NVMEV(NVME_CTRLR_DATA_MIC_MCTRLRS, cdata->mic) != 0 ? + "Multiple controllers, " : "", + NVMEV(NVME_CTRLR_DATA_MIC_MPORTS, cdata->mic) != 0 ? + "Multiple ports" : ""); /* TODO: Use CAP.MPSMIN to determine true memory page size. */ printf("Max Data Transfer Size: "); if (cdata->mdts == 0) printf("Unlimited\n"); else printf("%ld bytes\n", PAGE_SIZE * (1L << cdata->mdts)); printf("Sanitize Crypto Erase: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_CES_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_CES, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize Block Erase: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_BES_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_BES, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize Overwrite: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_OWS_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_OWS, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize NDI: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_NDI_SHIFT) & - NVME_CTRLR_DATA_SANICAP_NDI_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_NDI, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize NODMMAS: "); - switch (((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_NODMMAS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_NODMMAS_MASK)) { + switch (NVMEV(NVME_CTRLR_DATA_SANICAP_NODMMAS, cdata->sanicap)) { case NVME_CTRLR_DATA_SANICAP_NODMMAS_UNDEF: printf("Undefined\n"); break; case NVME_CTRLR_DATA_SANICAP_NODMMAS_NO: printf("No\n"); break; case NVME_CTRLR_DATA_SANICAP_NODMMAS_YES: printf("Yes\n"); break; default: printf("Unknown\n"); break; } printf("Controller ID: 0x%04x\n", cdata->ctrlr_id); printf("Version: %d.%d.%d\n", (cdata->ver >> 16) & 0xffff, (cdata->ver >> 8) & 0xff, cdata->ver & 0xff); printf("\n"); printf("Admin Command Set Attributes\n"); printf("============================\n"); printf("Security Send/Receive: %s\n", security ? "Supported" : "Not Supported"); printf("Format NVM: %s\n", fmt ? "Supported" : "Not Supported"); printf("Firmware Activate/Download: %s\n", fw ? "Supported" : "Not Supported"); printf("Namespace Management: %s\n", nsmgmt ? "Supported" : "Not Supported"); printf("Device Self-test: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_SELFTEST_SHIFT) & - NVME_CTRLR_DATA_OACS_SELFTEST_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_SELFTEST, oacs) != 0 ? "" : "Not "); printf("Directives: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_DIRECTIVES_SHIFT) & - NVME_CTRLR_DATA_OACS_DIRECTIVES_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_DIRECTIVES, oacs) != 0 ? "" : "Not "); printf("NVMe-MI Send/Receive: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_NVMEMI_SHIFT) & - NVME_CTRLR_DATA_OACS_NVMEMI_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_NVMEMI, oacs) != 0 ? "" : "Not "); printf("Virtualization Management: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_VM_SHIFT) & - NVME_CTRLR_DATA_OACS_VM_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_VM, oacs) != 0 ? "" : "Not "); printf("Doorbell Buffer Config: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_DBBUFFER_SHIFT) & - NVME_CTRLR_DATA_OACS_DBBUFFER_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_DBBUFFER, oacs) != 0 ? "" : "Not "); printf("Get LBA Status: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_GETLBA_SHIFT) & - NVME_CTRLR_DATA_OACS_GETLBA_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_GETLBA, oacs) != 0 ? "" : "Not "); printf("Sanitize: "); if (cdata->sanicap != 0) { printf("%s%s%s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_CES_MASK) ? "crypto, " : "", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_BES_MASK) ? "block, " : "", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_OWS_MASK) ? "overwrite" : ""); + NVMEV(NVME_CTRLR_DATA_SANICAP_CES, cdata->sanicap) != 0 ? + "crypto, " : "", + NVMEV(NVME_CTRLR_DATA_SANICAP_BES, cdata->sanicap) != 0 ? + "block, " : "", + NVMEV(NVME_CTRLR_DATA_SANICAP_OWS, cdata->sanicap) != 0 ? + "overwrite" : ""); } else { printf("Not Supported\n"); } printf("Abort Command Limit: %d\n", cdata->acl+1); printf("Async Event Request Limit: %d\n", cdata->aerl+1); printf("Number of Firmware Slots: %d\n", fw_num_slots); printf("Firmware Slot 1 Read-Only: %s\n", fw_slot1_ro ? "Yes" : "No"); printf("Per-Namespace SMART Log: %s\n", ns_smart ? "Yes" : "No"); printf("Error Log Page Entries: %d\n", cdata->elpe+1); printf("Number of Power States: %d\n", cdata->npss+1); if (cdata->ver >= 0x010200) { printf("Total NVM Capacity: %s bytes\n", uint128_to_str(to128(cdata->untncap.tnvmcap), cbuf, sizeof(cbuf))); printf("Unallocated NVM Capacity: %s bytes\n", uint128_to_str(to128(cdata->untncap.unvmcap), cbuf, sizeof(cbuf))); } printf("Firmware Update Granularity: %02x ", fwug); if (fwug == 0) printf("(Not Reported)\n"); else if (fwug == 0xFF) printf("(No Granularity)\n"); else printf("(%d bytes)\n", ((uint32_t)fwug << 12)); printf("Host Buffer Preferred Size: %llu bytes\n", (long long unsigned)cdata->hmpre * 4096); printf("Host Buffer Minimum Size: %llu bytes\n", (long long unsigned)cdata->hmmin * 4096); printf("\n"); printf("NVM Command Set Attributes\n"); printf("==========================\n"); printf("Submission Queue Entry Size\n"); printf(" Max: %d\n", 1 << sqes_max); printf(" Min: %d\n", 1 << sqes_min); printf("Completion Queue Entry Size\n"); printf(" Max: %d\n", 1 << cqes_max); printf(" Min: %d\n", 1 << cqes_min); printf("Number of Namespaces: %d\n", cdata->nn); printf("Compare Command: %s\n", compare ? "Supported" : "Not Supported"); printf("Write Uncorrectable Command: %s\n", write_unc ? "Supported" : "Not Supported"); printf("Dataset Management Command: %s\n", dsm ? "Supported" : "Not Supported"); printf("Write Zeroes Command: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_WRZERO_SHIFT) & - NVME_CTRLR_DATA_ONCS_WRZERO_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_WRZERO, oncs) != 0 ? "" : "Not "); printf("Save Features: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_SAVEFEAT_SHIFT) & - NVME_CTRLR_DATA_ONCS_SAVEFEAT_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_SAVEFEAT, oncs) != 0 ? "" : "Not "); printf("Reservations: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_RESERV_SHIFT) & - NVME_CTRLR_DATA_ONCS_RESERV_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_RESERV, oncs) != 0 ? "" : "Not "); printf("Timestamp feature: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_TIMESTAMP_SHIFT) & - NVME_CTRLR_DATA_ONCS_TIMESTAMP_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_TIMESTAMP, oncs) != 0 ? "" : "Not "); printf("Verify feature: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_VERIFY_SHIFT) & - NVME_CTRLR_DATA_ONCS_VERIFY_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_VERIFY, oncs) != 0 ? "" : "Not "); printf("Fused Operation Support: %s%s\n", (cdata->fuses == 0) ? "Not Supported" : "", - ((cdata->fuses >> NVME_CTRLR_DATA_FUSES_CNW_SHIFT) & - NVME_CTRLR_DATA_FUSES_CNW_MASK) ? "Compare and Write" : ""); + NVMEV(NVME_CTRLR_DATA_FUSES_CNW, cdata->fuses) != 0 ? + "Compare and Write" : ""); printf("Format NVM Attributes: %s%s Erase, %s Format\n", - ((cdata->fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) & - NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) ? "Crypto Erase, " : "", - ((cdata->fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) & - NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) ? "All-NVM" : "Per-NS", - ((cdata->fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) & - NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) ? "All-NVM" : "Per-NS"); - t = (cdata->vwc >> NVME_CTRLR_DATA_VWC_ALL_SHIFT) & - NVME_CTRLR_DATA_VWC_ALL_MASK; + NVMEV(NVME_CTRLR_DATA_FNA_CRYPTO_ERASE, cdata->fna) != 0 ? + "Crypto Erase, " : "", + NVMEV(NVME_CTRLR_DATA_FNA_ERASE_ALL, cdata->fna) != 0 ? + "All-NVM" : "Per-NS", + NVMEV(NVME_CTRLR_DATA_FNA_FORMAT_ALL, cdata->fna) != 0 ? + "All-NVM" : "Per-NS"); + t = NVMEV(NVME_CTRLR_DATA_VWC_ALL, cdata->vwc); printf("Volatile Write Cache: %s%s\n", - ((cdata->vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) & - NVME_CTRLR_DATA_VWC_PRESENT_MASK) ? "Present" : "Not Present", + NVMEV(NVME_CTRLR_DATA_VWC_PRESENT, cdata->vwc) != 0 ? + "Present" : "Not Present", (t == NVME_CTRLR_DATA_VWC_ALL_NO) ? ", no flush all" : (t == NVME_CTRLR_DATA_VWC_ALL_YES) ? ", flush all" : ""); if (cdata->ver >= 0x010201) printf("\nNVM Subsystem Name: %.256s\n", cdata->subnqn); } diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index 31ec3a443716..8c03aa6fb7f4 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -1,854 +1,833 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * Copyright (C) 2018-2019 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. * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* Tables for command line parsing */ static cmd_fn_t logpage; #define NONE 0xffffffffu static struct options { bool binary; bool hex; uint32_t page; uint8_t lsp; uint16_t lsi; bool rae; const char *vendor; const char *dev; } opt = { .binary = false, .hex = false, .page = NONE, .lsp = 0, .lsi = 0, .rae = false, .vendor = NULL, .dev = NULL, }; static const struct opts logpage_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("binary", 'b', arg_none, opt, binary, "Dump the log page as binary"), OPT("hex", 'x', arg_none, opt, hex, "Dump the log page as hex"), OPT("page", 'p', arg_uint32, opt, page, "Page to dump"), OPT("lsp", 'f', arg_uint8, opt, lsp, "Log Specific Field"), OPT("lsi", 'i', arg_uint16, opt, lsi, "Log Specific Identifier"), OPT("rae", 'r', arg_none, opt, rae, "Retain Asynchronous Event"), OPT("vendor", 'v', arg_string, opt, vendor, "Vendor specific formatting"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args logpage_args[] = { { arg_string, &opt.dev, "" }, { arg_none, NULL, NULL }, }; static struct cmd logpage_cmd = { .name = "logpage", .fn = logpage, .descr = "Print logpages in human-readable form", .ctx_size = sizeof(opt), .opts = logpage_opts, .args = logpage_args, }; CMD_COMMAND(logpage_cmd); /* End of tables for command line parsing */ #define MAX_FW_SLOTS (7) static SLIST_HEAD(,logpage_function) logpages; static int logpage_compare(struct logpage_function *a, struct logpage_function *b) { int c; if ((a->vendor == NULL) != (b->vendor == NULL)) return (a->vendor == NULL ? -1 : 1); if (a->vendor != NULL) { c = strcmp(a->vendor, b->vendor); if (c != 0) return (c); } return ((int)a->log_page - (int)b->log_page); } void logpage_register(struct logpage_function *p) { struct logpage_function *l, *a; a = NULL; l = SLIST_FIRST(&logpages); while (l != NULL) { if (logpage_compare(l, p) > 0) break; a = l; l = SLIST_NEXT(l, link); } if (a == NULL) SLIST_INSERT_HEAD(&logpages, p, link); else SLIST_INSERT_AFTER(a, p, link); } const char * kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) { static char bad[32]; size_t i; for (i = 0; i < kv_count; i++, kv++) if (kv->key == key) return kv->name; snprintf(bad, sizeof(bad), "Attribute %#x", key); return bad; } static void print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { print_hex(data, length); } static void print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { write(STDOUT_FILENO, data, length); } static void * get_log_buffer(uint32_t size) { void *buf; if ((buf = malloc(size)) == NULL) errx(EX_OSERR, "unable to malloc %u bytes", size); memset(buf, 0, size); return (buf); } void read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp, uint16_t lsi, uint8_t rae, void *payload, uint32_t payload_size) { struct nvme_pt_command pt; struct nvme_error_information_entry *err_entry; u_int i, err_pages, numd; numd = payload_size / sizeof(uint32_t) - 1; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32( (numd << 16) | /* NUMDL */ (rae << 15) | /* RAE */ (lsp << 8) | /* LSP */ log_page); /* LID */ pt.cmd.cdw11 = htole32( ((uint32_t)lsi << 16) | /* LSI */ (numd >> 16)); /* NUMDU */ pt.cmd.cdw12 = 0; /* LPOL */ pt.cmd.cdw13 = 0; /* LPOU */ pt.cmd.cdw14 = 0; /* UUID Index */ pt.buf = payload; pt.len = payload_size; pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "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_CHANGED_NAMESPACE: nvme_ns_list_swapbytes((struct nvme_ns_list *)payload); break; case NVME_LOG_DEVICE_SELF_TEST: nvme_device_self_test_swapbytes( (struct nvme_device_self_test_page *)payload); break; case NVME_LOG_COMMAND_EFFECT: nvme_command_effects_page_swapbytes( (struct nvme_command_effects_page *)payload); break; case NVME_LOG_RES_NOTIFICATION: nvme_res_notification_page_swapbytes( (struct nvme_res_notification_page *)payload); break; case NVME_LOG_SANITIZE_STATUS: nvme_sanitize_status_page_swapbytes( (struct nvme_sanitize_status_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(EX_IOERR, "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); printf(" Transport type: %u\n", entry->trtype); printf(" Command specific info:%ju\n", entry->csi); printf(" Transport specific: %u\n", entry->ttsi); } } void print_temp_K(uint16_t t) { printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); } void print_temp_C(uint16_t t) { printf("%2.2f K, %u C, %3.2f F\n", (float)t + 273.15, t, (float)t * 9 / 5 + 32); } 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_K(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_K(health->temp_sensor[i]); } printf("Temperature 1 Transition Count: %d\n", health->tmt1tc); printf("Temperature 2 Transition Count: %d\n", health->tmt2tc); printf("Total Time For Temperature 1: %d\n", health->ttftmt1); printf("Total Time For Temperature 2: %d\n", health->ttftmt2); } 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; + afi_slot = NVMEV(NVME_FIRMWARE_PAGE_AFI_SLOT, fw->afi); - 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; + oacs_fw = NVMEV(NVME_CTRLR_DATA_OACS_FIRMWARE, cdata->oacs); + fw_num_slots = NVMEV(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, cdata->frmw); 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][0] == '\0') printf("Empty\n"); else printf("[%s] %.8s\n", status, fw->revision[i]); } } static void print_log_ns(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_ns_list *nsl; u_int i; nsl = (struct nvme_ns_list *)buf; printf("Changed Namespace List\n"); printf("======================\n"); for (i = 0; i < nitems(nsl->ns) && nsl->ns[i] != 0; i++) { printf("%08x\n", nsl->ns[i]); } } static void print_log_command_effects(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_command_effects_page *ce; u_int i; uint32_t s; ce = (struct nvme_command_effects_page *)buf; printf("Commands Supported and Effects\n"); printf("==============================\n"); printf(" Command\tLBCC\tNCC\tNIC\tCCC\tCSE\tUUID\n"); for (i = 0; i < 255; i++) { s = ce->acs[i]; - if (((s >> NVME_CE_PAGE_CSUP_SHIFT) & - NVME_CE_PAGE_CSUP_MASK) == 0) + if (NVMEV(NVME_CE_PAGE_CSUP, s) == 0) continue; printf("Admin\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i, - ((s >> NVME_CE_PAGE_LBCC_SHIFT) & - NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NCC_SHIFT) & - NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NIC_SHIFT) & - NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CCC_SHIFT) & - NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CSE_SHIFT) & - NVME_CE_PAGE_CSE_MASK), - ((s >> NVME_CE_PAGE_UUID_SHIFT) & - NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No"); + NVMEV(NVME_CE_PAGE_LBCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NIC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CSE, s), + NVMEV(NVME_CE_PAGE_UUID, s) != 0 ? "Yes" : "No"); } for (i = 0; i < 255; i++) { s = ce->iocs[i]; - if (((s >> NVME_CE_PAGE_CSUP_SHIFT) & - NVME_CE_PAGE_CSUP_MASK) == 0) + if (NVMEV(NVME_CE_PAGE_CSUP, s) == 0) continue; printf("I/O\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i, - ((s >> NVME_CE_PAGE_LBCC_SHIFT) & - NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NCC_SHIFT) & - NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NIC_SHIFT) & - NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CCC_SHIFT) & - NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CSE_SHIFT) & - NVME_CE_PAGE_CSE_MASK), - ((s >> NVME_CE_PAGE_UUID_SHIFT) & - NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No"); + NVMEV(NVME_CE_PAGE_LBCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NIC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CSE, s), + NVMEV(NVME_CE_PAGE_UUID, s) != 0 ? "Yes" : "No"); } } static void print_log_res_notification(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_res_notification_page *rn; rn = (struct nvme_res_notification_page *)buf; printf("Reservation Notification\n"); printf("========================\n"); printf("Log Page Count: %ju\n", rn->log_page_count); printf("Log Page Type: "); switch (rn->log_page_type) { case 0: printf("Empty Log Page\n"); break; case 1: printf("Registration Preempted\n"); break; case 2: printf("Reservation Released\n"); break; case 3: printf("Reservation Preempted\n"); break; default: printf("Unknown %x\n", rn->log_page_type); break; }; printf("Number of Available Log Pages: %d\n", rn->available_log_pages); printf("Namespace ID: 0x%x\n", rn->nsid); } static void print_log_sanitize_status(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_sanitize_status_page *ss; u_int p; ss = (struct nvme_sanitize_status_page *)buf; printf("Sanitize Status\n"); printf("===============\n"); printf("Sanitize Progress: %u%% (%u/65535)\n", (ss->sprog * 100 + 32768) / 65536, ss->sprog); printf("Sanitize Status: "); - switch ((ss->sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) & - NVME_SS_PAGE_SSTAT_STATUS_MASK) { + switch (NVMEV(NVME_SS_PAGE_SSTAT_STATUS, ss->sstat)) { case NVME_SS_PAGE_SSTAT_STATUS_NEVER: printf("Never sanitized"); break; case NVME_SS_PAGE_SSTAT_STATUS_COMPLETED: printf("Completed"); break; case NVME_SS_PAGE_SSTAT_STATUS_INPROG: printf("In Progress"); break; case NVME_SS_PAGE_SSTAT_STATUS_FAILED: printf("Failed"); break; case NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD: printf("Completed with deallocation"); break; default: printf("Unknown"); break; } - p = (ss->sstat >> NVME_SS_PAGE_SSTAT_PASSES_SHIFT) & - NVME_SS_PAGE_SSTAT_PASSES_MASK; + p = NVMEV(NVME_SS_PAGE_SSTAT_PASSES, ss->sstat); if (p > 0) printf(", %d passes", p); - if ((ss->sstat >> NVME_SS_PAGE_SSTAT_GDE_SHIFT) & - NVME_SS_PAGE_SSTAT_GDE_MASK) + if (NVMEV(NVME_SS_PAGE_SSTAT_GDE, ss->sstat) != 0) printf(", Global Data Erased"); printf("\n"); printf("Sanitize Command Dword 10: 0x%x\n", ss->scdw10); printf("Time For Overwrite: %u sec\n", ss->etfo); printf("Time For Block Erase: %u sec\n", ss->etfbe); printf("Time For Crypto Erase: %u sec\n", ss->etfce); printf("Time For Overwrite No-Deallocate: %u sec\n", ss->etfownd); printf("Time For Block Erase No-Deallocate: %u sec\n", ss->etfbewnd); printf("Time For Crypto Erase No-Deallocate: %u sec\n", ss->etfcewnd); } static const char * self_test_res[] = { [0] = "completed without error", [1] = "aborted by a Device Self-test command", [2] = "aborted by a Controller Level Reset", [3] = "aborted due to namespace removal", [4] = "aborted due to Format NVM command", [5] = "failed due to fatal or unknown test error", [6] = "completed with an unknown segment that failed", [7] = "completed with one or more failed segments", [8] = "aborted for unknown reason", [9] = "aborted due to a sanitize operation", }; static uint32_t self_test_res_max = nitems(self_test_res); static void print_log_self_test_status(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_device_self_test_page *dst; uint32_t r; dst = buf; printf("Device Self-test Status\n"); printf("=======================\n"); printf("Current Operation: "); switch (dst->curr_operation) { case 0x0: printf("No device self-test operation in progress\n"); break; case 0x1: printf("Short device self-test operation in progress\n"); break; case 0x2: printf("Extended device self-test operation in progress\n"); break; case 0xe: printf("Vendor specific\n"); break; default: printf("Reserved (0x%x)\n", dst->curr_operation); } if (dst->curr_operation != 0) printf("Current Completion: %u%%\n", dst->curr_compl & 0x7f); printf("Results\n"); for (r = 0; r < 20; r++) { uint64_t failing_lba; uint8_t code, res; code = (dst->result[r].status >> 4) & 0xf; res = dst->result[r].status & 0xf; if (res == 0xf) continue; printf("[%2u] ", r); switch (code) { case 0x1: printf("Short device self-test"); break; case 0x2: printf("Extended device self-test"); break; case 0xe: printf("Vendor specific"); break; default: printf("Reserved (0x%x)", code); } if (res < self_test_res_max) printf(" %s", self_test_res[res]); else printf(" Reserved status 0x%x", res); if (res == 7) printf(" starting in segment %u", dst->result[r].segment_num); #define BIT(b) (1 << (b)) if (dst->result[r].valid_diag_info & BIT(0)) printf(" NSID=0x%x", dst->result[r].nsid); if (dst->result[r].valid_diag_info & BIT(1)) { memcpy(&failing_lba, dst->result[r].failing_lba, sizeof(failing_lba)); printf(" FLBA=0x%jx", failing_lba); } if (dst->result[r].valid_diag_info & BIT(2)) printf(" SCT=0x%x", dst->result[r].status_code_type); if (dst->result[r].valid_diag_info & BIT(3)) printf(" SC=0x%x", dst->result[r].status_code); #undef BIT printf("\n"); } } /* * 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)); NVME_LOGPAGE(ns, NVME_LOG_CHANGED_NAMESPACE, NULL, "Changed Namespace List", print_log_ns, sizeof(struct nvme_ns_list)); NVME_LOGPAGE(ce, NVME_LOG_COMMAND_EFFECT, NULL, "Commands Supported and Effects", print_log_command_effects, sizeof(struct nvme_command_effects_page)); NVME_LOGPAGE(dst, NVME_LOG_DEVICE_SELF_TEST, NULL, "Device Self-test", print_log_self_test_status, sizeof(struct nvme_device_self_test_page)); NVME_LOGPAGE(thi, NVME_LOG_TELEMETRY_HOST_INITIATED, NULL, "Telemetry Host-Initiated", NULL, DEFAULT_SIZE); NVME_LOGPAGE(tci, NVME_LOG_TELEMETRY_CONTROLLER_INITIATED, NULL, "Telemetry Controller-Initiated", NULL, DEFAULT_SIZE); NVME_LOGPAGE(egi, NVME_LOG_ENDURANCE_GROUP_INFORMATION, NULL, "Endurance Group Information", NULL, DEFAULT_SIZE); NVME_LOGPAGE(plpns, NVME_LOG_PREDICTABLE_LATENCY_PER_NVM_SET, NULL, "Predictable Latency Per NVM Set", NULL, DEFAULT_SIZE); NVME_LOGPAGE(ple, NVME_LOG_PREDICTABLE_LATENCY_EVENT_AGGREGATE, NULL, "Predictable Latency Event Aggregate", NULL, DEFAULT_SIZE); NVME_LOGPAGE(ana, NVME_LOG_ASYMMETRIC_NAMESPACE_ACCESS, NULL, "Asymmetric Namespace Access", NULL, DEFAULT_SIZE); NVME_LOGPAGE(pel, NVME_LOG_PERSISTENT_EVENT_LOG, NULL, "Persistent Event Log", NULL, DEFAULT_SIZE); NVME_LOGPAGE(lbasi, NVME_LOG_LBA_STATUS_INFORMATION, NULL, "LBA Status Information", NULL, DEFAULT_SIZE); NVME_LOGPAGE(egea, NVME_LOG_ENDURANCE_GROUP_EVENT_AGGREGATE, NULL, "Endurance Group Event Aggregate", NULL, DEFAULT_SIZE); NVME_LOGPAGE(res_notification, NVME_LOG_RES_NOTIFICATION, NULL, "Reservation Notification", print_log_res_notification, sizeof(struct nvme_res_notification_page)); NVME_LOGPAGE(sanitize_status, NVME_LOG_SANITIZE_STATUS, NULL, "Sanitize Status", print_log_sanitize_status, sizeof(struct nvme_sanitize_status_page)); static void logpage_help(void) { const struct logpage_function *f; const char *v; fprintf(stderr, "\n"); fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); fprintf(stderr, "-------- ---------- ----------\n"); SLIST_FOREACH(f, &logpages, link) { v = f->vendor == NULL ? "-" : f->vendor; fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name); } exit(EX_USAGE); } static void logpage(const struct cmd *f, int argc, char *argv[]) { int fd; char *path; uint32_t nsid, size; void *buf; const struct logpage_function *lpf; struct nvme_controller_data cdata; print_fn_t print_fn; uint8_t ns_smart; if (arg_parse(argc, argv, f)) return; if (opt.hex && opt.binary) { fprintf(stderr, "Can't specify both binary and hex\n"); arg_help(argc, argv, f); } if (opt.vendor != NULL && strcmp(opt.vendor, "help") == 0) logpage_help(); if (opt.page == NONE) { fprintf(stderr, "Missing page_id (-p).\n"); arg_help(argc, argv, f); } open_dev(opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); if (nsid == 0) { nsid = NVME_GLOBAL_NAMESPACE_TAG; } else { close(fd); open_dev(path, &fd, 0, 1); } free(path); if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); - ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & - NVME_CTRLR_DATA_LPA_NS_SMART_MASK; + ns_smart = NVMEV(NVME_CTRLR_DATA_LPA_NS_SMART, cdata.lpa); /* * The log page attributes indicate whether or not the controller * supports the SMART/Health information log page on a per * namespace basis. */ if (nsid != NVME_GLOBAL_NAMESPACE_TAG) { if (opt.page != NVME_LOG_HEALTH_INFORMATION) errx(EX_USAGE, "log page %d valid only at controller level", opt.page); if (ns_smart == 0) errx(EX_UNAVAILABLE, "controller does not support per namespace " "smart/health information"); } print_fn = print_log_hex; size = DEFAULT_SIZE; if (opt.binary) print_fn = print_bin; if (!opt.binary && !opt.hex) { /* * See if there is a pretty print function for the specified log * page. If one isn't found, we just revert to the default * (print_hex). If there was a vendor specified by the user, and * the page is vendor specific, don't match the print function * unless the vendors match. */ SLIST_FOREACH(lpf, &logpages, link) { if (lpf->vendor != NULL && opt.vendor != NULL && strcmp(lpf->vendor, opt.vendor) != 0) continue; if (opt.page != lpf->log_page) continue; if (lpf->print_fn != NULL) print_fn = lpf->print_fn; size = lpf->size; break; } } if (opt.page == NVME_LOG_ERROR) { size = sizeof(struct nvme_error_information_entry); size *= (cdata.elpe + 1); } /* Read the log page */ buf = get_log_buffer(size); read_logpage(fd, opt.page, nsid, opt.lsp, opt.lsi, opt.rae, buf, size); print_fn(&cdata, buf, size); close(fd); exit(0); } diff --git a/sbin/nvmecontrol/ns.c b/sbin/nvmecontrol/ns.c index dd678568d5ca..a77f37e0f468 100644 --- a/sbin/nvmecontrol/ns.c +++ b/sbin/nvmecontrol/ns.c @@ -1,994 +1,981 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2017 Netflix, Inc. * Copyright (C) 2018-2019 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 #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* Tables for command line parsing */ static cmd_fn_t ns; static cmd_fn_t nsactive; static cmd_fn_t nsallocated; static cmd_fn_t nscontrollers; static cmd_fn_t nscreate; static cmd_fn_t nsdelete; static cmd_fn_t nsattach; static cmd_fn_t nsdetach; static cmd_fn_t nsattached; static cmd_fn_t nsidentify; #define NONE 0xffffffffu #define NONE64 0xffffffffffffffffull #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } #define OPT_END { NULL, 0, arg_none, NULL, NULL } static struct cmd ns_cmd = { .name = "ns", .fn = ns, .descr = "Namespace management commands", .ctx_size = 0, .opts = NULL, .args = NULL, }; CMD_COMMAND(ns_cmd); static struct active_options { const char *dev; } active_opt = { .dev = NULL, }; static const struct args active_args[] = { { arg_string, &active_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd active_cmd = { .name = "active", .fn = nsactive, .descr = "List active (attached) namespaces", .ctx_size = sizeof(active_opt), .opts = NULL, .args = active_args, }; CMD_SUBCOMMAND(ns_cmd, active_cmd); static struct cmd allocated_cmd = { .name = "allocated", .fn = nsallocated, .descr = "List allocated (created) namespaces", .ctx_size = sizeof(active_opt), .opts = NULL, .args = active_args, }; CMD_SUBCOMMAND(ns_cmd, allocated_cmd); static struct controllers_options { const char *dev; } controllers_opt = { .dev = NULL, }; static const struct args controllers_args[] = { { arg_string, &controllers_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd controllers_cmd = { .name = "controllers", .fn = nscontrollers, .descr = "List all controllers in NVM subsystem", .ctx_size = sizeof(controllers_opt), .opts = NULL, .args = controllers_args, }; CMD_SUBCOMMAND(ns_cmd, controllers_cmd); static struct create_options { uint64_t nsze; uint64_t cap; uint32_t lbaf; uint32_t mset; uint32_t nmic; uint32_t pi; uint32_t pil; uint32_t flbas; uint32_t dps; // uint32_t block_size; const char *dev; } create_opt = { .nsze = NONE64, .cap = NONE64, .lbaf = NONE, .mset = NONE, .nmic = NONE, .pi = NONE, .pil = NONE, .flbas = NONE, .dps = NONE, .dev = NULL, // .block_size = NONE, }; static const struct opts create_opts[] = { OPT("nsze", 's', arg_uint64, create_opt, nsze, "The namespace size"), OPT("ncap", 'c', arg_uint64, create_opt, cap, "The capacity of the namespace (<= ns size)"), OPT("lbaf", 'f', arg_uint32, create_opt, lbaf, "The FMT field of the FLBAS"), OPT("mset", 'm', arg_uint32, create_opt, mset, "The MSET field of the FLBAS"), OPT("nmic", 'n', arg_uint32, create_opt, nmic, "Namespace multipath and sharing capabilities"), OPT("pi", 'p', arg_uint32, create_opt, pi, "PI field of FLBAS"), OPT("pil", 'l', arg_uint32, create_opt, pil, "PIL field of FLBAS"), OPT("flbas", 'L', arg_uint32, create_opt, flbas, "Namespace formatted logical block size setting"), OPT("dps", 'd', arg_uint32, create_opt, dps, "Data protection settings"), // OPT("block-size", 'b', arg_uint32, create_opt, block_size, // "Blocksize of the namespace"), OPT_END }; static const struct args create_args[] = { { arg_string, &create_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd create_cmd = { .name = "create", .fn = nscreate, .descr = "Create a namespace", .ctx_size = sizeof(create_opt), .opts = create_opts, .args = create_args, }; CMD_SUBCOMMAND(ns_cmd, create_cmd); static struct delete_options { uint32_t nsid; const char *dev; } delete_opt = { .nsid = NONE - 1, .dev = NULL, }; static const struct opts delete_opts[] = { OPT("namespace-id", 'n', arg_uint32, delete_opt, nsid, "The namespace ID to delete"), OPT_END }; static const struct args delete_args[] = { { arg_string, &delete_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd delete_cmd = { .name = "delete", .fn = nsdelete, .descr = "Delete a namespace", .ctx_size = sizeof(delete_opt), .opts = delete_opts, .args = delete_args, }; CMD_SUBCOMMAND(ns_cmd, delete_cmd); static struct attach_options { uint32_t nsid; uint32_t ctrlrid; const char *dev; } attach_opt = { .nsid = NONE, .ctrlrid = NONE - 1, .dev = NULL, }; static const struct opts attach_opts[] = { OPT("namespace-id", 'n', arg_uint32, attach_opt, nsid, "The namespace ID to attach"), OPT("controller", 'c', arg_uint32, attach_opt, ctrlrid, "The controller ID to attach"), OPT_END }; static const struct args attach_args[] = { { arg_string, &attach_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd attach_cmd = { .name = "attach", .fn = nsattach, .descr = "Attach a controller to a namespace", .ctx_size = sizeof(attach_opt), .opts = attach_opts, .args = attach_args, }; CMD_SUBCOMMAND(ns_cmd, attach_cmd); static struct attached_options { uint32_t nsid; const char *dev; } attached_opt = { .nsid = NONE, .dev = NULL, }; static const struct opts attached_opts[] = { OPT("namespace-id", 'n', arg_uint32, attached_opt, nsid, "The namespace ID to request attached controllers"), OPT_END }; static const struct args attached_args[] = { { arg_string, &attached_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd attached_cmd = { .name = "attached", .fn = nsattached, .descr = "List controllers attached to a namespace", .ctx_size = sizeof(attached_opt), .opts = attached_opts, .args = attached_args, }; CMD_SUBCOMMAND(ns_cmd, attached_cmd); static struct detach_options { uint32_t nsid; uint32_t ctrlrid; const char *dev; } detach_opt = { .nsid = NONE, .ctrlrid = NONE - 1, .dev = NULL, }; static const struct opts detach_opts[] = { OPT("namespace-id", 'n', arg_uint32, detach_opt, nsid, "The namespace ID to detach"), OPT("controller", 'c', arg_uint32, detach_opt, ctrlrid, "The controller ID to detach"), OPT_END }; static const struct args detach_args[] = { { arg_string, &detach_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd detach_cmd = { .name = "detach", .fn = nsdetach, .descr = "Detach a controller from a namespace", .ctx_size = sizeof(detach_opt), .opts = detach_opts, .args = detach_args, }; CMD_SUBCOMMAND(ns_cmd, detach_cmd); static struct identify_options { bool hex; bool verbose; const char *dev; uint32_t nsid; } identify_opt = { .hex = false, .verbose = false, .dev = NULL, .nsid = NONE, }; static const struct opts identify_opts[] = { OPT("hex", 'x', arg_none, identify_opt, hex, "Print identiy information in hex"), OPT("verbose", 'v', arg_none, identify_opt, verbose, "More verbosity: print entire identify table"), OPT("nsid", 'n', arg_uint32, identify_opt, nsid, "The namespace ID to print IDENTIFY for"), { NULL, 0, arg_none, NULL, NULL } }; static const struct args identify_args[] = { { arg_string, &identify_opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd identify_cmd = { .name = "identify", .fn = nsidentify, .descr = "Print IDENTIFY for allocated namespace", .ctx_size = sizeof(identify_opt), .opts = identify_opts, .args = identify_args, }; CMD_SUBCOMMAND(ns_cmd, identify_cmd); /* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */ struct ns_result_str { uint16_t res; const char * str; }; static struct ns_result_str ns_result[] = { { 0x2, "Invalid Field"}, { 0xa, "Invalid Format"}, { 0xb, "Invalid Namespace or format"}, { 0x15, "Namespace insufficient capacity"}, { 0x16, "Namespace ID unavailable"}, { 0x18, "Namespace already attached"}, { 0x19, "Namespace is private"}, { 0x1a, "Namespace is not attached"}, { 0x1b, "Thin provisioning not supported"}, { 0x1c, "Controller list invalid"}, { 0x24, "ANA Group Identifier Invalid"}, { 0x25, "ANA Attach Failed"}, { 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; } static void nsactive(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, i; char *path; uint32_t nsid; uint32_t list[1024]; if (arg_parse(argc, argv, f)) return; open_dev(active_opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 0, 1); } free(path); if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(0); pt.cmd.cdw10 = htole32(0x02); pt.buf = list; pt.len = sizeof(list); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "identify request returned error"); printf("Active namespaces:\n"); for (i = 0; list[i] != 0; i++) printf("%10d\n", le32toh(list[i])); exit(0); } static void nsallocated(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, i; char *path; uint32_t nsid; uint32_t list[1024]; if (arg_parse(argc, argv, f)) return; open_dev(active_opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 0, 1); } free(path); if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(0); pt.cmd.cdw10 = htole32(0x10); pt.buf = list; pt.len = sizeof(list); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "identify request returned error"); printf("Allocated namespaces:\n"); for (i = 0; list[i] != 0; i++) printf("%10d\n", le32toh(list[i])); exit(0); } static void nscontrollers(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, i, n; char *path; uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; open_dev(controllers_opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 0, 1); } free(path); if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); 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(EX_IOERR, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "identify request returned error"); n = le16toh(clist[0]); printf("NVM subsystem includes %d controller(s):\n", n); for (i = 0; i < n; i++) printf(" 0x%04x\n", le16toh(clist[i + 1])); exit(0); } /* * NS MGMT Command specific status values: * 0xa = Invalid Format * 0x15 = Namespace Insuffience capacity * 0x16 = Namespace ID unavailable (number namespaces exceeded) * 0xb = Thin Provisioning Not supported */ static void nscreate(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; struct nvme_namespace_data nsdata; int fd, result; char *path; uint32_t nsid; if (arg_parse(argc, argv, f)) return; if (create_opt.cap == NONE64) create_opt.cap = create_opt.nsze; if (create_opt.nsze == NONE64) { fprintf(stderr, "Size not specified\n"); arg_help(argc, argv, f); } open_dev(create_opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } free(path); if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&nsdata, 0, sizeof(nsdata)); nsdata.nsze = create_opt.nsze; nsdata.ncap = create_opt.cap; if (create_opt.flbas != NONE) { nsdata.flbas = create_opt.flbas; } else { /* Default to the first format, whatever it is. */ nsdata.flbas = 0; if (create_opt.lbaf != NONE) { nsdata.flbas |= (create_opt.lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK) << NVME_NS_DATA_FLBAS_FORMAT_SHIFT; } if (create_opt.mset != NONE) { nsdata.flbas |= (create_opt.mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK) << NVME_NS_DATA_FLBAS_EXTENDED_SHIFT; } } if (create_opt.dps != NONE) { nsdata.dps = create_opt.dps; } else { /* Default to protection disabled. */ nsdata.dps = 0; if (create_opt.pi != NONE) { nsdata.dps |= (create_opt.pi & NVME_NS_DATA_DPS_MD_START_MASK) << NVME_NS_DATA_DPS_MD_START_SHIFT; } if (create_opt.pil != NONE) { nsdata.dps |= (create_opt.pil & NVME_NS_DATA_DPS_PIT_MASK) << NVME_NS_DATA_DPS_PIT_SHIFT; } } if (create_opt.nmic != NONE) { nsdata.nmic = create_opt.nmic; } else { /* Allow namespaces sharing if Multi-Path I/O is supported. */ nsdata.nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK << NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0; } nvme_namespace_data_swapbytes(&nsdata); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; pt.cmd.cdw10 = htole32(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(EX_IOERR, "ioctl request to %s failed: %d", create_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace creation failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } printf("namespace %d created\n", pt.cpl.cdw0); exit(0); } static void nsdelete(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, result; char *path; uint32_t nsid; char buf[2]; if (arg_parse(argc, argv, f)) return; open_dev(delete_opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } else if (delete_opt.nsid == NONE - 1) { close(fd); fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } if (delete_opt.nsid != NONE - 1) nsid = delete_opt.nsid; free(path); if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; pt.cmd.cdw10 = htole32(1); /* delete */ pt.buf = buf; pt.len = sizeof(buf); pt.is_read = 1; pt.cmd.nsid = nsid; if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(EX_IOERR, "ioctl request to %s failed: %d", delete_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace deletion failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } 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. */ static void nsattach(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, result; char *path; uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; open_dev(attach_opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } else if (attach_opt.nsid == NONE) { close(fd); fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } if (attach_opt.nsid != NONE) nsid = attach_opt.nsid; if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); if (attach_opt.ctrlrid == NONE) { /* Get full list of controllers to attach to. */ memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(0x13); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "identify request returned error"); } else { /* By default attach to this controller. */ if (attach_opt.ctrlrid == NONE - 1) attach_opt.ctrlrid = cd.ctrlr_id; memset(&clist, 0, sizeof(clist)); clist[0] = htole16(1); clist[1] = htole16(attach_opt.ctrlrid); } memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; pt.cmd.cdw10 = htole32(0); /* attach */ pt.cmd.nsid = nsid; pt.buf = &clist; pt.len = sizeof(clist); if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(EX_IOERR, "ioctl request to %s failed: %d", attach_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace attach failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } printf("namespace %d attached\n", nsid); exit(0); } static void nsdetach(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, result; char *path; uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; open_dev(detach_opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } else if (detach_opt.nsid == NONE) { close(fd); fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } if (detach_opt.nsid != NONE) nsid = detach_opt.nsid; if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); if (detach_opt.ctrlrid == NONE) { /* Get list of controllers this namespace attached to. */ memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(0x12); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "identify request returned error"); if (clist[0] == 0) { detach_opt.ctrlrid = cd.ctrlr_id; memset(&clist, 0, sizeof(clist)); clist[0] = htole16(1); clist[1] = htole16(detach_opt.ctrlrid); } } else { /* By default detach from this controller. */ if (detach_opt.ctrlrid == NONE - 1) detach_opt.ctrlrid = cd.ctrlr_id; memset(&clist, 0, sizeof(clist)); clist[0] = htole16(1); clist[1] = htole16(detach_opt.ctrlrid); } memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; pt.cmd.cdw10 = htole32(1); /* detach */ pt.cmd.nsid = nsid; pt.buf = &clist; pt.len = sizeof(clist); if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(EX_IOERR, "ioctl request to %s failed: %d", detach_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace detach failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } printf("namespace %d detached\n", nsid); exit(0); } static void nsattached(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; int fd, i, n; char *path; uint32_t nsid; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; open_dev(attached_opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } else if (attached_opt.nsid == NONE) { close(fd); fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } if (attached_opt.nsid != NONE) nsid = attached_opt.nsid; if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); 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(EX_IOERR, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "identify request returned error"); n = le16toh(clist[0]); printf("Attached %d controller(s):\n", n); for (i = 0; i < n; i++) printf(" 0x%04x\n", le16toh(clist[i + 1])); exit(0); } static void nsidentify(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_controller_data cd; struct nvme_namespace_data nsdata; uint8_t *data; int fd; char *path; uint32_t nsid; u_int i; if (arg_parse(argc, argv, f)) return; open_dev(identify_opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } else if (identify_opt.nsid == NONE) { close(fd); fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } if (identify_opt.nsid != NONE) nsid = identify_opt.nsid; if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(0x11); pt.buf = &nsdata; pt.len = sizeof(nsdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "identify request returned error"); close(fd); data = (uint8_t *)&nsdata; for (i = 0; i < sizeof(nsdata); i++) { if (data[i] != 0) break; } if (i == sizeof(nsdata)) errx(EX_UNAVAILABLE, "namespace %d is not allocated", nsid); /* Convert data to host endian */ nvme_namespace_data_swapbytes(&nsdata); if (identify_opt.hex) { i = sizeof(struct nvme_namespace_data); if (!identify_opt.verbose) { for (; i > 384; i--) { if (data[i - 1] != 0) break; } } print_hex(&nsdata, i); exit(0); } print_namespace(&nsdata); exit(0); } static void ns(const struct cmd *nf __unused, int argc, char *argv[]) { cmd_dispatch(argc, argv, &ns_cmd); } diff --git a/sbin/nvmecontrol/power.c b/sbin/nvmecontrol/power.c index 78de46dbc277..887010feb782 100644 --- a/sbin/nvmecontrol/power.c +++ b/sbin/nvmecontrol/power.c @@ -1,210 +1,206 @@ /*- * Copyright (c) 2016 Netflix, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" _Static_assert(sizeof(struct nvme_power_state) == 256 / NBBY, "nvme_power_state size wrong"); #define POWER_NONE 0xffffffffu static struct options { bool list; uint32_t power; uint32_t workload; const char *dev; } opt = { .list = false, .power = POWER_NONE, .workload = 0, .dev = NULL, }; static void power_list_one(int i, struct nvme_power_state *nps) { int mpower, apower, ipower; uint8_t mps, nops, aps, apw; - mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) & - NVME_PWR_ST_MPS_MASK; - nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) & - NVME_PWR_ST_NOPS_MASK; - apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) & - NVME_PWR_ST_APW_MASK; - aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) & - NVME_PWR_ST_APS_MASK; + mps = NVMEV(NVME_PWR_ST_MPS, nps->mps_nops); + nops = NVMEV(NVME_PWR_ST_NOPS, nps->mps_nops); + apw = NVMEV(NVME_PWR_ST_APW, nps->apw_aps); + aps = NVMEV(NVME_PWR_ST_APS, nps->apw_aps); 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 Workload\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(EX_IOERR, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "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(EX_IOERR, "set feature power mgmt request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "set feature power mgmt request returned error"); printf("Current Power State is %d\n", pt.cpl.cdw0 & 0x1F); printf("Current Workload Hint is %d\n", pt.cpl.cdw0 >> 5); } static void power(const struct cmd *f, int argc, char *argv[]) { struct nvme_controller_data cdata; int fd; char *path; uint32_t nsid; if (arg_parse(argc, argv, f)) return; if (opt.list && opt.power != POWER_NONE) { fprintf(stderr, "Can't set power and list power states\n"); arg_help(argc, argv, f); } open_dev(opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } free(path); if (opt.list) { if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); power_list(&cdata); goto out; } if (opt.power != POWER_NONE) { power_set(fd, opt.power, opt.workload, 0); goto out; } power_show(fd); out: close(fd); exit(0); } static const struct opts power_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("list", 'l', arg_none, opt, list, "List the valid power states"), OPT("power", 'p', arg_uint32, opt, power, "Set the power state"), OPT("workload", 'w', arg_uint32, opt, workload, "Set the workload hint"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args power_args[] = { { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd power_cmd = { .name = "power", .fn = power, .descr = "Manage power states for the drive", .ctx_size = sizeof(opt), .opts = power_opts, .args = power_args, }; CMD_COMMAND(power_cmd); diff --git a/sbin/nvmecontrol/sanitize.c b/sbin/nvmecontrol/sanitize.c index d56a74685651..4611a6964a4d 100644 --- a/sbin/nvmecontrol/sanitize.c +++ b/sbin/nvmecontrol/sanitize.c @@ -1,222 +1,218 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2019 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. * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* Tables for command line parsing */ static cmd_fn_t sanitize; static struct options { bool ause; bool ndas; bool oipbp; bool reportonly; uint8_t owpass; uint32_t ovrpat; const char *sanact; const char *dev; } opt = { .ause = false, .ndas = false, .oipbp = false, .reportonly = false, .owpass = 1, .ovrpat = 0, .sanact = NULL, .dev = NULL, }; static const struct opts sanitize_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("ause", 'U', arg_none, opt, ause, "Allow Unrestricted Sanitize Exit"), OPT("ndas", 'd', arg_none, opt, ndas, "No Deallocate After Sanitize"), OPT("oipbp", 'I', arg_none, opt, oipbp, "Overwrite Invert Pattern Between Passes"), OPT("reportonly", 'r', arg_none, opt, reportonly, "Report previous sanitize status"), OPT("owpass", 'c', arg_uint8, opt, owpass, "Overwrite Pass Count"), OPT("ovrpat", 'p', arg_uint32, opt, ovrpat, "Overwrite Pattern"), OPT("sanact", 'a', arg_string, opt, sanact, "Sanitize Action (block, overwrite, crypto)"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static const struct args sanitize_args[] = { { arg_string, &opt.dev, "controller-id" }, { arg_none, NULL, NULL }, }; static struct cmd sanitize_cmd = { .name = "sanitize", .fn = sanitize, .descr = "Sanitize NVM subsystem", .ctx_size = sizeof(opt), .opts = sanitize_opts, .args = sanitize_args, }; CMD_COMMAND(sanitize_cmd); /* End of tables for command line parsing */ static void sanitize(const struct cmd *f, int argc, char *argv[]) { struct nvme_controller_data cd; struct nvme_pt_command pt; struct nvme_sanitize_status_page ss; char *path; uint32_t nsid; int sanact = 0, fd, delay = 1; if (arg_parse(argc, argv, f)) return; if (opt.sanact == NULL) { if (!opt.reportonly) { fprintf(stderr, "Sanitize Action is not specified\n"); arg_help(argc, argv, f); } } else { if (strcmp(opt.sanact, "exitfailure") == 0) sanact = 1; else if (strcmp(opt.sanact, "block") == 0) sanact = 2; else if (strcmp(opt.sanact, "overwrite") == 0) sanact = 3; else if (strcmp(opt.sanact, "crypto") == 0) sanact = 4; else { fprintf(stderr, "Incorrect Sanitize Action value\n"); arg_help(argc, argv, f); } } if (opt.owpass == 0 || opt.owpass > 16) { fprintf(stderr, "Incorrect Overwrite Pass Count value\n"); arg_help(argc, argv, f); } open_dev(opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } free(path); if (opt.reportonly) goto wait; /* Check that controller can execute this command. */ if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); - if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_BES_MASK) == 0 && sanact == 2) + if (NVMEV(NVME_CTRLR_DATA_SANICAP_BES, cd.sanicap) == 0 && sanact == 2) errx(EX_UNAVAILABLE, "controller does not support Block Erase"); - if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_OWS_MASK) == 0 && sanact == 3) + if (NVMEV(NVME_CTRLR_DATA_SANICAP_OWS, cd.sanicap) == 0 && sanact == 3) errx(EX_UNAVAILABLE, "controller does not support Overwrite"); - if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_CES_MASK) == 0 && sanact == 4) + if (NVMEV(NVME_CTRLR_DATA_SANICAP_CES, cd.sanicap) == 0 && sanact == 4) errx(EX_UNAVAILABLE, "controller does not support Crypto Erase"); /* * If controller supports only one namespace, we may sanitize it. * If there can be more, make user explicit in his commands. */ if (nsid != 0 && cd.nn > 1) errx(EX_UNAVAILABLE, "can't sanitize one of namespaces, specify controller"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_SANITIZE; pt.cmd.cdw10 = htole32((opt.ndas << 9) | (opt.oipbp << 8) | ((opt.owpass & 0xf) << 4) | (opt.ause << 3) | sanact); pt.cmd.cdw11 = htole32(opt.ovrpat); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "sanitize request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "sanitize request returned error"); wait: read_logpage(fd, NVME_LOG_SANITIZE_STATUS, NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &ss, sizeof(ss)); - switch ((ss.sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) & - NVME_SS_PAGE_SSTAT_STATUS_MASK) { + switch (NVMEV(NVME_SS_PAGE_SSTAT_STATUS, ss.sstat)) { case NVME_SS_PAGE_SSTAT_STATUS_NEVER: printf("Never sanitized"); break; case NVME_SS_PAGE_SSTAT_STATUS_COMPLETED: printf("Sanitize completed"); break; case NVME_SS_PAGE_SSTAT_STATUS_INPROG: printf("Sanitize in progress: %u%% (%u/65535)\r", (ss.sprog * 100 + 32768) / 65536, ss.sprog); fflush(stdout); if (delay < 16) delay++; sleep(delay); goto wait; case NVME_SS_PAGE_SSTAT_STATUS_FAILED: printf("Sanitize failed"); break; case NVME_SS_PAGE_SSTAT_STATUS_COMPLETEDWD: printf("Sanitize completed with deallocation"); break; default: printf("Sanitize status unknown"); break; } if (delay > 1) printf(" "); printf("\n"); close(fd); exit(0); } diff --git a/sbin/nvmecontrol/selftest.c b/sbin/nvmecontrol/selftest.c index 6139a7902234..0fae3a3062ae 100644 --- a/sbin/nvmecontrol/selftest.c +++ b/sbin/nvmecontrol/selftest.c @@ -1,135 +1,134 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Chuck Tuffli * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define SELFTEST_CODE_NONE 0xffu #define SELFTEST_CODE_MAX 0xfu static struct options { const char *dev; uint8_t stc; /* Self-test Code */ } opt = { .dev = NULL, .stc = SELFTEST_CODE_NONE, }; static void selftest_op(int fd, uint32_t nsid, uint8_t stc) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_DEVICE_SELF_TEST; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(stc); if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "self-test request failed"); if (NVME_STATUS_GET_SCT(pt.cpl.status) == NVME_SCT_COMMAND_SPECIFIC && NVME_STATUS_GET_SC(pt.cpl.status) == NVME_SC_SELF_TEST_IN_PROGRESS) errx(EX_UNAVAILABLE, "device self-test in progress"); else if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "self-test request returned error"); } static void selftest(const struct cmd *f, int argc, char *argv[]) { struct nvme_controller_data cdata; int fd; char *path; uint32_t nsid; if (arg_parse(argc, argv, f)) return; open_dev(opt.dev, &fd, 1, 1); get_nsid(fd, &path, &nsid); if (nsid != 0) { close(fd); open_dev(path, &fd, 1, 1); } free(path); if (opt.stc == SELFTEST_CODE_NONE) errx(EX_USAGE, "must specify a Self-test Code"); else if (opt.stc > SELFTEST_CODE_MAX) errx(EX_DATAERR, "illegal Self-test Code 0x%x", opt.stc); if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); - if (((cdata.oacs >> NVME_CTRLR_DATA_OACS_SELFTEST_SHIFT) & - NVME_CTRLR_DATA_OACS_SELFTEST_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_SELFTEST, cdata.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support self-test"); selftest_op(fd, nsid, opt.stc); close(fd); exit(0); } static const struct opts selftest_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("test-code", 'c', arg_uint8, opt, stc, "Self-test Code to execute"), { NULL, 0, arg_none, NULL, NULL } }; #undef OPT static struct args selftest_args[] = { { arg_string, &opt.dev, "controller-id|namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd selftest_cmd = { .name = "selftest", .fn = selftest, .descr = "Start device self-test", .ctx_size = sizeof(opt), .opts = selftest_opts, .args = selftest_args, }; CMD_COMMAND(selftest_cmd);