Index: stable/12/sbin/nvmecontrol/identify.c =================================================================== --- stable/12/sbin/nvmecontrol/identify.c (revision 360374) +++ stable/12/sbin/nvmecontrol/identify.c (revision 360375) @@ -1,292 +1,292 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #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; flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & NVME_NS_DATA_FLBAS_FORMAT_MASK; 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\n", flbas_fmt); printf("Data Protection Caps: %s%s%s%s%s%s\n", (nsdata->dpc == 0) ? "Not Supported" : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_END_SHIFT) & NVME_NS_DATA_DPC_MD_END_MASK) ? "Last Bytes, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_START_SHIFT) & NVME_NS_DATA_DPC_MD_START_MASK) ? "First Bytes, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT3_SHIFT) & NVME_NS_DATA_DPC_PIT3_MASK) ? "Type 3, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_SHIFT) & NVME_NS_DATA_DPC_PIT2_MASK) ? "Type 2, " : "", ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT1_SHIFT) & NVME_NS_DATA_DPC_PIT1_MASK) ? "Type 1" : ""); printf("Data Protection Settings: "); ptype = (nsdata->dps >> NVME_NS_DATA_DPS_PIT_SHIFT) & NVME_NS_DATA_DPS_PIT_MASK; if (ptype) { printf("Type %d, %s Bytes\n", ptype, ((nsdata->dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) & NVME_NS_DATA_DPS_MD_START_MASK) ? "First" : "Last"); } else { printf("Not Enabled\n"); } printf("Multi-Path I/O Capabilities: %s%s\n", (nsdata->nmic == 0) ? "Not Supported" : "", ((nsdata->nmic >> NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) & NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK) ? "May be shared" : ""); printf("Reservation Capabilities: %s%s%s%s%s%s%s%s%s\n", (nsdata->rescap == 0) ? "Not Supported" : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_IEKEY13_SHIFT) & NVME_NS_DATA_RESCAP_IEKEY13_MASK) ? "IEKEY13, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_AR_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_AR_MASK) ? "EX_AC_AR, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_AR_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_AR_MASK) ? "WR_EX_AR, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_RO_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_RO_MASK) ? "EX_AC_RO, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_RO_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_RO_MASK) ? "WR_EX_RO, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_SHIFT) & NVME_NS_DATA_RESCAP_EX_AC_MASK) ? "EX_AC, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_SHIFT) & NVME_NS_DATA_RESCAP_WR_EX_MASK) ? "WR_EX, " : "", ((nsdata->rescap >> NVME_NS_DATA_RESCAP_PTPL_SHIFT) & NVME_NS_DATA_RESCAP_PTPL_MASK) ? "PTPL" : ""); printf("Format Progress Indicator: "); if ((nsdata->fpi >> NVME_NS_DATA_FPI_SUPP_SHIFT) & NVME_NS_DATA_FPI_SUPP_MASK) { printf("%u%% remains\n", (nsdata->fpi >> NVME_NS_DATA_FPI_PERC_SHIFT) & NVME_NS_DATA_FPI_PERC_MASK); } else printf("Not Supported\n"); t = (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_READ_SHIFT) & NVME_NS_DATA_DLFEAT_READ_MASK; 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" : ""); 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) { printf("Preferred Write Granularity: %u blocks", nsdata->npwg + 1); printf("Preferred Write Alignment: %u blocks", nsdata->npwa + 1); printf("Preferred Deallocate Granul: %u blocks", nsdata->npdg + 1); printf("Preferred Deallocate Align: %u blocks", nsdata->npda + 1); printf("Optimal Write Size: %u blocks", 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; ms = (lbaf >> NVME_NS_DATA_LBAF_MS_SHIFT) & NVME_NS_DATA_LBAF_MS_MASK; rp = (lbaf >> NVME_NS_DATA_LBAF_RP_SHIFT) & NVME_NS_DATA_LBAF_RP_MASK; printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d" " Performance: %s\n", i, 1 << lbads, ms, (rp == 0) ? "Best" : (rp == 1) ? "Better" : (rp == 2) ? "Good" : "Degraded"); } } static void identify_ctrlr(int fd) { struct nvme_controller_data cdata; int hexlength; read_controller_data(fd, &cdata); 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; read_namespace_data(fd, nsid, &nsdata); 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, 1, 1); + 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, 1, 1); + 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); Index: stable/12/sbin/nvmecontrol/logpage.c =================================================================== --- stable/12/sbin/nvmecontrol/logpage.c (revision 360374) +++ stable/12/sbin/nvmecontrol/logpage.c (revision 360375) @@ -1,757 +1,757 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #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, lsp, "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(1, "unable to malloc %u bytes", size); memset(buf, 0, size); return (buf); } void read_logpage(int fd, uint8_t log_page, uint32_t nsid, 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(1, "get log page request failed"); /* Convert data to host endian */ switch (log_page) { case NVME_LOG_ERROR: err_entry = (struct nvme_error_information_entry *)payload; err_pages = payload_size / sizeof(struct nvme_error_information_entry); for (i = 0; i < err_pages; i++) nvme_error_information_entry_swapbytes(err_entry++); break; case NVME_LOG_HEALTH_INFORMATION: nvme_health_information_page_swapbytes( (struct nvme_health_information_page *)payload); break; case NVME_LOG_FIRMWARE_SLOT: nvme_firmware_page_swapbytes( (struct nvme_firmware_page *)payload); break; case NVME_LOG_CHANGED_NAMESPACE: nvme_ns_list_swapbytes((struct nvme_ns_list *)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(1, "get log page request returned error"); } static void print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) { int i, nentries; uint16_t status; uint8_t p, sc, sct, m, dnr; struct nvme_error_information_entry *entry = buf; printf("Error Information Log\n"); printf("=====================\n"); if (entry->error_count == 0) { printf("No error entries found\n"); return; } nentries = size/sizeof(struct nvme_error_information_entry); for (i = 0; i < nentries; i++, entry++) { if (entry->error_count == 0) break; status = entry->status; p = NVME_STATUS_GET_P(status); sc = NVME_STATUS_GET_SC(status); sct = NVME_STATUS_GET_SCT(status); m = NVME_STATUS_GET_M(status); dnr = NVME_STATUS_GET_DNR(status); printf("Entry %02d\n", i + 1); printf("=========\n"); printf(" Error count: %ju\n", entry->error_count); printf(" Submission queue ID: %u\n", entry->sqid); printf(" Command ID: %u\n", entry->cid); /* TODO: Export nvme_status_string structures from kernel? */ printf(" Status:\n"); printf(" Phase tag: %d\n", p); printf(" Status code: %d\n", sc); printf(" Status code type: %d\n", sct); printf(" More: %d\n", m); printf(" DNR: %d\n", dnr); printf(" Error location: %u\n", entry->error_location); printf(" LBA: %ju\n", entry->lba); printf(" Namespace ID: %u\n", entry->nsid); printf(" Vendor specific info: %u\n", entry->vendor_specific); 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(uint16_t t) { printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); } static void print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_health_information_page *health = buf; char cbuf[UINT128_DIG + 1]; uint8_t warning; int i; warning = health->critical_warning; printf("SMART/Health Information Log\n"); printf("============================\n"); printf("Critical Warning State: 0x%02x\n", warning); printf(" Available spare: %d\n", !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE)); printf(" Temperature: %d\n", !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE)); printf(" Device reliability: %d\n", !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY)); printf(" Read only: %d\n", !!(warning & NVME_CRIT_WARN_ST_READ_ONLY)); printf(" Volatile memory backup: %d\n", !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP)); printf("Temperature: "); print_temp(health->temperature); printf("Available spare: %u\n", health->available_spare); printf("Available spare threshold: %u\n", health->available_spare_threshold); printf("Percentage used: %u\n", health->percentage_used); printf("Data units (512,000 byte) read: %s\n", uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); printf("Data units written: %s\n", uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); printf("Host read commands: %s\n", uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); printf("Host write commands: %s\n", uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); printf("Controller busy time (minutes): %s\n", uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); printf("Power cycles: %s\n", uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); printf("Power on hours: %s\n", uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); printf("Unsafe shutdowns: %s\n", uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); printf("Media errors: %s\n", uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); printf("No. error info log entries: %s\n", uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); printf("Error Temp Composite Time: %d\n", health->error_temp_time); for (i = 0; i < 8; i++) { if (health->temp_sensor[i] == 0) continue; printf("Temperature Sensor %d: ", i + 1); print_temp(health->temp_sensor[i]); } 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; oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; printf("Firmware Slot Log\n"); printf("=================\n"); if (oacs_fw == 0) slots = 1; else slots = MIN(fw_num_slots, MAX_FW_SLOTS); for (i = 0; i < slots; i++) { printf("Slot %d: ", i + 1); if (afi_slot == i + 1) status = " Active"; else status = "Inactive"; if (fw->revision[i] == 0LLU) printf("Empty\n"); else if (isprint(*(char *)&fw->revision[i])) printf("[%s] %.8s\n", status, (char *)&fw->revision[i]); else printf("[%s] %016jx\n", status, fw->revision[i]); } } 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) 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"); } for (i = 0; i < 255; i++) { s = ce->iocs[i]; if (((s >> NVME_CE_PAGE_CSUP_SHIFT) & NVME_CE_PAGE_CSUP_MASK) == 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"); } } 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) { 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; if (p > 0) printf(", %d passes", p); if ((ss->sstat >> NVME_SS_PAGE_SSTAT_GDE_SHIFT) & NVME_SS_PAGE_SSTAT_GDE_MASK) 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); } /* * 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", NULL, 564); 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_NAMESPAVE_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(1); } 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, 1, 1); + 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, 1, 1); + open_dev(path, &fd, 0, 1); } free(path); read_controller_data(fd, &cdata); ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & NVME_CTRLR_DATA_LPA_NS_SMART_MASK; /* * The log page attribtues indicate whether or not the controller * supports the SMART/Health information log page on a per * namespace basis. */ if (nsid != NVME_GLOBAL_NAMESPACE_TAG) { if (opt.page != NVME_LOG_HEALTH_INFORMATION) errx(1, "log page %d valid only at controller level", opt.page); if (ns_smart == 0) errx(1, "controller does not support per namespace " "smart/health information"); } print_fn = print_log_hex; size = DEFAULT_SIZE; if (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); } Index: stable/12/sbin/nvmecontrol/ns.c =================================================================== --- stable/12/sbin/nvmecontrol/ns.c (revision 360374) +++ stable/12/sbin/nvmecontrol/ns.c (revision 360375) @@ -1,884 +1,884 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #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" }, { 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" }, { 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" }, { 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, .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" }, { 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" }, { 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" }, { 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" }, { 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" }, { 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 insufficent capacity"}, { 0x16, "Namespace ID unavaliable"}, { 0x18, "Namespace already attached"}, { 0x19, "Namespace is private"}, { 0x1a, "Namespace is not attached"}, { 0x1b, "Thin provisioning not supported"}, { 0x1c, "Controller list invalid"}, { 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; int fd, i; uint32_t list[1024]; if (arg_parse(argc, argv, f)) return; - open_dev(active_opt.dev, &fd, 1, 1); + open_dev(active_opt.dev, &fd, 0, 1); 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(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "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; uint32_t list[1024]; if (arg_parse(argc, argv, f)) return; - open_dev(active_opt.dev, &fd, 1, 1); + open_dev(active_opt.dev, &fd, 0, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_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(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "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; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; - open_dev(controllers_opt.dev, &fd, 1, 1); + open_dev(controllers_opt.dev, &fd, 0, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(0x13); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); 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; 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); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); /* Allow namespaces sharing if Multi-Path I/O is supported. */ if (create_opt.nmic == NONE) { create_opt.nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK << NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0; } memset(&nsdata, 0, sizeof(nsdata)); nsdata.nsze = create_opt.nsze; nsdata.ncap = create_opt.cap; if (create_opt.flbas == NONE) nsdata.flbas = ((create_opt.lbaf & NVME_NS_DATA_FLBAS_FORMAT_MASK) << NVME_NS_DATA_FLBAS_FORMAT_SHIFT) | ((create_opt.mset & NVME_NS_DATA_FLBAS_EXTENDED_MASK) << NVME_NS_DATA_FLBAS_EXTENDED_SHIFT); else nsdata.flbas = create_opt.flbas; if (create_opt.dps == NONE) nsdata.dps = ((create_opt.pi & NVME_NS_DATA_DPS_MD_START_MASK) << NVME_NS_DATA_DPS_MD_START_SHIFT) | ((create_opt.pil & NVME_NS_DATA_DPS_PIT_MASK) << NVME_NS_DATA_DPS_PIT_SHIFT); else nsdata.dps = create_opt.dps; nsdata.nmic = create_opt.nmic; 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(1, "ioctl request to %s failed: %d", argv[optind], result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace creation failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } printf("namespace %d created\n", pt.cpl.cdw0); exit(0); } 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 buf[2]; if (arg_parse(argc, argv, f)) return; if (delete_opt.nsid == NONE) { fprintf(stderr, "No NSID specified"); arg_help(argc, argv, f); } open_dev(delete_opt.dev, &fd, 1, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; pt.cmd.cdw10 = htole32(1); /* delete */ pt.buf = buf; pt.len = sizeof(buf); pt.is_read = 1; pt.cmd.nsid = delete_opt.nsid; if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", delete_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace deletion failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } printf("namespace %d deleted\n", delete_opt.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; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; if (attach_opt.nsid == NONE) { fprintf(stderr, "No valid NSID specified\n"); arg_help(argc, argv, f); } open_dev(attach_opt.dev, &fd, 1, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); if (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(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } else { /* By default attach to this controller. */ if (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 = attach_opt.nsid; pt.buf = &clist; pt.len = sizeof(clist); if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", attach_opt.dev, result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace attach failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } printf("namespace %d attached\n", attach_opt.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; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; if (detach_opt.nsid == NONE) { fprintf(stderr, "No valid NSID specified\n"); arg_help(argc, argv, f); } open_dev(detach_opt.dev, &fd, 1, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); if (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(detach_opt.nsid); pt.cmd.cdw10 = htole32(0x12); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); if (clist[0] == 0) { 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 = detach_opt.nsid; pt.buf = &clist; pt.len = sizeof(clist); if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) errx(1, "ioctl request to %s failed: %d", argv[optind], result); if (nvme_completion_is_error(&pt.cpl)) { errx(1, "namespace detach failed: %s", get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & NVME_STATUS_SC_MASK)); } printf("namespace %d detached\n", detach_opt.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; uint16_t clist[2048]; if (arg_parse(argc, argv, f)) return; if (attached_opt.nsid == NONE) { fprintf(stderr, "No valid NSID specified\n"); arg_help(argc, argv, f); } - open_dev(attached_opt.dev, &fd, 1, 1); + open_dev(attached_opt.dev, &fd, 0, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(attached_opt.nsid); pt.cmd.cdw10 = htole32(0x12); pt.buf = clist; pt.len = sizeof(clist); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); 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; u_int i; if (arg_parse(argc, argv, f)) return; if (identify_opt.nsid == NONE) { fprintf(stderr, "No valid NSID specified\n"); arg_help(argc, argv, f); } - open_dev(identify_opt.dev, &fd, 1, 1); + open_dev(identify_opt.dev, &fd, 0, 1); read_controller_data(fd, &cd); /* Check that controller can execute this command. */ if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) errx(1, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(identify_opt.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(1, "identify request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "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(1, "namespace %d is not allocated", identify_opt.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); } Index: stable/12/sbin/nvmecontrol/nsid.c =================================================================== --- stable/12/sbin/nvmecontrol/nsid.c (revision 360374) +++ stable/12/sbin/nvmecontrol/nsid.c (revision 360375) @@ -1,81 +1,81 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include "nvmecontrol.h" #include "comnd.h" /* Tables for command line parsing */ static cmd_fn_t gnsid; static struct nsid_options { const char *dev; } nsid_opt = { .dev = NULL, }; static const struct args nsid_args[] = { { arg_string, &nsid_opt.dev, "namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd nsid_cmd = { .name = "nsid", .fn = gnsid, .descr = "Get controller and NSID for namespace", .ctx_size = sizeof(nsid_opt), .opts = NULL, .args = nsid_args, }; CMD_COMMAND(nsid_cmd); static void gnsid(const struct cmd *f, int argc, char *argv[]) { char *path; int fd; uint32_t nsid; if (arg_parse(argc, argv, f)) return; - open_dev(nsid_opt.dev, &fd, 1, 1); + open_dev(nsid_opt.dev, &fd, 0, 1); get_nsid(fd, &path, &nsid); close(fd); printf("%s\t%u\n", path, nsid); free(path); } Index: stable/12/sbin/nvmecontrol/nvmecontrol.c =================================================================== --- stable/12/sbin/nvmecontrol/nvmecontrol.c (revision 360374) +++ stable/12/sbin/nvmecontrol/nvmecontrol.c (revision 360375) @@ -1,188 +1,187 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" static void print_bytes(void *data, uint32_t length) { uint32_t i, j; uint8_t *p, *end; end = (uint8_t *)data + length; for (i = 0; i < length; i++) { p = (uint8_t *)data + (i*16); printf("%03x: ", i*16); for (j = 0; j < 16 && p < end; j++) printf("%02x ", *p++); if (p >= end) break; printf("\n"); } printf("\n"); } static void print_dwords(void *data, uint32_t length) { uint32_t *p; uint32_t i, j; p = (uint32_t *)data; length /= sizeof(uint32_t); for (i = 0; i < length; i+=8) { printf("%03x: ", i*4); for (j = 0; j < 8; j++) printf("%08x ", p[i+j]); printf("\n"); } printf("\n"); } void print_hex(void *data, uint32_t length) { if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0) print_dwords(data, length); else print_bytes(data, length); } void read_controller_data(int fd, struct nvme_controller_data *cdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(1); pt.buf = cdata; pt.len = sizeof(*cdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_controller_data_swapbytes(cdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = htole32(0); pt.buf = nsdata; pt.len = sizeof(*nsdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_namespace_data_swapbytes(nsdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } int -open_dev(const char *str, int *fd, int show_error, int exit_on_error) +open_dev(const char *str, int *fd, int write, int exit_on_error) { char full_path[64]; snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str); - *fd = open(full_path, O_RDWR); + *fd = open(full_path, write ? O_RDWR : O_RDONLY); if (*fd < 0) { - if (show_error) - warn("could not open %s", full_path); - if (exit_on_error) - exit(1); - else + if (exit_on_error) { + err(1, "could not open %s%s", full_path, + write ? " for write" : ""); + } else return (errno); } return (0); } void get_nsid(int fd, char **ctrlr_str, uint32_t *nsid) { struct nvme_get_nsid gnsid; if (ioctl(fd, NVME_GET_NSID, &gnsid) < 0) err(1, "NVME_GET_NSID ioctl failed"); if (ctrlr_str != NULL) *ctrlr_str = strndup(gnsid.cdev, sizeof(gnsid.cdev)); if (nsid != NULL) *nsid = gnsid.nsid; } int main(int argc, char *argv[]) { cmd_init(); cmd_load_dir("/lib/nvmecontrol", NULL, NULL); cmd_load_dir("/usr/local/lib/nvmecontrol", NULL, NULL); cmd_dispatch(argc, argv, NULL); return (0); } Index: stable/12/sbin/nvmecontrol/nvmecontrol.h =================================================================== --- stable/12/sbin/nvmecontrol/nvmecontrol.h (revision 360374) +++ stable/12/sbin/nvmecontrol/nvmecontrol.h (revision 360375) @@ -1,104 +1,104 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __NVMECONTROL_H__ #define __NVMECONTROL_H__ #include #include "comnd.h" typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size); struct logpage_function { SLIST_ENTRY(logpage_function) link; uint8_t log_page; const char *vendor; const char *name; print_fn_t print_fn; size_t size; }; #define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \ static struct logpage_function unique ## _lpf = { \ .log_page = lp, \ .vendor = vend, \ .name = nam, \ .print_fn = fn, \ .size = sz, \ } ; \ static void logpage_reg_##unique(void) __attribute__((constructor)); \ static void logpage_reg_##unique(void) { logpage_register(&unique##_lpf); } #define DEFAULT_SIZE (4096) struct kv_name { uint32_t key; const char *name; }; const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key); void logpage_register(struct logpage_function *p); #define NVME_CTRLR_PREFIX "nvme" #define NVME_NS_PREFIX "ns" -int open_dev(const char *str, int *fd, int show_error, int exit_on_error); +int open_dev(const char *str, int *fd, int write, int exit_on_error); void get_nsid(int fd, char **ctrlr_str, uint32_t *nsid); void read_controller_data(int fd, struct nvme_controller_data *cdata); void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata); void print_hex(void *data, uint32_t length); void print_namespace(struct nvme_namespace_data *nsdata); 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); void print_temp(uint16_t t); void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused); /* Utility Routines */ /* * 128-bit integer augments to standard values. On i386 this * doesn't exist, so we use 64-bit values. So, on 32-bit i386, * you'll get truncated values until someone implement 128bit * ints in sofware. */ #define UINT128_DIG 39 #ifdef __i386__ typedef uint64_t uint128_t; #else typedef __uint128_t uint128_t; #endif static __inline uint128_t to128(void *p) { return *(uint128_t *)p; } uint64_t le48dec(const void *pp); char * uint128_to_str(uint128_t u, char *buf, size_t buflen); #endif Index: stable/12/sbin/nvmecontrol/resv.c =================================================================== --- stable/12/sbin/nvmecontrol/resv.c (revision 360374) +++ stable/12/sbin/nvmecontrol/resv.c (revision 360375) @@ -1,442 +1,442 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* Tables for command line parsing */ static cmd_fn_t resv; static cmd_fn_t resvacquire; static cmd_fn_t resvregister; static cmd_fn_t resvrelease; static cmd_fn_t resvreport; #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 resv_cmd = { .name = "resv", .fn = resv, .descr = "Reservation commands", .ctx_size = 0, .opts = NULL, .args = NULL, }; CMD_COMMAND(resv_cmd); static struct acquire_options { uint64_t crkey; uint64_t prkey; uint8_t rtype; uint8_t racqa; const char *dev; } acquire_opt = { .crkey = 0, .prkey = 0, .rtype = 0, .racqa = 0, .dev = NULL, }; static const struct opts acquire_opts[] = { OPT("crkey", 'c', arg_uint64, acquire_opt, crkey, "Current Reservation Key"), OPT("prkey", 'p', arg_uint64, acquire_opt, prkey, "Preempt Reservation Key"), OPT("rtype", 't', arg_uint8, acquire_opt, rtype, "Reservation Type"), OPT("racqa", 'a', arg_uint8, acquire_opt, racqa, "Acquire Action (0=acq, 1=pre, 2=pre+ab)"), { NULL, 0, arg_none, NULL, NULL } }; static const struct args acquire_args[] = { { arg_string, &acquire_opt.dev, "namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd acquire_cmd = { .name = "acquire", .fn = resvacquire, .descr = "Acquire/preempt reservation", .ctx_size = sizeof(acquire_opt), .opts = acquire_opts, .args = acquire_args, }; CMD_SUBCOMMAND(resv_cmd, acquire_cmd); static struct register_options { uint64_t crkey; uint64_t nrkey; uint8_t rrega; bool iekey; uint8_t cptpl; const char *dev; } register_opt = { .crkey = 0, .nrkey = 0, .rrega = 0, .iekey = false, .cptpl = 0, .dev = NULL, }; static const struct opts register_opts[] = { OPT("crkey", 'c', arg_uint64, register_opt, crkey, "Current Reservation Key"), OPT("nrkey", 'k', arg_uint64, register_opt, nrkey, "New Reservation Key"), OPT("rrega", 'r', arg_uint8, register_opt, rrega, "Register Action (0=reg, 1=unreg, 2=replace)"), OPT("iekey", 'i', arg_none, register_opt, iekey, "Ignore Existing Key"), OPT("cptpl", 'p', arg_uint8, register_opt, cptpl, "Change Persist Through Power Loss State"), { NULL, 0, arg_none, NULL, NULL } }; static const struct args register_args[] = { { arg_string, ®ister_opt.dev, "namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd register_cmd = { .name = "register", .fn = resvregister, .descr = "Register/unregister reservation", .ctx_size = sizeof(register_opt), .opts = register_opts, .args = register_args, }; CMD_SUBCOMMAND(resv_cmd, register_cmd); static struct release_options { uint64_t crkey; uint8_t rtype; uint8_t rrela; const char *dev; } release_opt = { .crkey = 0, .rtype = 0, .rrela = 0, .dev = NULL, }; static const struct opts release_opts[] = { OPT("crkey", 'c', arg_uint64, release_opt, crkey, "Current Reservation Key"), OPT("rtype", 't', arg_uint8, release_opt, rtype, "Reservation Type"), OPT("rrela", 'a', arg_uint8, release_opt, rrela, "Release Action (0=release, 1=clear)"), { NULL, 0, arg_none, NULL, NULL } }; static const struct args release_args[] = { { arg_string, &release_opt.dev, "namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd release_cmd = { .name = "release", .fn = resvrelease, .descr = "Release/clear reservation", .ctx_size = sizeof(release_opt), .opts = release_opts, .args = release_args, }; CMD_SUBCOMMAND(resv_cmd, release_cmd); static struct report_options { bool hex; bool verbose; bool eds; const char *dev; } report_opt = { .hex = false, .verbose = false, .eds = false, .dev = NULL, }; static const struct opts report_opts[] = { OPT("hex", 'x', arg_none, report_opt, hex, "Print reservation status in hex"), OPT("verbose", 'v', arg_none, report_opt, verbose, "More verbosity"), OPT("eds", 'e', arg_none, report_opt, eds, "Extended Data Structure"), { NULL, 0, arg_none, NULL, NULL } }; static const struct args report_args[] = { { arg_string, &report_opt.dev, "namespace-id" }, { arg_none, NULL, NULL }, }; static struct cmd report_cmd = { .name = "report", .fn = resvreport, .descr = "Print reservation status", .ctx_size = sizeof(report_opt), .opts = report_opts, .args = report_args, }; CMD_SUBCOMMAND(resv_cmd, report_cmd); /* handles NVME_OPC_RESERVATION_* NVM commands */ static void resvacquire(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; uint64_t data[2]; int fd; uint32_t nsid; if (arg_parse(argc, argv, f)) return; - open_dev(acquire_opt.dev, &fd, 1, 1); + open_dev(acquire_opt.dev, &fd, 0, 1); get_nsid(fd, NULL, &nsid); if (nsid == 0) { fprintf(stderr, "This command require namespace-id\n"); arg_help(argc, argv, f); } data[0] = htole64(acquire_opt.crkey); data[1] = htole64(acquire_opt.prkey); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_RESERVATION_ACQUIRE; pt.cmd.cdw10 = htole32((acquire_opt.racqa & 7) | (acquire_opt.rtype << 8)); pt.buf = &data; pt.len = sizeof(data); pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "acquire request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "acquire request returned error"); close(fd); exit(0); } static void resvregister(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; uint64_t data[2]; int fd; uint32_t nsid; if (arg_parse(argc, argv, f)) return; - open_dev(register_opt.dev, &fd, 1, 1); + open_dev(register_opt.dev, &fd, 0, 1); get_nsid(fd, NULL, &nsid); if (nsid == 0) { fprintf(stderr, "This command require namespace-id\n"); arg_help(argc, argv, f); } data[0] = htole64(register_opt.crkey); data[1] = htole64(register_opt.nrkey); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_RESERVATION_REGISTER; pt.cmd.cdw10 = htole32((register_opt.rrega & 7) | (register_opt.iekey << 3) | (register_opt.cptpl << 30)); pt.buf = &data; pt.len = sizeof(data); pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "register request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "register request returned error"); close(fd); exit(0); } static void resvrelease(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; uint64_t data[1]; int fd; uint32_t nsid; if (arg_parse(argc, argv, f)) return; - open_dev(release_opt.dev, &fd, 1, 1); + open_dev(release_opt.dev, &fd, 0, 1); get_nsid(fd, NULL, &nsid); if (nsid == 0) { fprintf(stderr, "This command require namespace-id\n"); arg_help(argc, argv, f); } data[0] = htole64(release_opt.crkey); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_RESERVATION_RELEASE; pt.cmd.cdw10 = htole32((release_opt.rrela & 7) | (release_opt.rtype << 8)); pt.buf = &data; pt.len = sizeof(data); pt.is_read = 0; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "release request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "release request returned error"); close(fd); exit(0); } static void resvreport(const struct cmd *f, int argc, char *argv[]) { struct nvme_pt_command pt; struct nvme_resv_status *s; struct nvme_resv_status_ext *e; uint8_t data[4096] __aligned(4); int fd; u_int i, n; uint32_t nsid; if (arg_parse(argc, argv, f)) return; - open_dev(report_opt.dev, &fd, 1, 1); + open_dev(report_opt.dev, &fd, 0, 1); get_nsid(fd, NULL, &nsid); if (nsid == 0) { fprintf(stderr, "This command require namespace-id\n"); arg_help(argc, argv, f); } bzero(data, sizeof(data)); memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_RESERVATION_REPORT; pt.cmd.cdw10 = htole32(sizeof(data) / 4 - 1); pt.cmd.cdw11 = htole32(report_opt.eds); /* EDS */ pt.buf = &data; pt.len = sizeof(data); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "report request failed"); if (nvme_completion_is_error(&pt.cpl)) errx(1, "report request returned error"); close(fd); if (report_opt.eds) nvme_resv_status_ext_swapbytes((void *)data, sizeof(data)); else nvme_resv_status_swapbytes((void *)data, sizeof(data)); if (report_opt.hex) { i = sizeof(data); if (!report_opt.verbose) { for (; i > 64; i--) { if (data[i - 1] != 0) break; } } print_hex(&data, i); exit(0); } s = (struct nvme_resv_status *)data; n = (s->regctl[1] << 8) | s->regctl[0]; printf("Generation: %u\n", s->gen); printf("Reservation Type: %u\n", s->rtype); printf("Number of Registered Controllers: %u\n", n); printf("Persist Through Power Loss State: %u\n", s->ptpls); if (report_opt.eds) { e = (struct nvme_resv_status_ext *)data; n = MIN(n, (sizeof(data) - sizeof(e)) / sizeof(e->ctrlr[0])); for (i = 0; i < n; i++) { printf("Controller ID: 0x%04x\n", e->ctrlr[i].ctrlr_id); printf(" Reservation Status: %u\n", e->ctrlr[i].rcsts); printf(" Reservation Key: 0x%08jx\n", e->ctrlr[i].rkey); printf(" Host Identifier: 0x%08jx%08jx\n", e->ctrlr[i].hostid[0], e->ctrlr[i].hostid[1]); } } else { n = MIN(n, (sizeof(data) - sizeof(s)) / sizeof(s->ctrlr[0])); for (i = 0; i < n; i++) { printf("Controller ID: 0x%04x\n", s->ctrlr[i].ctrlr_id); printf(" Reservation Status: %u\n", s->ctrlr[i].rcsts); printf(" Host Identifier: 0x%08jx\n", s->ctrlr[i].hostid); printf(" Reservation Key: 0x%08jx\n", s->ctrlr[i].rkey); } } exit(0); } static void resv(const struct cmd *nf __unused, int argc, char *argv[]) { cmd_dispatch(argc, argv, &resv_cmd); } Index: stable/12 =================================================================== --- stable/12 (revision 360374) +++ stable/12 (revision 360375) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r360117