logpage.c revision 252277
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: head/sbin/nvmecontrol/logpage.c 252277 2013-06-26 23:53:54Z jimharris $"); 32252277Sjimharris 33252277Sjimharris#include <sys/param.h> 34252277Sjimharris#include <sys/ioccom.h> 35252277Sjimharris 36252277Sjimharris#include <ctype.h> 37252277Sjimharris#include <errno.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 <sysexits.h> 45252277Sjimharris#include <unistd.h> 46252277Sjimharris 47252277Sjimharris#include "nvmecontrol.h" 48252277Sjimharris 49252277Sjimharris#define DEFAULT_SIZE (4096) 50252277Sjimharris#define MAX_FW_SLOTS (7) 51252277Sjimharris 52252277Sjimharristypedef void (*print_fn_t)(void *buf, uint32_t size); 53252277Sjimharris 54252277Sjimharrisstatic void * 55252277Sjimharrisget_log_buffer(size_t size) 56252277Sjimharris{ 57252277Sjimharris void *buf; 58252277Sjimharris 59252277Sjimharris if ((buf = malloc(size)) == NULL) { 60252277Sjimharris fprintf(stderr, "Unable to malloc %zd bytes\n", size); 61252277Sjimharris exit(EX_IOERR); 62252277Sjimharris } 63252277Sjimharris memset(buf, 0, size); 64252277Sjimharris return (buf); 65252277Sjimharris} 66252277Sjimharris 67252277Sjimharrisvoid 68252277Sjimharrisread_logpage(int fd, uint8_t log_page, int nsid, void *payload, 69252277Sjimharris uint32_t payload_size) 70252277Sjimharris{ 71252277Sjimharris struct nvme_pt_command pt; 72252277Sjimharris 73252277Sjimharris memset(&pt, 0, sizeof(pt)); 74252277Sjimharris pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 75252277Sjimharris pt.cmd.nsid = nsid; 76252277Sjimharris pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 77252277Sjimharris pt.cmd.cdw10 |= log_page; 78252277Sjimharris pt.buf = payload; 79252277Sjimharris pt.len = payload_size; 80252277Sjimharris pt.is_read = 1; 81252277Sjimharris 82252277Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) { 83252277Sjimharris printf("Get log page request failed. errno=%d (%s)\n", 84252277Sjimharris errno, strerror(errno)); 85252277Sjimharris exit(EX_IOERR); 86252277Sjimharris } 87252277Sjimharris 88252277Sjimharris if (nvme_completion_is_error(&pt.cpl)) { 89252277Sjimharris printf("Passthrough command returned error.\n"); 90252277Sjimharris exit(EX_IOERR); 91252277Sjimharris } 92252277Sjimharris} 93252277Sjimharris 94252277Sjimharrisstatic void 95252277Sjimharrisprint_log_error(void *buf, uint32_t size) 96252277Sjimharris{ 97252277Sjimharris int i, nentries; 98252277Sjimharris struct nvme_error_information_entry *entry = buf; 99252277Sjimharris struct nvme_status *status; 100252277Sjimharris 101252277Sjimharris printf("Error Information Log\n"); 102252277Sjimharris printf("=====================\n"); 103252277Sjimharris 104252277Sjimharris if (entry->error_count == 0) { 105252277Sjimharris printf("No error entries found\n"); 106252277Sjimharris return; 107252277Sjimharris } 108252277Sjimharris 109252277Sjimharris nentries = size/sizeof(struct nvme_error_information_entry); 110252277Sjimharris for (i = 0; i < nentries; i++, entry++) { 111252277Sjimharris if (entry->error_count == 0) 112252277Sjimharris break; 113252277Sjimharris 114252277Sjimharris status = &entry->status; 115252277Sjimharris printf("Entry %02d\n", i + 1); 116252277Sjimharris printf("=========\n"); 117252277Sjimharris printf(" Error count: %ju\n", entry->error_count); 118252277Sjimharris printf(" Submission queue ID: %u\n", entry->sqid); 119252277Sjimharris printf(" Command ID: %u\n", entry->cid); 120252277Sjimharris /* TODO: Export nvme_status_string structures from kernel? */ 121252277Sjimharris printf(" Status:\n"); 122252277Sjimharris printf(" Phase tag: %d\n", status->p); 123252277Sjimharris printf(" Status code: %d\n", status->sc); 124252277Sjimharris printf(" Status code type: %d\n", status->sct); 125252277Sjimharris printf(" More: %d\n", status->m); 126252277Sjimharris printf(" DNR: %d\n", status->dnr); 127252277Sjimharris printf(" Error location: %u\n", entry->error_location); 128252277Sjimharris printf(" LBA: %ju\n", entry->lba); 129252277Sjimharris printf(" Namespace ID: %u\n", entry->nsid); 130252277Sjimharris printf(" Vendor specific info: %u\n", entry->vendor_specific); 131252277Sjimharris } 132252277Sjimharris} 133252277Sjimharris 134252277Sjimharrisstatic void 135252277Sjimharrisprint_log_health(void *buf, uint32_t size __unused) 136252277Sjimharris{ 137252277Sjimharris struct nvme_health_information_page *health = buf; 138252277Sjimharris 139252277Sjimharris printf("SMART/Health Information Log\n"); 140252277Sjimharris printf("============================\n"); 141252277Sjimharris 142252277Sjimharris printf("Critical Warning State: 0x%02x\n", 143252277Sjimharris health->critical_warning.raw); 144252277Sjimharris printf(" Available spare: %d\n", 145252277Sjimharris health->critical_warning.bits.available_spare); 146252277Sjimharris printf(" Temperature: %d\n", 147252277Sjimharris health->critical_warning.bits.temperature); 148252277Sjimharris printf(" Device reliability: %d\n", 149252277Sjimharris health->critical_warning.bits.device_reliability); 150252277Sjimharris printf(" Read only: %d\n", 151252277Sjimharris health->critical_warning.bits.read_only); 152252277Sjimharris printf(" Volatile memory backup: %d\n", 153252277Sjimharris health->critical_warning.bits.volatile_memory_backup); 154252277Sjimharris printf("Temperature: %u K, %2.2f C, %3.2f F\n", 155252277Sjimharris health->temperature, 156252277Sjimharris (float)health->temperature - (float)273.15, 157252277Sjimharris ((float)health->temperature * (float)9/5) - (float)459.67); 158252277Sjimharris printf("Available spare: %u\n", 159252277Sjimharris health->available_spare); 160252277Sjimharris printf("Available spare threshold: %u\n", 161252277Sjimharris health->available_spare_threshold); 162252277Sjimharris printf("Percentage used: %u\n", 163252277Sjimharris health->percentage_used); 164252277Sjimharris 165252277Sjimharris /* 166252277Sjimharris * TODO: These are pretty ugly in hex. Is there a library that 167252277Sjimharris * will convert 128-bit unsigned values to decimal? 168252277Sjimharris */ 169252277Sjimharris printf("Data units (512 byte) read: 0x%016jx%016jx\n", 170252277Sjimharris health->data_units_read[1], 171252277Sjimharris health->data_units_read[0]); 172252277Sjimharris printf("Data units (512 byte) written: 0x%016jx%016jx\n", 173252277Sjimharris health->data_units_written[1], 174252277Sjimharris health->data_units_written[0]); 175252277Sjimharris printf("Host read commands: 0x%016jx%016jx\n", 176252277Sjimharris health->host_read_commands[1], 177252277Sjimharris health->host_read_commands[0]); 178252277Sjimharris printf("Host write commands: 0x%016jx%016jx\n", 179252277Sjimharris health->host_write_commands[1], 180252277Sjimharris health->host_write_commands[0]); 181252277Sjimharris printf("Controller busy time (minutes): 0x%016jx%016jx\n", 182252277Sjimharris health->controller_busy_time[1], 183252277Sjimharris health->controller_busy_time[0]); 184252277Sjimharris printf("Power cycles: 0x%016jx%016jx\n", 185252277Sjimharris health->power_cycles[1], 186252277Sjimharris health->power_cycles[0]); 187252277Sjimharris printf("Power on hours: 0x%016jx%016jx\n", 188252277Sjimharris health->power_on_hours[1], 189252277Sjimharris health->power_on_hours[0]); 190252277Sjimharris printf("Unsafe shutdowns: 0x%016jx%016jx\n", 191252277Sjimharris health->unsafe_shutdowns[1], 192252277Sjimharris health->unsafe_shutdowns[0]); 193252277Sjimharris printf("Media errors: 0x%016jx%016jx\n", 194252277Sjimharris health->media_errors[1], 195252277Sjimharris health->media_errors[0]); 196252277Sjimharris printf("No. error info log entries: 0x%016jx%016jx\n", 197252277Sjimharris health->num_error_info_log_entries[1], 198252277Sjimharris health->num_error_info_log_entries[0]); 199252277Sjimharris} 200252277Sjimharris 201252277Sjimharrisstatic void 202252277Sjimharrisprint_log_firmware(void *buf, uint32_t size __unused) 203252277Sjimharris{ 204252277Sjimharris int i; 205252277Sjimharris const char *status; 206252277Sjimharris struct nvme_firmware_page *fw = buf; 207252277Sjimharris 208252277Sjimharris printf("Firmware Slot Log\n"); 209252277Sjimharris printf("=================\n"); 210252277Sjimharris 211252277Sjimharris for (i = 0; i < MAX_FW_SLOTS; i++) { 212252277Sjimharris printf("Slot %d: ", i + 1); 213252277Sjimharris if (fw->afi.slot == i + 1) 214252277Sjimharris status = " Active"; 215252277Sjimharris else 216252277Sjimharris status = "Inactive"; 217252277Sjimharris 218252277Sjimharris if (fw->revision[i] == 0LLU) 219252277Sjimharris printf("Empty\n"); 220252277Sjimharris else 221252277Sjimharris if (isprint(*(char *)&fw->revision[i])) 222252277Sjimharris printf("[%s] %.8s\n", status, 223252277Sjimharris (char *)&fw->revision[i]); 224252277Sjimharris else 225252277Sjimharris printf("[%s] %016jx\n", status, 226252277Sjimharris fw->revision[i]); 227252277Sjimharris } 228252277Sjimharris} 229252277Sjimharris 230252277Sjimharrisstruct logpage_function { 231252277Sjimharris uint8_t log_page; 232252277Sjimharris print_fn_t fn; 233252277Sjimharris} logfuncs[] = { 234252277Sjimharris {NVME_LOG_ERROR, print_log_error }, 235252277Sjimharris {NVME_LOG_HEALTH_INFORMATION, print_log_health }, 236252277Sjimharris {NVME_LOG_FIRMWARE_SLOT, print_log_firmware }, 237252277Sjimharris {0, NULL }, 238252277Sjimharris}; 239252277Sjimharris 240252277Sjimharrisstatic void 241252277Sjimharrislogpage_usage(void) 242252277Sjimharris{ 243252277Sjimharris fprintf(stderr, "usage:\n"); 244252277Sjimharris fprintf(stderr, LOGPAGE_USAGE); 245252277Sjimharris exit(EX_USAGE); 246252277Sjimharris} 247252277Sjimharris 248252277Sjimharrisvoid 249252277Sjimharrislogpage(int argc, char *argv[]) 250252277Sjimharris{ 251252277Sjimharris int fd, nsid, len; 252252277Sjimharris int log_page = 0, pageflag = false; 253252277Sjimharris int hexflag = false; 254252277Sjimharris int allow_ns = false; 255252277Sjimharris char ch, *p, *nsloc = NULL; 256252277Sjimharris char *cname = NULL; 257252277Sjimharris size_t size; 258252277Sjimharris void *buf; 259252277Sjimharris struct logpage_function *f; 260252277Sjimharris struct nvme_controller_data cdata; 261252277Sjimharris print_fn_t print_fn; 262252277Sjimharris 263252277Sjimharris while ((ch = getopt(argc, argv, "p:x")) != -1) { 264252277Sjimharris switch (ch) { 265252277Sjimharris case 'p': 266252277Sjimharris /* TODO: Add human-readable ASCII page IDs */ 267252277Sjimharris log_page = strtol(optarg, &p, 0); 268252277Sjimharris if (p != NULL && *p != '\0') { 269252277Sjimharris fprintf(stderr, 270252277Sjimharris "\"%s\" not valid log page id.\n", 271252277Sjimharris optarg); 272252277Sjimharris logpage_usage(); 273252277Sjimharris /* TODO: Define valid log page id ranges in nvme.h? */ 274252277Sjimharris } else if (log_page == 0 || 275252277Sjimharris (log_page >= 0x04 && log_page <= 0x7F) || 276252277Sjimharris (log_page >= 0x80 && log_page <= 0xBF)) { 277252277Sjimharris fprintf(stderr, 278252277Sjimharris "\"%s\" not valid log page id.\n", 279252277Sjimharris optarg); 280252277Sjimharris logpage_usage(); 281252277Sjimharris } 282252277Sjimharris pageflag = true; 283252277Sjimharris break; 284252277Sjimharris case 'x': 285252277Sjimharris hexflag = true; 286252277Sjimharris break; 287252277Sjimharris } 288252277Sjimharris } 289252277Sjimharris 290252277Sjimharris if (!pageflag) { 291252277Sjimharris printf("Missing page_id (-p).\n"); 292252277Sjimharris logpage_usage(); 293252277Sjimharris } 294252277Sjimharris 295252277Sjimharris /* Check that a controller and/or namespace was specified. */ 296252277Sjimharris if (optind >= argc) 297252277Sjimharris logpage_usage(); 298252277Sjimharris 299252277Sjimharris /* 300252277Sjimharris * The log page attribtues indicate whether or not the controller 301252277Sjimharris * supports the SMART/Health information log page on a per 302252277Sjimharris * namespace basis. 303252277Sjimharris */ 304252277Sjimharris cname = malloc(strlen(NVME_CTRLR_PREFIX) + 2); 305252277Sjimharris len = strlen(NVME_CTRLR_PREFIX) + 1; 306252277Sjimharris cname = strncpy(cname, argv[optind], len); 307252277Sjimharris open_dev(cname, &fd, 1, 1); 308252277Sjimharris read_controller_data(fd, &cdata); 309252277Sjimharris 310252277Sjimharris if (log_page == NVME_LOG_HEALTH_INFORMATION && cdata.lpa.ns_smart != 0) 311252277Sjimharris allow_ns = true; 312252277Sjimharris 313252277Sjimharris /* If a namespace id was specified, validate it's use */ 314252277Sjimharris if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 315252277Sjimharris if (!allow_ns) { 316252277Sjimharris if (log_page != NVME_LOG_HEALTH_INFORMATION) { 317252277Sjimharris fprintf(stderr, 318252277Sjimharris "Namespace ID not valid for log page %d.\n", 319252277Sjimharris log_page); 320252277Sjimharris } else if (cdata.lpa.ns_smart == 0) { 321252277Sjimharris fprintf(stderr, 322252277Sjimharris "Controller does not support per " 323252277Sjimharris "namespace SMART/Health information.\n"); 324252277Sjimharris } 325252277Sjimharris close(fd); 326252277Sjimharris exit(EX_IOERR); 327252277Sjimharris } 328252277Sjimharris nsloc = strnstr(argv[optind], NVME_NS_PREFIX, 10); 329252277Sjimharris if (nsloc != NULL) 330252277Sjimharris nsid = strtol(nsloc + 2, NULL, 10); 331252277Sjimharris if (nsloc == NULL || (nsid == 0 && errno != 0)) { 332252277Sjimharris fprintf(stderr, 333252277Sjimharris "Invalid namespace ID %s.\n", 334252277Sjimharris argv[optind]); 335252277Sjimharris close(fd); 336252277Sjimharris exit(EX_IOERR); 337252277Sjimharris } 338252277Sjimharris 339252277Sjimharris /* 340252277Sjimharris * User is asking for per namespace log page information 341252277Sjimharris * so close the controller and open up the namespace. 342252277Sjimharris */ 343252277Sjimharris close(fd); 344252277Sjimharris open_dev(argv[optind], &fd, 1, 1); 345252277Sjimharris } else 346252277Sjimharris nsid = NVME_GLOBAL_NAMESPACE_TAG; 347252277Sjimharris 348252277Sjimharris print_fn = print_hex; 349252277Sjimharris if (!hexflag) { 350252277Sjimharris /* 351252277Sjimharris * See if there is a pretty print function for the 352252277Sjimharris * specified log page. If one isn't found, we 353252277Sjimharris * just revert to the default (print_hex). 354252277Sjimharris */ 355252277Sjimharris f = logfuncs; 356252277Sjimharris while (f->log_page > 0) { 357252277Sjimharris if (log_page == f->log_page) { 358252277Sjimharris print_fn = f->fn; 359252277Sjimharris break; 360252277Sjimharris } 361252277Sjimharris f++; 362252277Sjimharris } 363252277Sjimharris } 364252277Sjimharris 365252277Sjimharris /* Read the log page */ 366252277Sjimharris switch (log_page) { 367252277Sjimharris case NVME_LOG_ERROR: 368252277Sjimharris size = sizeof(struct nvme_error_information_entry); 369252277Sjimharris size *= (cdata.elpe + 1); 370252277Sjimharris break; 371252277Sjimharris case NVME_LOG_HEALTH_INFORMATION: 372252277Sjimharris size = sizeof(struct nvme_health_information_page); 373252277Sjimharris break; 374252277Sjimharris case NVME_LOG_FIRMWARE_SLOT: 375252277Sjimharris size = sizeof(struct nvme_firmware_page); 376252277Sjimharris break; 377252277Sjimharris default: 378252277Sjimharris size = DEFAULT_SIZE; 379252277Sjimharris break; 380252277Sjimharris } 381252277Sjimharris 382252277Sjimharris buf = get_log_buffer(size); 383252277Sjimharris read_logpage(fd, log_page, nsid, buf, size); 384252277Sjimharris print_fn(buf, size); 385252277Sjimharris 386252277Sjimharris close(fd); 387252277Sjimharris exit(EX_OK); 388252277Sjimharris} 389