diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index f7a6ef311a40..4ed941e5f045 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -1,860 +1,865 @@ /*- * 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 #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_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_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(uint16_t t) +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(health->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(health->temp_sensor[i]); + 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; 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); } 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; /* * 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(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/modules/intel/intel.c b/sbin/nvmecontrol/modules/intel/intel.c index 8e3ed9e06cb9..895ffe30683f 100644 --- a/sbin/nvmecontrol/modules/intel/intel.c +++ b/sbin/nvmecontrol/modules/intel/intel.c @@ -1,195 +1,195 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" /* * Intel specific log pages from * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf * * Though the version as of this date has a typo for the size of log page 0xca, * offset 147: it is only 1 byte, not 6. */ static void print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct intel_log_temp_stats *temp = buf; printf("Intel Temperature Log\n"); printf("=====================\n"); printf("Current: "); - print_temp(temp->current); + print_temp_C(temp->current); printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); printf("Max Temperature "); - print_temp(temp->max_temp); + print_temp_C(temp->max_temp); printf("Min Temperature "); - print_temp(temp->min_temp); + print_temp_C(temp->min_temp); printf("Max Operating Temperature "); - print_temp(temp->max_oper_temp); + print_temp_C(temp->max_oper_temp); printf("Min Operating Temperature "); - print_temp(temp->min_oper_temp); + print_temp_C(temp->min_oper_temp); printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); } /* * Format from Table 22, section 5.7 IO Command Latency Statistics. * Read and write stats pages have identical encoding. */ static void print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { const char *walker = buf; int i; printf("Major: %d\n", le16dec(walker + 0)); printf("Minor: %d\n", le16dec(walker + 2)); for (i = 0; i < 32; i++) printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4)); for (i = 1; i < 32; i++) printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4)); for (i = 1; i < 32; i++) printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4)); } static void print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) { printf("Intel Read Latency Log\n"); printf("======================\n"); print_intel_read_write_lat_log(cdata, buf, size); } static void print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) { printf("Intel Write Latency Log\n"); printf("=======================\n"); print_intel_read_write_lat_log(cdata, buf, size); } /* * Table 19. 5.4 SMART Attributes. Others also implement this and some extra data not documented. */ void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { uint8_t *walker = buf; uint8_t *end = walker + 150; const char *name; uint64_t raw; uint8_t normalized; static struct kv_name kv[] = { { 0xab, "Program Fail Count" }, { 0xac, "Erase Fail Count" }, { 0xad, "Wear Leveling Count" }, { 0xb8, "End to End Error Count" }, { 0xc7, "CRC Error Count" }, { 0xe2, "Timed: Media Wear" }, { 0xe3, "Timed: Host Read %" }, { 0xe4, "Timed: Elapsed Time" }, { 0xea, "Thermal Throttle Status" }, { 0xf0, "Retry Buffer Overflows" }, { 0xf3, "PLL Lock Loss Count" }, { 0xf4, "NAND Bytes Written" }, { 0xf5, "Host Bytes Written" }, }; printf("Additional SMART Data Log\n"); printf("=========================\n"); /* * walker[0] = Key * walker[1,2] = reserved * walker[3] = Normalized Value * walker[4] = reserved * walker[5..10] = Little Endian Raw value * (or other represenations) * walker[11] = reserved */ while (walker < end) { name = kv_lookup(kv, nitems(kv), *walker); normalized = walker[3]; raw = le48dec(walker + 5); switch (*walker){ case 0: break; case 0xad: printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized, le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); break; case 0xe2: printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); break; case 0xea: printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); break; default: printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); break; } walker += 12; } } NVME_LOGPAGE(intel_temp, INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats", print_intel_temp_stats, sizeof(struct intel_log_temp_stats)); NVME_LOGPAGE(intel_rlat, INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies", print_intel_read_lat_log, DEFAULT_SIZE); NVME_LOGPAGE(intel_wlat, INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies", print_intel_write_lat_log, DEFAULT_SIZE); NVME_LOGPAGE(intel_smart, INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data", print_intel_add_smart, DEFAULT_SIZE); diff --git a/sbin/nvmecontrol/modules/samsung/samsung.c b/sbin/nvmecontrol/modules/samsung/samsung.c index 497acf46e01c..d07732967b54 100644 --- a/sbin/nvmecontrol/modules/samsung/samsung.c +++ b/sbin/nvmecontrol/modules/samsung/samsung.c @@ -1,165 +1,165 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2022 Wanpeng Qian * * 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" struct samsung_log_extended_smart { uint8_t kv[256];/* Key-Value pair */ uint32_t lwaf; /* Lifetime Write Amplification */ uint32_t thwaf; /* Trailing Hour Write Amplification Factor */ uint64_t luw[2]; /* Lifetime User Writes */ uint64_t lnw[2]; /* Lifetime NAND Writes */ uint64_t lur[2]; /* Lifetime User Reads */ uint32_t lrbc; /* Lifetime Retired Block Count */ uint16_t ct; /* Current Temperature */ uint16_t ch; /* Capacitor Health */ uint32_t luurb; /* Lifetime Unused Reserved Block */ uint64_t rrc; /* Read Reclaim Count */ uint64_t lueccc; /* Lifetime Uncorrectable ECC count */ uint32_t lurb; /* Lifetime Used Reserved Block */ uint64_t poh[2]; /* Power on Hours */ uint64_t npoc[2];/* Normal Power Off Count */ uint64_t spoc[2];/* Sudden Power Off Count */ uint32_t pi; /* Performance Indicator */ } __packed; static void print_samsung_extended_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct samsung_log_extended_smart *temp = buf; char cbuf[UINT128_DIG + 1]; uint8_t *walker = buf; uint8_t *end = walker + 150; const char *name; uint64_t raw; uint8_t normalized; static struct kv_name kv[] = { { 0xab, "Lifetime Program Fail Count" }, { 0xac, "Lifetime Erase Fail Count" }, { 0xad, "Lifetime Wear Leveling Count" }, { 0xb8, "Lifetime End to End Error Count" }, { 0xc7, "Lifetime CRC Error Count" }, { 0xe2, "Media Wear %" }, { 0xe3, "Host Read %" }, { 0xe4, "Workload Timer" }, { 0xea, "Lifetime Thermal Throttle Status" }, { 0xf4, "Lifetime Phy Pages Written Count" }, { 0xf5, "Lifetime Data Units Written" }, }; printf("Extended SMART Information\n"); printf("=========================\n"); /* * walker[0] = Key * walker[1,2] = reserved * walker[3] = Normalized Value * walker[4] = reserved * walker[5..10] = Little Endian Raw value * (or other represenations) * walker[11] = reserved */ while (walker < end) { name = kv_lookup(kv, nitems(kv), *walker); normalized = walker[3]; raw = le48dec(walker + 5); switch (*walker){ case 0: break; case 0xad: printf("%2X %-41s: %3d min: %u max: %u ave: %u\n", le16dec(walker), name, normalized, le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); break; case 0xe2: printf("%2X %-41s: %3d %.3f%%\n", le16dec(walker), name, normalized, raw / 1024.0); break; case 0xea: printf("%2X %-41s: %3d %d%% %d times\n", le16dec(walker), name, normalized, walker[5], le32dec(walker+6)); break; default: printf("%2X %-41s: %3d %ju\n", le16dec(walker), name, normalized, (uintmax_t)raw); break; } walker += 12; } printf(" Lifetime Write Amplification Factor : %u\n", le32dec(&temp->lwaf)); printf(" Trailing Hour Write Amplification Factor : %u\n", le32dec(&temp->thwaf)); printf(" Lifetime User Writes : %s\n", uint128_to_str(to128(temp->luw), cbuf, sizeof(cbuf))); printf(" Lifetime NAND Writes : %s\n", uint128_to_str(to128(temp->lnw), cbuf, sizeof(cbuf))); printf(" Lifetime User Reads : %s\n", uint128_to_str(to128(temp->lur), cbuf, sizeof(cbuf))); printf(" Lifetime Retired Block Count : %u\n", le32dec(&temp->lrbc)); printf(" Current Temperature : "); - print_temp(le16dec(&temp->ct)); + print_temp_K(le16dec(&temp->ct)); printf(" Capacitor Health : %u\n", le16dec(&temp->ch)); printf(" Reserved Erase Block Count : %u\n", le32dec(&temp->luurb)); printf(" Read Reclaim Count : %ju\n", (uintmax_t) le64dec(&temp->rrc)); printf(" Lifetime Uncorrectable ECC Count : %ju\n", (uintmax_t) le64dec(&temp->lueccc)); printf(" Reallocated Block Count : %u\n", le32dec(&temp->lurb)); printf(" Power on Hours : %s\n", uint128_to_str(to128(temp->poh), cbuf, sizeof(cbuf))); printf(" Normal Power Off Count : %s\n", uint128_to_str(to128(temp->npoc), cbuf, sizeof(cbuf))); printf(" Sudden Power Off Count : %s\n", uint128_to_str(to128(temp->spoc), cbuf, sizeof(cbuf))); printf(" Performance Indicator : %u\n", le32dec(&temp->pi)); } #define SAMSUNG_LOG_EXTEND_SMART 0xca NVME_LOGPAGE(samsung_extended_smart, SAMSUNG_LOG_EXTEND_SMART, "samsung", "Extended SMART Information", print_samsung_extended_smart, DEFAULT_SIZE); diff --git a/sbin/nvmecontrol/nvmecontrol.h b/sbin/nvmecontrol/nvmecontrol.h index c1daaf6730d3..4b587c48d9a0 100644 --- a/sbin/nvmecontrol/nvmecontrol.h +++ b/sbin/nvmecontrol/nvmecontrol.h @@ -1,104 +1,105 @@ /*- * 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 write, int exit_on_error); void get_nsid(int fd, char **ctrlr_str, uint32_t *nsid); int read_controller_data(int fd, struct nvme_controller_data *cdata); int 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_temp_C(uint16_t t); +void print_temp_K(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