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