logpage.c revision 328672
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: stable/11/sbin/nvmecontrol/logpage.c 328672 2018-02-01 16:20:44Z mav $"); 32 33#include <sys/param.h> 34#include <sys/ioccom.h> 35 36#include <ctype.h> 37#include <err.h> 38#include <fcntl.h> 39#include <stdbool.h> 40#include <stddef.h> 41#include <stdio.h> 42#include <stdlib.h> 43#include <string.h> 44#include <unistd.h> 45#include <sys/endian.h> 46 47#if _BYTE_ORDER != _LITTLE_ENDIAN 48#error "Code only works on little endian machines" 49#endif 50 51#include "nvmecontrol.h" 52 53#define DEFAULT_SIZE (4096) 54#define MAX_FW_SLOTS (7) 55 56typedef void (*print_fn_t)(void *buf, uint32_t size); 57 58 59/* 60 * 128-bit integer augments to standard values 61 */ 62#define UINT128_DIG 39 63typedef __uint128_t uint128_t; 64 65static inline uint128_t 66to128(void *p) 67{ 68 return *(uint128_t *)p; 69} 70 71static char * 72uint128_to_str(uint128_t u, char *buf, size_t buflen) 73{ 74 char *end = buf + buflen - 1; 75 76 *end-- = '\0'; 77 if (u == 0) 78 *end-- = '0'; 79 while (u && end >= buf) { 80 *end-- = u % 10 + '0'; 81 u /= 10; 82 } 83 end++; 84 if (u != 0) 85 return NULL; 86 87 return end; 88} 89 90static void * 91get_log_buffer(uint32_t size) 92{ 93 void *buf; 94 95 if ((buf = malloc(size)) == NULL) 96 errx(1, "unable to malloc %u bytes", size); 97 98 memset(buf, 0, size); 99 return (buf); 100} 101 102void 103read_logpage(int fd, uint8_t log_page, int nsid, void *payload, 104 uint32_t payload_size) 105{ 106 struct nvme_pt_command pt; 107 108 memset(&pt, 0, sizeof(pt)); 109 pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 110 pt.cmd.nsid = nsid; 111 pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 112 pt.cmd.cdw10 |= log_page; 113 pt.buf = payload; 114 pt.len = payload_size; 115 pt.is_read = 1; 116 117 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 118 err(1, "get log page request failed"); 119 120 if (nvme_completion_is_error(&pt.cpl)) 121 errx(1, "get log page request returned error"); 122} 123 124static void 125print_log_error(void *buf, uint32_t size) 126{ 127 int i, nentries; 128 struct nvme_error_information_entry *entry = buf; 129 struct nvme_status *status; 130 131 printf("Error Information Log\n"); 132 printf("=====================\n"); 133 134 if (entry->error_count == 0) { 135 printf("No error entries found\n"); 136 return; 137 } 138 139 nentries = size/sizeof(struct nvme_error_information_entry); 140 for (i = 0; i < nentries; i++, entry++) { 141 if (entry->error_count == 0) 142 break; 143 144 status = &entry->status; 145 printf("Entry %02d\n", i + 1); 146 printf("=========\n"); 147 printf(" Error count: %ju\n", entry->error_count); 148 printf(" Submission queue ID: %u\n", entry->sqid); 149 printf(" Command ID: %u\n", entry->cid); 150 /* TODO: Export nvme_status_string structures from kernel? */ 151 printf(" Status:\n"); 152 printf(" Phase tag: %d\n", status->p); 153 printf(" Status code: %d\n", status->sc); 154 printf(" Status code type: %d\n", status->sct); 155 printf(" More: %d\n", status->m); 156 printf(" DNR: %d\n", status->dnr); 157 printf(" Error location: %u\n", entry->error_location); 158 printf(" LBA: %ju\n", entry->lba); 159 printf(" Namespace ID: %u\n", entry->nsid); 160 printf(" Vendor specific info: %u\n", entry->vendor_specific); 161 } 162} 163 164static void 165print_temp(uint16_t t) 166{ 167 printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 168} 169 170 171static void 172print_log_health(void *buf, uint32_t size __unused) 173{ 174 struct nvme_health_information_page *health = buf; 175 char cbuf[UINT128_DIG + 1]; 176 int i; 177 178 printf("SMART/Health Information Log\n"); 179 printf("============================\n"); 180 181 printf("Critical Warning State: 0x%02x\n", 182 health->critical_warning.raw); 183 printf(" Available spare: %d\n", 184 health->critical_warning.bits.available_spare); 185 printf(" Temperature: %d\n", 186 health->critical_warning.bits.temperature); 187 printf(" Device reliability: %d\n", 188 health->critical_warning.bits.device_reliability); 189 printf(" Read only: %d\n", 190 health->critical_warning.bits.read_only); 191 printf(" Volatile memory backup: %d\n", 192 health->critical_warning.bits.volatile_memory_backup); 193 printf("Temperature: "); 194 print_temp(health->temperature); 195 printf("Available spare: %u\n", 196 health->available_spare); 197 printf("Available spare threshold: %u\n", 198 health->available_spare_threshold); 199 printf("Percentage used: %u\n", 200 health->percentage_used); 201 202 printf("Data units (512,000 byte) read: %s\n", 203 uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); 204 printf("Data units written: %s\n", 205 uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); 206 printf("Host read commands: %s\n", 207 uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); 208 printf("Host write commands: %s\n", 209 uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); 210 printf("Controller busy time (minutes): %s\n", 211 uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); 212 printf("Power cycles: %s\n", 213 uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); 214 printf("Power on hours: %s\n", 215 uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); 216 printf("Unsafe shutdowns: %s\n", 217 uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); 218 printf("Media errors: %s\n", 219 uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); 220 printf("No. error info log entries: %s\n", 221 uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); 222 223 printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 224 printf("Error Temp Composite Time: %d\n", health->error_temp_time); 225 for (i = 0; i < 7; i++) { 226 if (health->temp_sensor[i] == 0) 227 continue; 228 printf("Temperature Sensor %d: ", i + 1); 229 print_temp(health->temp_sensor[i]); 230 } 231} 232 233static void 234print_log_firmware(void *buf, uint32_t size __unused) 235{ 236 int i; 237 const char *status; 238 struct nvme_firmware_page *fw = buf; 239 240 printf("Firmware Slot Log\n"); 241 printf("=================\n"); 242 243 for (i = 0; i < MAX_FW_SLOTS; i++) { 244 printf("Slot %d: ", i + 1); 245 if (fw->afi.slot == i + 1) 246 status = " Active"; 247 else 248 status = "Inactive"; 249 250 if (fw->revision[i] == 0LLU) 251 printf("Empty\n"); 252 else 253 if (isprint(*(char *)&fw->revision[i])) 254 printf("[%s] %.8s\n", status, 255 (char *)&fw->revision[i]); 256 else 257 printf("[%s] %016jx\n", status, 258 fw->revision[i]); 259 } 260} 261 262static struct logpage_function { 263 uint8_t log_page; 264 print_fn_t print_fn; 265 size_t size; 266} logfuncs[] = { 267 {NVME_LOG_ERROR, print_log_error, 268 0}, 269 {NVME_LOG_HEALTH_INFORMATION, print_log_health, 270 sizeof(struct nvme_health_information_page)}, 271 {NVME_LOG_FIRMWARE_SLOT, print_log_firmware, 272 sizeof(struct nvme_firmware_page)}, 273 {0, NULL, 274 0}, 275}; 276 277static void 278logpage_usage(void) 279{ 280 fprintf(stderr, "usage:\n"); 281 fprintf(stderr, LOGPAGE_USAGE); 282 exit(1); 283} 284 285void 286logpage(int argc, char *argv[]) 287{ 288 int fd, nsid; 289 int log_page = 0, pageflag = false; 290 int hexflag = false, ns_specified; 291 char ch, *p; 292 char cname[64]; 293 uint32_t size; 294 void *buf; 295 struct logpage_function *f; 296 struct nvme_controller_data cdata; 297 print_fn_t print_fn; 298 299 while ((ch = getopt(argc, argv, "p:x")) != -1) { 300 switch (ch) { 301 case 'p': 302 /* TODO: Add human-readable ASCII page IDs */ 303 log_page = strtol(optarg, &p, 0); 304 if (p != NULL && *p != '\0') { 305 fprintf(stderr, 306 "\"%s\" not valid log page id.\n", 307 optarg); 308 logpage_usage(); 309 /* TODO: Define valid log page id ranges in nvme.h? */ 310 } else if (log_page == 0 || 311 (log_page >= 0x04 && log_page <= 0x7F) || 312 (log_page >= 0x80 && log_page <= 0xBF)) { 313 fprintf(stderr, 314 "\"%s\" not valid log page id.\n", 315 optarg); 316 logpage_usage(); 317 } 318 pageflag = true; 319 break; 320 case 'x': 321 hexflag = true; 322 break; 323 } 324 } 325 326 if (!pageflag) { 327 printf("Missing page_id (-p).\n"); 328 logpage_usage(); 329 } 330 331 /* Check that a controller and/or namespace was specified. */ 332 if (optind >= argc) 333 logpage_usage(); 334 335 if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 336 ns_specified = true; 337 parse_ns_str(argv[optind], cname, &nsid); 338 open_dev(cname, &fd, 1, 1); 339 } else { 340 ns_specified = false; 341 nsid = NVME_GLOBAL_NAMESPACE_TAG; 342 open_dev(argv[optind], &fd, 1, 1); 343 } 344 345 read_controller_data(fd, &cdata); 346 347 /* 348 * The log page attribtues indicate whether or not the controller 349 * supports the SMART/Health information log page on a per 350 * namespace basis. 351 */ 352 if (ns_specified) { 353 if (log_page != NVME_LOG_HEALTH_INFORMATION) 354 errx(1, "log page %d valid only at controller level", 355 log_page); 356 if (cdata.lpa.ns_smart == 0) 357 errx(1, 358 "controller does not support per namespace " 359 "smart/health information"); 360 } 361 362 print_fn = print_hex; 363 size = DEFAULT_SIZE; 364 if (!hexflag) { 365 /* 366 * See if there is a pretty print function for the 367 * specified log page. If one isn't found, we 368 * just revert to the default (print_hex). 369 */ 370 f = logfuncs; 371 while (f->log_page > 0) { 372 if (log_page == f->log_page) { 373 print_fn = f->print_fn; 374 size = f->size; 375 break; 376 } 377 f++; 378 } 379 } 380 381 if (log_page == NVME_LOG_ERROR) { 382 size = sizeof(struct nvme_error_information_entry); 383 size *= (cdata.elpe + 1); 384 } 385 386 /* Read the log page */ 387 buf = get_log_buffer(size); 388 read_logpage(fd, log_page, nsid, buf, size); 389 print_fn(buf, size); 390 391 close(fd); 392 exit(0); 393} 394