logpage.c revision 253109
1/*- 2 * Copyright (c) 2013 EMC Corp. 3 * All rights reserved. 4 * 5 * Copyright (C) 2012-2013 Intel Corporation 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 253109 2013-07-09 21:14:15Z jimharris $"); 32 33#include <sys/param.h> 34#include <sys/ioccom.h> 35 36#include <ctype.h> 37#include <err.h> 38#include <errno.h> 39#include <fcntl.h> 40#include <stdbool.h> 41#include <stddef.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <unistd.h> 46 47#include "nvmecontrol.h" 48 49#define DEFAULT_SIZE (4096) 50#define MAX_FW_SLOTS (7) 51 52typedef void (*print_fn_t)(void *buf, uint32_t size); 53 54static void * 55get_log_buffer(uint32_t size) 56{ 57 void *buf; 58 59 if ((buf = malloc(size)) == NULL) 60 errx(1, "unable to malloc %u bytes", size); 61 62 memset(buf, 0, size); 63 return (buf); 64} 65 66void 67read_logpage(int fd, uint8_t log_page, int nsid, void *payload, 68 uint32_t payload_size) 69{ 70 struct nvme_pt_command pt; 71 72 memset(&pt, 0, sizeof(pt)); 73 pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 74 pt.cmd.nsid = nsid; 75 pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 76 pt.cmd.cdw10 |= log_page; 77 pt.buf = payload; 78 pt.len = payload_size; 79 pt.is_read = 1; 80 81 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 82 err(1, "get log page request failed"); 83 84 if (nvme_completion_is_error(&pt.cpl)) 85 errx(1, "get log page request returned error"); 86} 87 88static void 89print_log_error(void *buf, uint32_t size) 90{ 91 int i, nentries; 92 struct nvme_error_information_entry *entry = buf; 93 struct nvme_status *status; 94 95 printf("Error Information Log\n"); 96 printf("=====================\n"); 97 98 if (entry->error_count == 0) { 99 printf("No error entries found\n"); 100 return; 101 } 102 103 nentries = size/sizeof(struct nvme_error_information_entry); 104 for (i = 0; i < nentries; i++, entry++) { 105 if (entry->error_count == 0) 106 break; 107 108 status = &entry->status; 109 printf("Entry %02d\n", i + 1); 110 printf("=========\n"); 111 printf(" Error count: %ju\n", entry->error_count); 112 printf(" Submission queue ID: %u\n", entry->sqid); 113 printf(" Command ID: %u\n", entry->cid); 114 /* TODO: Export nvme_status_string structures from kernel? */ 115 printf(" Status:\n"); 116 printf(" Phase tag: %d\n", status->p); 117 printf(" Status code: %d\n", status->sc); 118 printf(" Status code type: %d\n", status->sct); 119 printf(" More: %d\n", status->m); 120 printf(" DNR: %d\n", status->dnr); 121 printf(" Error location: %u\n", entry->error_location); 122 printf(" LBA: %ju\n", entry->lba); 123 printf(" Namespace ID: %u\n", entry->nsid); 124 printf(" Vendor specific info: %u\n", entry->vendor_specific); 125 } 126} 127 128static void 129print_log_health(void *buf, uint32_t size __unused) 130{ 131 struct nvme_health_information_page *health = buf; 132 133 printf("SMART/Health Information Log\n"); 134 printf("============================\n"); 135 136 printf("Critical Warning State: 0x%02x\n", 137 health->critical_warning.raw); 138 printf(" Available spare: %d\n", 139 health->critical_warning.bits.available_spare); 140 printf(" Temperature: %d\n", 141 health->critical_warning.bits.temperature); 142 printf(" Device reliability: %d\n", 143 health->critical_warning.bits.device_reliability); 144 printf(" Read only: %d\n", 145 health->critical_warning.bits.read_only); 146 printf(" Volatile memory backup: %d\n", 147 health->critical_warning.bits.volatile_memory_backup); 148 printf("Temperature: %u K, %2.2f C, %3.2f F\n", 149 health->temperature, 150 (float)health->temperature - (float)273.15, 151 ((float)health->temperature * (float)9/5) - (float)459.67); 152 printf("Available spare: %u\n", 153 health->available_spare); 154 printf("Available spare threshold: %u\n", 155 health->available_spare_threshold); 156 printf("Percentage used: %u\n", 157 health->percentage_used); 158 159 /* 160 * TODO: These are pretty ugly in hex. Is there a library that 161 * will convert 128-bit unsigned values to decimal? 162 */ 163 printf("Data units (512 byte) read: 0x%016jx%016jx\n", 164 health->data_units_read[1], 165 health->data_units_read[0]); 166 printf("Data units (512 byte) written: 0x%016jx%016jx\n", 167 health->data_units_written[1], 168 health->data_units_written[0]); 169 printf("Host read commands: 0x%016jx%016jx\n", 170 health->host_read_commands[1], 171 health->host_read_commands[0]); 172 printf("Host write commands: 0x%016jx%016jx\n", 173 health->host_write_commands[1], 174 health->host_write_commands[0]); 175 printf("Controller busy time (minutes): 0x%016jx%016jx\n", 176 health->controller_busy_time[1], 177 health->controller_busy_time[0]); 178 printf("Power cycles: 0x%016jx%016jx\n", 179 health->power_cycles[1], 180 health->power_cycles[0]); 181 printf("Power on hours: 0x%016jx%016jx\n", 182 health->power_on_hours[1], 183 health->power_on_hours[0]); 184 printf("Unsafe shutdowns: 0x%016jx%016jx\n", 185 health->unsafe_shutdowns[1], 186 health->unsafe_shutdowns[0]); 187 printf("Media errors: 0x%016jx%016jx\n", 188 health->media_errors[1], 189 health->media_errors[0]); 190 printf("No. error info log entries: 0x%016jx%016jx\n", 191 health->num_error_info_log_entries[1], 192 health->num_error_info_log_entries[0]); 193} 194 195static void 196print_log_firmware(void *buf, uint32_t size __unused) 197{ 198 int i; 199 const char *status; 200 struct nvme_firmware_page *fw = buf; 201 202 printf("Firmware Slot Log\n"); 203 printf("=================\n"); 204 205 for (i = 0; i < MAX_FW_SLOTS; i++) { 206 printf("Slot %d: ", i + 1); 207 if (fw->afi.slot == i + 1) 208 status = " Active"; 209 else 210 status = "Inactive"; 211 212 if (fw->revision[i] == 0LLU) 213 printf("Empty\n"); 214 else 215 if (isprint(*(char *)&fw->revision[i])) 216 printf("[%s] %.8s\n", status, 217 (char *)&fw->revision[i]); 218 else 219 printf("[%s] %016jx\n", status, 220 fw->revision[i]); 221 } 222} 223 224static struct logpage_function { 225 uint8_t log_page; 226 print_fn_t fn; 227} logfuncs[] = { 228 {NVME_LOG_ERROR, print_log_error }, 229 {NVME_LOG_HEALTH_INFORMATION, print_log_health }, 230 {NVME_LOG_FIRMWARE_SLOT, print_log_firmware }, 231 {0, NULL }, 232}; 233 234static void 235logpage_usage(void) 236{ 237 fprintf(stderr, "usage:\n"); 238 fprintf(stderr, LOGPAGE_USAGE); 239 exit(1); 240} 241 242void 243logpage(int argc, char *argv[]) 244{ 245 int fd, nsid, len; 246 int log_page = 0, pageflag = false; 247 int hexflag = false; 248 int allow_ns = false; 249 char ch, *p, *nsloc = NULL; 250 char *cname = NULL; 251 uint32_t size; 252 void *buf; 253 struct logpage_function *f; 254 struct nvme_controller_data cdata; 255 print_fn_t print_fn; 256 257 while ((ch = getopt(argc, argv, "p:x")) != -1) { 258 switch (ch) { 259 case 'p': 260 /* TODO: Add human-readable ASCII page IDs */ 261 log_page = strtol(optarg, &p, 0); 262 if (p != NULL && *p != '\0') { 263 fprintf(stderr, 264 "\"%s\" not valid log page id.\n", 265 optarg); 266 logpage_usage(); 267 /* TODO: Define valid log page id ranges in nvme.h? */ 268 } else if (log_page == 0 || 269 (log_page >= 0x04 && log_page <= 0x7F) || 270 (log_page >= 0x80 && log_page <= 0xBF)) { 271 fprintf(stderr, 272 "\"%s\" not valid log page id.\n", 273 optarg); 274 logpage_usage(); 275 } 276 pageflag = true; 277 break; 278 case 'x': 279 hexflag = true; 280 break; 281 } 282 } 283 284 if (!pageflag) { 285 printf("Missing page_id (-p).\n"); 286 logpage_usage(); 287 } 288 289 /* Check that a controller and/or namespace was specified. */ 290 if (optind >= argc) 291 logpage_usage(); 292 293 /* 294 * The log page attribtues indicate whether or not the controller 295 * supports the SMART/Health information log page on a per 296 * namespace basis. 297 */ 298 cname = malloc(strlen(NVME_CTRLR_PREFIX) + 2); 299 len = strlen(NVME_CTRLR_PREFIX) + 1; 300 cname = strncpy(cname, argv[optind], len); 301 open_dev(cname, &fd, 1, 1); 302 read_controller_data(fd, &cdata); 303 304 if (log_page == NVME_LOG_HEALTH_INFORMATION && cdata.lpa.ns_smart != 0) 305 allow_ns = true; 306 307 /* If a namespace id was specified, validate it's use */ 308 if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 309 if (!allow_ns) { 310 if (log_page != NVME_LOG_HEALTH_INFORMATION) 311 errx(1, 312 "log page %d valid only at controller level", 313 log_page); 314 else if (cdata.lpa.ns_smart == 0) 315 errx(1, 316 "controller does not support per " 317 "namespace smart/health information"); 318 } 319 nsloc = strnstr(argv[optind], NVME_NS_PREFIX, 10); 320 if (nsloc != NULL) 321 nsid = strtol(nsloc + 2, NULL, 10); 322 if (nsloc == NULL || (nsid == 0 && errno != 0)) 323 errx(1, "invalid namespace id '%s'", argv[optind]); 324 325 /* 326 * User is asking for per namespace log page information 327 * so close the controller and open up the namespace. 328 */ 329 close(fd); 330 open_dev(argv[optind], &fd, 1, 1); 331 } else 332 nsid = NVME_GLOBAL_NAMESPACE_TAG; 333 334 print_fn = print_hex; 335 if (!hexflag) { 336 /* 337 * See if there is a pretty print function for the 338 * specified log page. If one isn't found, we 339 * just revert to the default (print_hex). 340 */ 341 f = logfuncs; 342 while (f->log_page > 0) { 343 if (log_page == f->log_page) { 344 print_fn = f->fn; 345 break; 346 } 347 f++; 348 } 349 } 350 351 /* Read the log page */ 352 switch (log_page) { 353 case NVME_LOG_ERROR: 354 size = sizeof(struct nvme_error_information_entry); 355 size *= (cdata.elpe + 1); 356 break; 357 case NVME_LOG_HEALTH_INFORMATION: 358 size = sizeof(struct nvme_health_information_page); 359 break; 360 case NVME_LOG_FIRMWARE_SLOT: 361 size = sizeof(struct nvme_firmware_page); 362 break; 363 default: 364 size = DEFAULT_SIZE; 365 break; 366 } 367 368 buf = get_log_buffer(size); 369 read_logpage(fd, log_page, nsid, buf, size); 370 print_fn(buf, size); 371 372 close(fd); 373 exit(0); 374} 375