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