logpage.c revision 328673
1252277Sjimharris/*- 2252277Sjimharris * Copyright (c) 2013 EMC Corp. 3252277Sjimharris * All rights reserved. 4252277Sjimharris * 5252277Sjimharris * Copyright (C) 2012-2013 Intel Corporation 6252277Sjimharris * All rights reserved. 7252277Sjimharris * 8252277Sjimharris * Redistribution and use in source and binary forms, with or without 9252277Sjimharris * modification, are permitted provided that the following conditions 10252277Sjimharris * are met: 11252277Sjimharris * 1. Redistributions of source code must retain the above copyright 12252277Sjimharris * notice, this list of conditions and the following disclaimer. 13252277Sjimharris * 2. Redistributions in binary form must reproduce the above copyright 14252277Sjimharris * notice, this list of conditions and the following disclaimer in the 15252277Sjimharris * documentation and/or other materials provided with the distribution. 16252277Sjimharris * 17252277Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18252277Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19252277Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20252277Sjimharris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21252277Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22252277Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23252277Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24252277Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25252277Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26252277Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27252277Sjimharris * SUCH DAMAGE. 28252277Sjimharris */ 29252277Sjimharris 30252277Sjimharris#include <sys/cdefs.h> 31252277Sjimharris__FBSDID("$FreeBSD: stable/11/sbin/nvmecontrol/logpage.c 328673 2018-02-01 16:21:45Z mav $"); 32252277Sjimharris 33252277Sjimharris#include <sys/param.h> 34252277Sjimharris#include <sys/ioccom.h> 35252277Sjimharris 36252277Sjimharris#include <ctype.h> 37253109Sjimharris#include <err.h> 38252277Sjimharris#include <fcntl.h> 39252277Sjimharris#include <stdbool.h> 40252277Sjimharris#include <stddef.h> 41252277Sjimharris#include <stdio.h> 42252277Sjimharris#include <stdlib.h> 43252277Sjimharris#include <string.h> 44252277Sjimharris#include <unistd.h> 45328668Smav#include <sys/endian.h> 46252277Sjimharris 47328668Smav#if _BYTE_ORDER != _LITTLE_ENDIAN 48328668Smav#error "Code only works on little endian machines" 49328668Smav#endif 50328668Smav 51252277Sjimharris#include "nvmecontrol.h" 52252277Sjimharris 53252277Sjimharris#define DEFAULT_SIZE (4096) 54252277Sjimharris#define MAX_FW_SLOTS (7) 55252277Sjimharris 56252277Sjimharristypedef void (*print_fn_t)(void *buf, uint32_t size); 57252277Sjimharris 58328668Smav 59328668Smav/* 60328668Smav * 128-bit integer augments to standard values 61328668Smav */ 62328668Smav#define UINT128_DIG 39 63328668Smavtypedef __uint128_t uint128_t; 64328668Smav 65328668Smavstatic inline uint128_t 66328668Smavto128(void *p) 67328668Smav{ 68328668Smav return *(uint128_t *)p; 69328668Smav} 70328668Smav 71328668Smavstatic char * 72328668Smavuint128_to_str(uint128_t u, char *buf, size_t buflen) 73328668Smav{ 74328668Smav char *end = buf + buflen - 1; 75328668Smav 76328668Smav *end-- = '\0'; 77328668Smav if (u == 0) 78328668Smav *end-- = '0'; 79328668Smav while (u && end >= buf) { 80328668Smav *end-- = u % 10 + '0'; 81328668Smav u /= 10; 82328668Smav } 83328668Smav end++; 84328668Smav if (u != 0) 85328668Smav return NULL; 86328668Smav 87328668Smav return end; 88328668Smav} 89328668Smav 90252277Sjimharrisstatic void * 91253109Sjimharrisget_log_buffer(uint32_t size) 92252277Sjimharris{ 93252277Sjimharris void *buf; 94252277Sjimharris 95253109Sjimharris if ((buf = malloc(size)) == NULL) 96253109Sjimharris errx(1, "unable to malloc %u bytes", size); 97253109Sjimharris 98252277Sjimharris memset(buf, 0, size); 99252277Sjimharris return (buf); 100252277Sjimharris} 101252277Sjimharris 102252277Sjimharrisvoid 103328673Smavread_logpage(int fd, uint8_t log_page, int nsid, void *payload, 104252277Sjimharris uint32_t payload_size) 105252277Sjimharris{ 106252277Sjimharris struct nvme_pt_command pt; 107252277Sjimharris 108252277Sjimharris memset(&pt, 0, sizeof(pt)); 109252277Sjimharris pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 110252277Sjimharris pt.cmd.nsid = nsid; 111252277Sjimharris pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 112252277Sjimharris pt.cmd.cdw10 |= log_page; 113252277Sjimharris pt.buf = payload; 114252277Sjimharris pt.len = payload_size; 115252277Sjimharris pt.is_read = 1; 116252277Sjimharris 117253109Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 118253109Sjimharris err(1, "get log page request failed"); 119252277Sjimharris 120253109Sjimharris if (nvme_completion_is_error(&pt.cpl)) 121253109Sjimharris errx(1, "get log page request returned error"); 122252277Sjimharris} 123252277Sjimharris 124252277Sjimharrisstatic void 125252277Sjimharrisprint_log_error(void *buf, uint32_t size) 126252277Sjimharris{ 127252277Sjimharris int i, nentries; 128252277Sjimharris struct nvme_error_information_entry *entry = buf; 129252277Sjimharris struct nvme_status *status; 130252277Sjimharris 131252277Sjimharris printf("Error Information Log\n"); 132252277Sjimharris printf("=====================\n"); 133252277Sjimharris 134252277Sjimharris if (entry->error_count == 0) { 135252277Sjimharris printf("No error entries found\n"); 136252277Sjimharris return; 137252277Sjimharris } 138252277Sjimharris 139252277Sjimharris nentries = size/sizeof(struct nvme_error_information_entry); 140252277Sjimharris for (i = 0; i < nentries; i++, entry++) { 141252277Sjimharris if (entry->error_count == 0) 142252277Sjimharris break; 143252277Sjimharris 144252277Sjimharris status = &entry->status; 145252277Sjimharris printf("Entry %02d\n", i + 1); 146252277Sjimharris printf("=========\n"); 147252277Sjimharris printf(" Error count: %ju\n", entry->error_count); 148252277Sjimharris printf(" Submission queue ID: %u\n", entry->sqid); 149252277Sjimharris printf(" Command ID: %u\n", entry->cid); 150252277Sjimharris /* TODO: Export nvme_status_string structures from kernel? */ 151252277Sjimharris printf(" Status:\n"); 152252277Sjimharris printf(" Phase tag: %d\n", status->p); 153252277Sjimharris printf(" Status code: %d\n", status->sc); 154252277Sjimharris printf(" Status code type: %d\n", status->sct); 155252277Sjimharris printf(" More: %d\n", status->m); 156252277Sjimharris printf(" DNR: %d\n", status->dnr); 157252277Sjimharris printf(" Error location: %u\n", entry->error_location); 158252277Sjimharris printf(" LBA: %ju\n", entry->lba); 159252277Sjimharris printf(" Namespace ID: %u\n", entry->nsid); 160252277Sjimharris printf(" Vendor specific info: %u\n", entry->vendor_specific); 161252277Sjimharris } 162252277Sjimharris} 163252277Sjimharris 164252277Sjimharrisstatic void 165328669Smavprint_temp(uint16_t t) 166328669Smav{ 167328669Smav printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 168328669Smav} 169328669Smav 170328669Smav 171328669Smavstatic void 172252277Sjimharrisprint_log_health(void *buf, uint32_t size __unused) 173252277Sjimharris{ 174252277Sjimharris struct nvme_health_information_page *health = buf; 175328668Smav char cbuf[UINT128_DIG + 1]; 176328669Smav int i; 177252277Sjimharris 178252277Sjimharris printf("SMART/Health Information Log\n"); 179252277Sjimharris printf("============================\n"); 180252277Sjimharris 181252277Sjimharris printf("Critical Warning State: 0x%02x\n", 182252277Sjimharris health->critical_warning.raw); 183252277Sjimharris printf(" Available spare: %d\n", 184252277Sjimharris health->critical_warning.bits.available_spare); 185252277Sjimharris printf(" Temperature: %d\n", 186252277Sjimharris health->critical_warning.bits.temperature); 187252277Sjimharris printf(" Device reliability: %d\n", 188252277Sjimharris health->critical_warning.bits.device_reliability); 189252277Sjimharris printf(" Read only: %d\n", 190252277Sjimharris health->critical_warning.bits.read_only); 191252277Sjimharris printf(" Volatile memory backup: %d\n", 192252277Sjimharris health->critical_warning.bits.volatile_memory_backup); 193328669Smav printf("Temperature: "); 194328669Smav print_temp(health->temperature); 195252277Sjimharris printf("Available spare: %u\n", 196252277Sjimharris health->available_spare); 197252277Sjimharris printf("Available spare threshold: %u\n", 198252277Sjimharris health->available_spare_threshold); 199252277Sjimharris printf("Percentage used: %u\n", 200252277Sjimharris health->percentage_used); 201252277Sjimharris 202328669Smav printf("Data units (512,000 byte) read: %s\n", 203328668Smav uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); 204328669Smav printf("Data units written: %s\n", 205328668Smav uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); 206328668Smav printf("Host read commands: %s\n", 207328668Smav uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); 208328668Smav printf("Host write commands: %s\n", 209328668Smav uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); 210328668Smav printf("Controller busy time (minutes): %s\n", 211328668Smav uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); 212328668Smav printf("Power cycles: %s\n", 213328668Smav uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); 214328668Smav printf("Power on hours: %s\n", 215328668Smav uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); 216328668Smav printf("Unsafe shutdowns: %s\n", 217328668Smav uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); 218328668Smav printf("Media errors: %s\n", 219328668Smav uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); 220328668Smav printf("No. error info log entries: %s\n", 221328668Smav uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); 222328669Smav 223328669Smav printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 224328669Smav printf("Error Temp Composite Time: %d\n", health->error_temp_time); 225328669Smav for (i = 0; i < 7; i++) { 226328669Smav if (health->temp_sensor[i] == 0) 227328669Smav continue; 228328669Smav printf("Temperature Sensor %d: ", i + 1); 229328669Smav print_temp(health->temp_sensor[i]); 230328669Smav } 231252277Sjimharris} 232252277Sjimharris 233252277Sjimharrisstatic void 234252277Sjimharrisprint_log_firmware(void *buf, uint32_t size __unused) 235252277Sjimharris{ 236252277Sjimharris int i; 237252277Sjimharris const char *status; 238252277Sjimharris struct nvme_firmware_page *fw = buf; 239252277Sjimharris 240252277Sjimharris printf("Firmware Slot Log\n"); 241252277Sjimharris printf("=================\n"); 242252277Sjimharris 243252277Sjimharris for (i = 0; i < MAX_FW_SLOTS; i++) { 244252277Sjimharris printf("Slot %d: ", i + 1); 245252277Sjimharris if (fw->afi.slot == i + 1) 246252277Sjimharris status = " Active"; 247252277Sjimharris else 248252277Sjimharris status = "Inactive"; 249252277Sjimharris 250252277Sjimharris if (fw->revision[i] == 0LLU) 251252277Sjimharris printf("Empty\n"); 252252277Sjimharris else 253252277Sjimharris if (isprint(*(char *)&fw->revision[i])) 254252277Sjimharris printf("[%s] %.8s\n", status, 255252277Sjimharris (char *)&fw->revision[i]); 256252277Sjimharris else 257252277Sjimharris printf("[%s] %016jx\n", status, 258252277Sjimharris fw->revision[i]); 259252277Sjimharris } 260252277Sjimharris} 261252277Sjimharris 262328673Smavstatic void 263328673Smavprint_intel_temp_stats(void *buf, uint32_t size __unused) 264328673Smav{ 265328673Smav struct intel_log_temp_stats *temp = buf; 266328673Smav 267328673Smav printf("Intel Temperature Log\n"); 268328673Smav printf("=====================\n"); 269328673Smav 270328673Smav printf("Current: "); 271328673Smav print_temp(temp->current); 272328673Smav printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); 273328673Smav printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); 274328673Smav printf("Max Temperature "); 275328673Smav print_temp(temp->max_temp); 276328673Smav printf("Min Temperature "); 277328673Smav print_temp(temp->min_temp); 278328673Smav printf("Max Operating Temperature "); 279328673Smav print_temp(temp->max_oper_temp); 280328673Smav printf("Min Operating Temperature "); 281328673Smav print_temp(temp->min_oper_temp); 282328673Smav printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); 283328673Smav} 284328673Smav 285328673Smav/* 286328673Smav * Table of log page printer / sizing. 287328673Smav * 288328673Smav * This includes Intel specific pages that are widely implemented. Not 289328673Smav * sure how best to switch between different vendors. 290328673Smav */ 291252302Sglebiusstatic struct logpage_function { 292252277Sjimharris uint8_t log_page; 293328672Smav print_fn_t print_fn; 294328672Smav size_t size; 295252277Sjimharris} logfuncs[] = { 296328672Smav {NVME_LOG_ERROR, print_log_error, 297328672Smav 0}, 298328672Smav {NVME_LOG_HEALTH_INFORMATION, print_log_health, 299328672Smav sizeof(struct nvme_health_information_page)}, 300328672Smav {NVME_LOG_FIRMWARE_SLOT, print_log_firmware, 301328672Smav sizeof(struct nvme_firmware_page)}, 302328673Smav {INTEL_LOG_TEMP_STATS, print_intel_temp_stats, 303328673Smav sizeof(struct intel_log_temp_stats)}, 304328672Smav {0, NULL, 305328672Smav 0}, 306252277Sjimharris}; 307252277Sjimharris 308252277Sjimharrisstatic void 309252277Sjimharrislogpage_usage(void) 310252277Sjimharris{ 311252277Sjimharris fprintf(stderr, "usage:\n"); 312252277Sjimharris fprintf(stderr, LOGPAGE_USAGE); 313253109Sjimharris exit(1); 314252277Sjimharris} 315252277Sjimharris 316252277Sjimharrisvoid 317252277Sjimharrislogpage(int argc, char *argv[]) 318252277Sjimharris{ 319253114Sjimharris int fd, nsid; 320252277Sjimharris int log_page = 0, pageflag = false; 321253114Sjimharris int hexflag = false, ns_specified; 322253114Sjimharris char ch, *p; 323253114Sjimharris char cname[64]; 324253109Sjimharris uint32_t size; 325252277Sjimharris void *buf; 326252277Sjimharris struct logpage_function *f; 327252277Sjimharris struct nvme_controller_data cdata; 328252277Sjimharris print_fn_t print_fn; 329252277Sjimharris 330252277Sjimharris while ((ch = getopt(argc, argv, "p:x")) != -1) { 331252277Sjimharris switch (ch) { 332252277Sjimharris case 'p': 333252277Sjimharris /* TODO: Add human-readable ASCII page IDs */ 334252277Sjimharris log_page = strtol(optarg, &p, 0); 335252277Sjimharris if (p != NULL && *p != '\0') { 336252277Sjimharris fprintf(stderr, 337252277Sjimharris "\"%s\" not valid log page id.\n", 338252277Sjimharris optarg); 339252277Sjimharris logpage_usage(); 340252277Sjimharris /* TODO: Define valid log page id ranges in nvme.h? */ 341252277Sjimharris } else if (log_page == 0 || 342252277Sjimharris (log_page >= 0x04 && log_page <= 0x7F) || 343252277Sjimharris (log_page >= 0x80 && log_page <= 0xBF)) { 344252277Sjimharris fprintf(stderr, 345252277Sjimharris "\"%s\" not valid log page id.\n", 346252277Sjimharris optarg); 347252277Sjimharris logpage_usage(); 348252277Sjimharris } 349252277Sjimharris pageflag = true; 350252277Sjimharris break; 351252277Sjimharris case 'x': 352252277Sjimharris hexflag = true; 353252277Sjimharris break; 354252277Sjimharris } 355252277Sjimharris } 356252277Sjimharris 357252277Sjimharris if (!pageflag) { 358252277Sjimharris printf("Missing page_id (-p).\n"); 359252277Sjimharris logpage_usage(); 360252277Sjimharris } 361252277Sjimharris 362252277Sjimharris /* Check that a controller and/or namespace was specified. */ 363252277Sjimharris if (optind >= argc) 364252277Sjimharris logpage_usage(); 365252277Sjimharris 366253114Sjimharris if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 367253114Sjimharris ns_specified = true; 368253114Sjimharris parse_ns_str(argv[optind], cname, &nsid); 369253114Sjimharris open_dev(cname, &fd, 1, 1); 370253114Sjimharris } else { 371253114Sjimharris ns_specified = false; 372253114Sjimharris nsid = NVME_GLOBAL_NAMESPACE_TAG; 373253114Sjimharris open_dev(argv[optind], &fd, 1, 1); 374253114Sjimharris } 375253114Sjimharris 376285796Sjimharris read_controller_data(fd, &cdata); 377285796Sjimharris 378252277Sjimharris /* 379252277Sjimharris * The log page attribtues indicate whether or not the controller 380252277Sjimharris * supports the SMART/Health information log page on a per 381252277Sjimharris * namespace basis. 382252277Sjimharris */ 383253114Sjimharris if (ns_specified) { 384253114Sjimharris if (log_page != NVME_LOG_HEALTH_INFORMATION) 385253114Sjimharris errx(1, "log page %d valid only at controller level", 386253114Sjimharris log_page); 387253114Sjimharris if (cdata.lpa.ns_smart == 0) 388253114Sjimharris errx(1, 389253114Sjimharris "controller does not support per namespace " 390253114Sjimharris "smart/health information"); 391253114Sjimharris } 392252277Sjimharris 393252277Sjimharris print_fn = print_hex; 394328672Smav size = DEFAULT_SIZE; 395252277Sjimharris if (!hexflag) { 396252277Sjimharris /* 397252277Sjimharris * See if there is a pretty print function for the 398252277Sjimharris * specified log page. If one isn't found, we 399252277Sjimharris * just revert to the default (print_hex). 400252277Sjimharris */ 401252277Sjimharris f = logfuncs; 402252277Sjimharris while (f->log_page > 0) { 403252277Sjimharris if (log_page == f->log_page) { 404328672Smav print_fn = f->print_fn; 405328672Smav size = f->size; 406252277Sjimharris break; 407252277Sjimharris } 408252277Sjimharris f++; 409252277Sjimharris } 410252277Sjimharris } 411252277Sjimharris 412328672Smav if (log_page == NVME_LOG_ERROR) { 413252277Sjimharris size = sizeof(struct nvme_error_information_entry); 414252277Sjimharris size *= (cdata.elpe + 1); 415252277Sjimharris } 416252277Sjimharris 417328672Smav /* Read the log page */ 418252277Sjimharris buf = get_log_buffer(size); 419252277Sjimharris read_logpage(fd, log_page, nsid, buf, size); 420252277Sjimharris print_fn(buf, size); 421252277Sjimharris 422252277Sjimharris close(fd); 423253109Sjimharris exit(0); 424252277Sjimharris} 425