logpage.c revision 328748
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: stable/11/sbin/nvmecontrol/logpage.c 328748 2018-02-01 21:14:54Z mav $"); 32252277Sjimharris 33252277Sjimharris#include <sys/param.h> 34252277Sjimharris#include <sys/ioccom.h> 35252277Sjimharris 36252277Sjimharris#include <ctype.h> 37253109Sjimharris#include <err.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 <unistd.h> 45328668Smav#include <sys/endian.h> 46252277Sjimharris 47328668Smav#if _BYTE_ORDER != _LITTLE_ENDIAN 48328668Smav#error "Code only works on little endian machines" 49328668Smav#endif 50328668Smav 51252277Sjimharris#include "nvmecontrol.h" 52252277Sjimharris 53252277Sjimharris#define DEFAULT_SIZE (4096) 54252277Sjimharris#define MAX_FW_SLOTS (7) 55252277Sjimharris 56328741Smavtypedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size); 57252277Sjimharris 58328674Smavstruct kv_name 59328674Smav{ 60328674Smav uint32_t key; 61328674Smav const char *name; 62328674Smav}; 63328674Smav 64328674Smavstatic const char * 65328674Smavkv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) 66328674Smav{ 67328674Smav static char bad[32]; 68328674Smav size_t i; 69328674Smav 70328674Smav for (i = 0; i < kv_count; i++, kv++) 71328674Smav if (kv->key == key) 72328674Smav return kv->name; 73328674Smav snprintf(bad, sizeof(bad), "Attribute %#x", key); 74328674Smav return bad; 75328674Smav} 76328674Smav 77328721Smavstatic void 78328741Smavprint_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 79328721Smav{ 80328741Smav 81328741Smav print_hex(data, length); 82328741Smav} 83328741Smav 84328741Smavstatic void 85328741Smavprint_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 86328741Smav{ 87328741Smav 88328721Smav write(STDOUT_FILENO, data, length); 89328721Smav} 90328721Smav 91252277Sjimharrisstatic void * 92253109Sjimharrisget_log_buffer(uint32_t size) 93252277Sjimharris{ 94252277Sjimharris void *buf; 95252277Sjimharris 96253109Sjimharris if ((buf = malloc(size)) == NULL) 97253109Sjimharris errx(1, "unable to malloc %u bytes", size); 98253109Sjimharris 99252277Sjimharris memset(buf, 0, size); 100252277Sjimharris return (buf); 101252277Sjimharris} 102252277Sjimharris 103252277Sjimharrisvoid 104328673Smavread_logpage(int fd, uint8_t log_page, int nsid, void *payload, 105252277Sjimharris uint32_t payload_size) 106252277Sjimharris{ 107252277Sjimharris struct nvme_pt_command pt; 108252277Sjimharris 109252277Sjimharris memset(&pt, 0, sizeof(pt)); 110252277Sjimharris pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 111252277Sjimharris pt.cmd.nsid = nsid; 112252277Sjimharris pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 113252277Sjimharris pt.cmd.cdw10 |= log_page; 114252277Sjimharris pt.buf = payload; 115252277Sjimharris pt.len = payload_size; 116252277Sjimharris pt.is_read = 1; 117252277Sjimharris 118253109Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 119253109Sjimharris err(1, "get log page request failed"); 120252277Sjimharris 121253109Sjimharris if (nvme_completion_is_error(&pt.cpl)) 122253109Sjimharris errx(1, "get log page request returned error"); 123252277Sjimharris} 124252277Sjimharris 125252277Sjimharrisstatic void 126328741Smavprint_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 127252277Sjimharris{ 128252277Sjimharris int i, nentries; 129252277Sjimharris struct nvme_error_information_entry *entry = buf; 130252277Sjimharris struct nvme_status *status; 131252277Sjimharris 132252277Sjimharris printf("Error Information Log\n"); 133252277Sjimharris printf("=====================\n"); 134252277Sjimharris 135252277Sjimharris if (entry->error_count == 0) { 136252277Sjimharris printf("No error entries found\n"); 137252277Sjimharris return; 138252277Sjimharris } 139252277Sjimharris 140252277Sjimharris nentries = size/sizeof(struct nvme_error_information_entry); 141252277Sjimharris for (i = 0; i < nentries; i++, entry++) { 142252277Sjimharris if (entry->error_count == 0) 143252277Sjimharris break; 144252277Sjimharris 145252277Sjimharris status = &entry->status; 146252277Sjimharris printf("Entry %02d\n", i + 1); 147252277Sjimharris printf("=========\n"); 148252277Sjimharris printf(" Error count: %ju\n", entry->error_count); 149252277Sjimharris printf(" Submission queue ID: %u\n", entry->sqid); 150252277Sjimharris printf(" Command ID: %u\n", entry->cid); 151252277Sjimharris /* TODO: Export nvme_status_string structures from kernel? */ 152252277Sjimharris printf(" Status:\n"); 153252277Sjimharris printf(" Phase tag: %d\n", status->p); 154252277Sjimharris printf(" Status code: %d\n", status->sc); 155252277Sjimharris printf(" Status code type: %d\n", status->sct); 156252277Sjimharris printf(" More: %d\n", status->m); 157252277Sjimharris printf(" DNR: %d\n", status->dnr); 158252277Sjimharris printf(" Error location: %u\n", entry->error_location); 159252277Sjimharris printf(" LBA: %ju\n", entry->lba); 160252277Sjimharris printf(" Namespace ID: %u\n", entry->nsid); 161252277Sjimharris printf(" Vendor specific info: %u\n", entry->vendor_specific); 162252277Sjimharris } 163252277Sjimharris} 164252277Sjimharris 165252277Sjimharrisstatic void 166328669Smavprint_temp(uint16_t t) 167328669Smav{ 168328669Smav printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 169328669Smav} 170328669Smav 171328669Smav 172328669Smavstatic void 173328741Smavprint_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 174252277Sjimharris{ 175252277Sjimharris struct nvme_health_information_page *health = buf; 176328668Smav char cbuf[UINT128_DIG + 1]; 177328669Smav int i; 178252277Sjimharris 179252277Sjimharris printf("SMART/Health Information Log\n"); 180252277Sjimharris printf("============================\n"); 181252277Sjimharris 182252277Sjimharris printf("Critical Warning State: 0x%02x\n", 183252277Sjimharris health->critical_warning.raw); 184252277Sjimharris printf(" Available spare: %d\n", 185252277Sjimharris health->critical_warning.bits.available_spare); 186252277Sjimharris printf(" Temperature: %d\n", 187252277Sjimharris health->critical_warning.bits.temperature); 188252277Sjimharris printf(" Device reliability: %d\n", 189252277Sjimharris health->critical_warning.bits.device_reliability); 190252277Sjimharris printf(" Read only: %d\n", 191252277Sjimharris health->critical_warning.bits.read_only); 192252277Sjimharris printf(" Volatile memory backup: %d\n", 193252277Sjimharris health->critical_warning.bits.volatile_memory_backup); 194328669Smav printf("Temperature: "); 195328669Smav print_temp(health->temperature); 196252277Sjimharris printf("Available spare: %u\n", 197252277Sjimharris health->available_spare); 198252277Sjimharris printf("Available spare threshold: %u\n", 199252277Sjimharris health->available_spare_threshold); 200252277Sjimharris printf("Percentage used: %u\n", 201252277Sjimharris health->percentage_used); 202252277Sjimharris 203328669Smav printf("Data units (512,000 byte) read: %s\n", 204328668Smav uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); 205328669Smav printf("Data units written: %s\n", 206328668Smav uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); 207328668Smav printf("Host read commands: %s\n", 208328668Smav uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); 209328668Smav printf("Host write commands: %s\n", 210328668Smav uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); 211328668Smav printf("Controller busy time (minutes): %s\n", 212328668Smav uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); 213328668Smav printf("Power cycles: %s\n", 214328668Smav uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); 215328668Smav printf("Power on hours: %s\n", 216328668Smav uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); 217328668Smav printf("Unsafe shutdowns: %s\n", 218328668Smav uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); 219328668Smav printf("Media errors: %s\n", 220328668Smav uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); 221328668Smav printf("No. error info log entries: %s\n", 222328668Smav uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); 223328669Smav 224328669Smav printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 225328669Smav printf("Error Temp Composite Time: %d\n", health->error_temp_time); 226328669Smav for (i = 0; i < 7; i++) { 227328669Smav if (health->temp_sensor[i] == 0) 228328669Smav continue; 229328669Smav printf("Temperature Sensor %d: ", i + 1); 230328669Smav print_temp(health->temp_sensor[i]); 231328669Smav } 232252277Sjimharris} 233252277Sjimharris 234252277Sjimharrisstatic void 235328741Smavprint_log_firmware(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 236252277Sjimharris{ 237328748Smav int i, slots; 238252277Sjimharris const char *status; 239252277Sjimharris struct nvme_firmware_page *fw = buf; 240252277Sjimharris 241252277Sjimharris printf("Firmware Slot Log\n"); 242252277Sjimharris printf("=================\n"); 243252277Sjimharris 244328748Smav if (cdata->oacs.firmware == 0) 245328748Smav slots = 1; 246328748Smav else 247328748Smav slots = MIN(cdata->frmw.num_slots, MAX_FW_SLOTS); 248328748Smav 249328748Smav for (i = 0; i < slots; i++) { 250252277Sjimharris printf("Slot %d: ", i + 1); 251252277Sjimharris if (fw->afi.slot == i + 1) 252252277Sjimharris status = " Active"; 253252277Sjimharris else 254252277Sjimharris status = "Inactive"; 255252277Sjimharris 256252277Sjimharris if (fw->revision[i] == 0LLU) 257252277Sjimharris printf("Empty\n"); 258252277Sjimharris else 259252277Sjimharris if (isprint(*(char *)&fw->revision[i])) 260252277Sjimharris printf("[%s] %.8s\n", status, 261252277Sjimharris (char *)&fw->revision[i]); 262252277Sjimharris else 263252277Sjimharris printf("[%s] %016jx\n", status, 264252277Sjimharris fw->revision[i]); 265252277Sjimharris } 266252277Sjimharris} 267252277Sjimharris 268328708Smav/* 269328708Smav * Intel specific log pages from 270328708Smav * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf 271328708Smav * 272328708Smav * Though the version as of this date has a typo for the size of log page 0xca, 273328708Smav * offset 147: it is only 1 byte, not 6. 274328708Smav */ 275328673Smavstatic void 276328741Smavprint_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 277328673Smav{ 278328673Smav struct intel_log_temp_stats *temp = buf; 279328673Smav 280328673Smav printf("Intel Temperature Log\n"); 281328673Smav printf("=====================\n"); 282328673Smav 283328673Smav printf("Current: "); 284328673Smav print_temp(temp->current); 285328673Smav printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); 286328673Smav printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); 287328673Smav printf("Max Temperature "); 288328673Smav print_temp(temp->max_temp); 289328673Smav printf("Min Temperature "); 290328673Smav print_temp(temp->min_temp); 291328673Smav printf("Max Operating Temperature "); 292328673Smav print_temp(temp->max_oper_temp); 293328673Smav printf("Min Operating Temperature "); 294328673Smav print_temp(temp->min_oper_temp); 295328673Smav printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); 296328673Smav} 297328673Smav 298328712Smav/* 299328712Smav * Format from Table 22, section 5.7 IO Command Latency Statistics. 300328712Smav * Read and write stats pages have identical encoding. 301328712Smav */ 302328708Smavstatic void 303328741Smavprint_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 304328712Smav{ 305328712Smav const char *walker = buf; 306328712Smav int i; 307328712Smav 308328712Smav printf("Major: %d\n", le16dec(walker + 0)); 309328712Smav printf("Minor: %d\n", le16dec(walker + 2)); 310328712Smav for (i = 0; i < 32; i++) 311328712Smav printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4)); 312328712Smav for (i = 1; i < 32; i++) 313328712Smav printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4)); 314328712Smav for (i = 1; i < 32; i++) 315328712Smav printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4)); 316328712Smav} 317328712Smav 318328712Smavstatic void 319328741Smavprint_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 320328712Smav{ 321328712Smav 322328712Smav printf("Intel Read Latency Log\n"); 323328712Smav printf("======================\n"); 324328741Smav print_intel_read_write_lat_log(cdata, buf, size); 325328712Smav} 326328712Smav 327328712Smavstatic void 328328741Smavprint_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 329328712Smav{ 330328712Smav 331328712Smav printf("Intel Write Latency Log\n"); 332328712Smav printf("=======================\n"); 333328741Smav print_intel_read_write_lat_log(cdata, buf, size); 334328712Smav} 335328712Smav 336328712Smav/* 337328725Smav * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented. 338328712Smav */ 339328712Smavstatic void 340328741Smavprint_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 341328708Smav{ 342328708Smav uint8_t *walker = buf; 343328708Smav uint8_t *end = walker + 150; 344328708Smav const char *name; 345328708Smav uint64_t raw; 346328708Smav uint8_t normalized; 347328708Smav 348328708Smav static struct kv_name kv[] = 349328708Smav { 350328708Smav { 0xab, "Program Fail Count" }, 351328708Smav { 0xac, "Erase Fail Count" }, 352328708Smav { 0xad, "Wear Leveling Count" }, 353328708Smav { 0xb8, "End to End Error Count" }, 354328708Smav { 0xc7, "CRC Error Count" }, 355328708Smav { 0xe2, "Timed: Media Wear" }, 356328708Smav { 0xe3, "Timed: Host Read %" }, 357328708Smav { 0xe4, "Timed: Elapsed Time" }, 358328708Smav { 0xea, "Thermal Throttle Status" }, 359328708Smav { 0xf0, "Retry Buffer Overflows" }, 360328708Smav { 0xf3, "PLL Lock Loss Count" }, 361328708Smav { 0xf4, "NAND Bytes Written" }, 362328708Smav { 0xf5, "Host Bytes Written" }, 363328708Smav }; 364328708Smav 365328708Smav printf("Additional SMART Data Log\n"); 366328708Smav printf("=========================\n"); 367328708Smav /* 368328708Smav * walker[0] = Key 369328708Smav * walker[1,2] = reserved 370328708Smav * walker[3] = Normalized Value 371328708Smav * walker[4] = reserved 372328708Smav * walker[5..10] = Little Endian Raw value 373328708Smav * (or other represenations) 374328708Smav * walker[11] = reserved 375328708Smav */ 376328708Smav while (walker < end) { 377328708Smav name = kv_lookup(kv, nitems(kv), *walker); 378328708Smav normalized = walker[3]; 379328708Smav raw = le48dec(walker + 5); 380328708Smav switch (*walker){ 381328708Smav case 0: 382328708Smav break; 383328708Smav case 0xad: 384328708Smav printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized, 385328708Smav le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); 386328708Smav break; 387328708Smav case 0xe2: 388328708Smav printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); 389328708Smav break; 390328708Smav case 0xea: 391328708Smav printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); 392328708Smav break; 393328708Smav default: 394328708Smav printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); 395328708Smav break; 396328708Smav } 397328708Smav walker += 12; 398328708Smav } 399328708Smav} 400328708Smav 401328673Smav/* 402328674Smav * HGST's 0xc1 page. This is a grab bag of additional data. Please see 403328674Smav * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf 404328674Smav * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf 405328674Smav * Appendix A for details 406328674Smav */ 407328674Smav 408328674Smavtypedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 409328674Smav 410328674Smavstruct subpage_print 411328674Smav{ 412328674Smav uint16_t key; 413328674Smav subprint_fn_t fn; 414328674Smav}; 415328674Smav 416328674Smavstatic void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 417328674Smavstatic void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 418328674Smavstatic void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 419328674Smavstatic void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 420328674Smavstatic void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 421328674Smavstatic void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 422328674Smavstatic void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 423328674Smavstatic void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 424328674Smavstatic void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 425328674Smavstatic void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 426328674Smav 427328674Smavstatic struct subpage_print hgst_subpage[] = { 428328674Smav { 0x02, print_hgst_info_write_errors }, 429328674Smav { 0x03, print_hgst_info_read_errors }, 430328674Smav { 0x05, print_hgst_info_verify_errors }, 431328674Smav { 0x10, print_hgst_info_self_test }, 432328674Smav { 0x15, print_hgst_info_background_scan }, 433328674Smav { 0x30, print_hgst_info_erase_errors }, 434328674Smav { 0x31, print_hgst_info_erase_counts }, 435328674Smav { 0x32, print_hgst_info_temp_history }, 436328674Smav { 0x37, print_hgst_info_ssd_perf }, 437328674Smav { 0x38, print_hgst_info_firmware_load }, 438328674Smav}; 439328674Smav 440328674Smav/* Print a subpage that is basically just key value pairs */ 441328674Smavstatic void 442328674Smavprint_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, 443328674Smav const struct kv_name *kv, size_t kv_count) 444328674Smav{ 445328674Smav uint8_t *wsp, *esp; 446328674Smav uint16_t ptype; 447328674Smav uint8_t plen; 448328674Smav uint64_t param; 449328674Smav int i; 450328674Smav 451328674Smav wsp = buf; 452328674Smav esp = wsp + size; 453328674Smav while (wsp < esp) { 454328674Smav ptype = le16dec(wsp); 455328674Smav wsp += 2; 456328674Smav wsp++; /* Flags, just ignore */ 457328674Smav plen = *wsp++; 458328674Smav param = 0; 459328674Smav for (i = 0; i < plen; i++) 460328674Smav param |= (uint64_t)*wsp++ << (i * 8); 461328674Smav printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); 462328674Smav } 463328674Smav} 464328674Smav 465328674Smavstatic void 466328674Smavprint_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 467328674Smav{ 468328674Smav static struct kv_name kv[] = 469328674Smav { 470328674Smav { 0x0000, "Corrected Without Delay" }, 471328674Smav { 0x0001, "Corrected Maybe Delayed" }, 472328674Smav { 0x0002, "Re-Writes" }, 473328674Smav { 0x0003, "Errors Corrected" }, 474328674Smav { 0x0004, "Correct Algorithm Used" }, 475328674Smav { 0x0005, "Bytes Processed" }, 476328674Smav { 0x0006, "Uncorrected Errors" }, 477328674Smav { 0x8000, "Flash Write Commands" }, 478328674Smav { 0x8001, "HGST Special" }, 479328674Smav }; 480328674Smav 481328674Smav printf("Write Errors Subpage:\n"); 482328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 483328674Smav} 484328674Smav 485328674Smavstatic void 486328674Smavprint_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 487328674Smav{ 488328674Smav static struct kv_name kv[] = 489328674Smav { 490328674Smav { 0x0000, "Corrected Without Delay" }, 491328674Smav { 0x0001, "Corrected Maybe Delayed" }, 492328674Smav { 0x0002, "Re-Reads" }, 493328674Smav { 0x0003, "Errors Corrected" }, 494328674Smav { 0x0004, "Correct Algorithm Used" }, 495328674Smav { 0x0005, "Bytes Processed" }, 496328674Smav { 0x0006, "Uncorrected Errors" }, 497328674Smav { 0x8000, "Flash Read Commands" }, 498328674Smav { 0x8001, "XOR Recovered" }, 499328674Smav { 0x8002, "Total Corrected Bits" }, 500328674Smav }; 501328674Smav 502328674Smav printf("Read Errors Subpage:\n"); 503328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 504328674Smav} 505328674Smav 506328674Smavstatic void 507328674Smavprint_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 508328674Smav{ 509328674Smav static struct kv_name kv[] = 510328674Smav { 511328674Smav { 0x0000, "Corrected Without Delay" }, 512328674Smav { 0x0001, "Corrected Maybe Delayed" }, 513328674Smav { 0x0002, "Re-Reads" }, 514328674Smav { 0x0003, "Errors Corrected" }, 515328674Smav { 0x0004, "Correct Algorithm Used" }, 516328674Smav { 0x0005, "Bytes Processed" }, 517328674Smav { 0x0006, "Uncorrected Errors" }, 518328674Smav { 0x8000, "Commands Processed" }, 519328674Smav }; 520328674Smav 521328674Smav printf("Verify Errors Subpage:\n"); 522328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 523328674Smav} 524328674Smav 525328674Smavstatic void 526328674Smavprint_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 527328674Smav{ 528328674Smav size_t i; 529328674Smav uint8_t *walker = buf; 530328674Smav uint16_t code, hrs; 531328674Smav uint32_t lba; 532328674Smav 533328674Smav printf("Self Test Subpage:\n"); 534328674Smav for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ 535328674Smav code = le16dec(walker); 536328674Smav walker += 2; 537328674Smav walker++; /* Ignore fixed flags */ 538328674Smav if (*walker == 0) /* Last entry is zero length */ 539328674Smav break; 540328674Smav if (*walker++ != 0x10) { 541328674Smav printf("Bad length for self test report\n"); 542328674Smav return; 543328674Smav } 544328674Smav printf(" %-30s: %d\n", "Recent Test", code); 545328674Smav printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); 546328674Smav printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); 547328674Smav walker++; 548328674Smav printf(" %-28s: %#x\n", "Self-Test Number", *walker++); 549328674Smav hrs = le16dec(walker); 550328674Smav walker += 2; 551328674Smav lba = le32dec(walker); 552328674Smav walker += 4; 553328674Smav printf(" %-28s: %u\n", "Total Power On Hrs", hrs); 554328674Smav printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); 555328674Smav printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); 556328674Smav printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); 557328674Smav printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); 558328674Smav printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); 559328674Smav } 560328674Smav} 561328674Smav 562328674Smavstatic void 563328674Smavprint_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 564328674Smav{ 565328674Smav uint8_t *walker = buf; 566328674Smav uint8_t status; 567328674Smav uint16_t code, nscan, progress; 568328674Smav uint32_t pom, nand; 569328674Smav 570328674Smav printf("Background Media Scan Subpage:\n"); 571328674Smav /* Decode the header */ 572328674Smav code = le16dec(walker); 573328674Smav walker += 2; 574328674Smav walker++; /* Ignore fixed flags */ 575328674Smav if (*walker++ != 0x10) { 576328674Smav printf("Bad length for background scan header\n"); 577328674Smav return; 578328674Smav } 579328674Smav if (code != 0) { 580328674Smav printf("Expceted code 0, found code %#x\n", code); 581328674Smav return; 582328674Smav } 583328674Smav pom = le32dec(walker); 584328674Smav walker += 4; 585328674Smav walker++; /* Reserved */ 586328674Smav status = *walker++; 587328674Smav nscan = le16dec(walker); 588328674Smav walker += 2; 589328674Smav progress = le16dec(walker); 590328674Smav walker += 2; 591328674Smav walker += 6; /* Reserved */ 592328674Smav printf(" %-30s: %d\n", "Power On Minutes", pom); 593328674Smav printf(" %-30s: %x (%s)\n", "BMS Status", status, 594328674Smav status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); 595328674Smav printf(" %-30s: %d\n", "Number of BMS", nscan); 596328674Smav printf(" %-30s: %d\n", "Progress Current BMS", progress); 597328674Smav /* Report retirements */ 598328674Smav if (walker - (uint8_t *)buf != 20) { 599328674Smav printf("Coding error, offset not 20\n"); 600328674Smav return; 601328674Smav } 602328674Smav size -= 20; 603328674Smav printf(" %-30s: %d\n", "BMS retirements", size / 0x18); 604328674Smav while (size > 0) { 605328674Smav code = le16dec(walker); 606328674Smav walker += 2; 607328674Smav walker++; 608328674Smav if (*walker++ != 0x14) { 609328674Smav printf("Bad length parameter\n"); 610328674Smav return; 611328674Smav } 612328674Smav pom = le32dec(walker); 613328674Smav walker += 4; 614328674Smav /* 615328674Smav * Spec sheet says the following are hard coded, if true, just 616328674Smav * print the NAND retirement. 617328674Smav */ 618328674Smav if (walker[0] == 0x41 && 619328674Smav walker[1] == 0x0b && 620328674Smav walker[2] == 0x01 && 621328674Smav walker[3] == 0x00 && 622328674Smav walker[4] == 0x00 && 623328674Smav walker[5] == 0x00 && 624328674Smav walker[6] == 0x00 && 625328674Smav walker[7] == 0x00) { 626328674Smav walker += 8; 627328674Smav walker += 4; /* Skip reserved */ 628328674Smav nand = le32dec(walker); 629328674Smav walker += 4; 630328674Smav printf(" %-30s: %d\n", "Retirement number", code); 631328674Smav printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); 632328674Smav } else { 633328674Smav printf("Parameter %#x entry corrupt\n", code); 634328674Smav walker += 16; 635328674Smav } 636328674Smav } 637328674Smav} 638328674Smav 639328674Smavstatic void 640328674Smavprint_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 641328674Smav{ 642328674Smav static struct kv_name kv[] = 643328674Smav { 644328674Smav { 0x0000, "Corrected Without Delay" }, 645328674Smav { 0x0001, "Corrected Maybe Delayed" }, 646328674Smav { 0x0002, "Re-Erase" }, 647328674Smav { 0x0003, "Errors Corrected" }, 648328674Smav { 0x0004, "Correct Algorithm Used" }, 649328674Smav { 0x0005, "Bytes Processed" }, 650328674Smav { 0x0006, "Uncorrected Errors" }, 651328674Smav { 0x8000, "Flash Erase Commands" }, 652328674Smav { 0x8001, "Mfg Defect Count" }, 653328674Smav { 0x8002, "Grown Defect Count" }, 654328674Smav { 0x8003, "Erase Count -- User" }, 655328674Smav { 0x8004, "Erase Count -- System" }, 656328674Smav }; 657328674Smav 658328674Smav printf("Erase Errors Subpage:\n"); 659328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 660328674Smav} 661328674Smav 662328674Smavstatic void 663328674Smavprint_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 664328674Smav{ 665328674Smav /* My drive doesn't export this -- so not coding up */ 666328674Smav printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); 667328674Smav} 668328674Smav 669328674Smavstatic void 670328674Smavprint_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 671328674Smav{ 672328674Smav uint8_t *walker = buf; 673328674Smav uint32_t min; 674328674Smav 675328674Smav printf("Temperature History:\n"); 676328674Smav printf(" %-30s: %d C\n", "Current Temperature", *walker++); 677328674Smav printf(" %-30s: %d C\n", "Reference Temperature", *walker++); 678328674Smav printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); 679328674Smav printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); 680328674Smav min = le32dec(walker); 681328674Smav walker += 4; 682328723Smav printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); 683328674Smav min = le32dec(walker); 684328674Smav walker += 4; 685328723Smav printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60); 686328674Smav min = le32dec(walker); 687328674Smav walker += 4; 688328723Smav printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); 689328674Smav} 690328674Smav 691328674Smavstatic void 692328674Smavprint_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) 693328674Smav{ 694328674Smav uint8_t *walker = buf; 695328674Smav uint64_t val; 696328674Smav 697328674Smav printf("SSD Performance Subpage Type %d:\n", res); 698328674Smav val = le64dec(walker); 699328674Smav walker += 8; 700328674Smav printf(" %-30s: %ju\n", "Host Read Commands", val); 701328674Smav val = le64dec(walker); 702328674Smav walker += 8; 703328674Smav printf(" %-30s: %ju\n", "Host Read Blocks", val); 704328674Smav val = le64dec(walker); 705328674Smav walker += 8; 706328674Smav printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); 707328674Smav val = le64dec(walker); 708328674Smav walker += 8; 709328674Smav printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); 710328674Smav val = le64dec(walker); 711328674Smav walker += 8; 712328674Smav printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); 713328674Smav val = le64dec(walker); 714328674Smav walker += 8; 715328674Smav printf(" %-30s: %ju\n", "Host Write Commands", val); 716328674Smav val = le64dec(walker); 717328674Smav walker += 8; 718328674Smav printf(" %-30s: %ju\n", "Host Write Blocks", val); 719328674Smav val = le64dec(walker); 720328674Smav walker += 8; 721328674Smav printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); 722328674Smav val = le64dec(walker); 723328674Smav walker += 8; 724328674Smav printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); 725328674Smav val = le64dec(walker); 726328674Smav walker += 8; 727328674Smav printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); 728328674Smav val = le64dec(walker); 729328674Smav walker += 8; 730328674Smav printf(" %-30s: %ju\n", "NAND Read Commands", val); 731328674Smav val = le64dec(walker); 732328674Smav walker += 8; 733328674Smav printf(" %-30s: %ju\n", "NAND Read Blocks", val); 734328674Smav val = le64dec(walker); 735328674Smav walker += 8; 736328674Smav printf(" %-30s: %ju\n", "NAND Write Commands", val); 737328674Smav val = le64dec(walker); 738328674Smav walker += 8; 739328674Smav printf(" %-30s: %ju\n", "NAND Write Blocks", val); 740328674Smav val = le64dec(walker); 741328674Smav walker += 8; 742328674Smav printf(" %-30s: %ju\n", "NAND Read Before Writes", val); 743328674Smav} 744328674Smav 745328674Smavstatic void 746328674Smavprint_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 747328674Smav{ 748328674Smav uint8_t *walker = buf; 749328674Smav 750328674Smav printf("Firmware Load Subpage:\n"); 751328674Smav printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); 752328674Smav} 753328674Smav 754328674Smavstatic void 755328674Smavkv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) 756328674Smav{ 757328674Smav size_t i; 758328674Smav 759328674Smav for (i = 0; i < nsp; i++, sp++) { 760328674Smav if (sp->key == subtype) { 761328674Smav sp->fn(buf, subtype, res, size); 762328674Smav return; 763328674Smav } 764328674Smav } 765328674Smav printf("No handler for page type %x\n", subtype); 766328674Smav} 767328674Smav 768328674Smavstatic void 769328741Smavprint_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 770328674Smav{ 771328674Smav uint8_t *walker, *end, *subpage; 772328674Smav int pages; 773328674Smav uint16_t len; 774328674Smav uint8_t subtype, res; 775328674Smav 776328674Smav printf("HGST Extra Info Log\n"); 777328674Smav printf("===================\n"); 778328674Smav 779328674Smav walker = buf; 780328674Smav pages = *walker++; 781328674Smav walker++; 782328674Smav len = le16dec(walker); 783328674Smav walker += 2; 784328674Smav end = walker + len; /* Length is exclusive of this header */ 785328674Smav 786328674Smav while (walker < end) { 787328674Smav subpage = walker + 4; 788328674Smav subtype = *walker++ & 0x3f; /* subtype */ 789328674Smav res = *walker++; /* Reserved */ 790328674Smav len = le16dec(walker); 791328674Smav walker += len + 2; /* Length, not incl header */ 792328674Smav if (walker > end) { 793328674Smav printf("Ooops! Off the end of the list\n"); 794328674Smav break; 795328674Smav } 796328674Smav kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); 797328674Smav } 798328674Smav} 799328674Smav 800328674Smav/* 801328673Smav * Table of log page printer / sizing. 802328673Smav * 803328725Smav * This includes Intel specific pages that are widely implemented. 804328725Smav * Make sure you keep all the pages of one vendor together so -v help 805328725Smav * lists all the vendors pages. 806328673Smav */ 807252302Sglebiusstatic struct logpage_function { 808252277Sjimharris uint8_t log_page; 809328710Smav const char *vendor; 810328725Smav const char *name; 811328672Smav print_fn_t print_fn; 812328672Smav size_t size; 813252277Sjimharris} logfuncs[] = { 814328725Smav {NVME_LOG_ERROR, NULL, "Drive Error Log", 815328725Smav print_log_error, 0}, 816328725Smav {NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", 817328725Smav print_log_health, sizeof(struct nvme_health_information_page)}, 818328725Smav {NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", 819328725Smav print_log_firmware, sizeof(struct nvme_firmware_page)}, 820328725Smav {HGST_INFO_LOG, "hgst", "Detailed Health/SMART", 821328725Smav print_hgst_info_log, DEFAULT_SIZE}, 822328725Smav {HGST_INFO_LOG, "wds", "Detailed Health/SMART", 823328725Smav print_hgst_info_log, DEFAULT_SIZE}, 824328725Smav {INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats", 825328725Smav print_intel_temp_stats, sizeof(struct intel_log_temp_stats)}, 826328725Smav {INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies", 827328725Smav print_intel_read_lat_log, DEFAULT_SIZE}, 828328725Smav {INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies", 829328725Smav print_intel_write_lat_log, DEFAULT_SIZE}, 830328725Smav {INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data", 831328725Smav print_intel_add_smart, DEFAULT_SIZE}, 832328725Smav {INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data", 833328725Smav print_intel_add_smart, DEFAULT_SIZE}, 834328725Smav 835328725Smav {0, NULL, NULL, NULL, 0}, 836252277Sjimharris}; 837252277Sjimharris 838252277Sjimharrisstatic void 839252277Sjimharrislogpage_usage(void) 840252277Sjimharris{ 841252277Sjimharris fprintf(stderr, "usage:\n"); 842252277Sjimharris fprintf(stderr, LOGPAGE_USAGE); 843253109Sjimharris exit(1); 844252277Sjimharris} 845252277Sjimharris 846328725Smavstatic void 847328725Smavlogpage_help(void) 848328725Smav{ 849328725Smav struct logpage_function *f; 850328725Smav const char *v; 851328725Smav 852328725Smav fprintf(stderr, "\n"); 853328725Smav fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); 854328725Smav fprintf(stderr, "-------- ---------- ----------\n"); 855328725Smav for (f = logfuncs; f->log_page > 0; f++) { 856328725Smav v = f->vendor == NULL ? "-" : f->vendor; 857328725Smav fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name); 858328725Smav } 859328725Smav 860328725Smav exit(1); 861328725Smav} 862328725Smav 863252277Sjimharrisvoid 864252277Sjimharrislogpage(int argc, char *argv[]) 865252277Sjimharris{ 866253114Sjimharris int fd, nsid; 867252277Sjimharris int log_page = 0, pageflag = false; 868328721Smav int binflag = false, hexflag = false, ns_specified; 869253114Sjimharris char ch, *p; 870253114Sjimharris char cname[64]; 871253109Sjimharris uint32_t size; 872252277Sjimharris void *buf; 873328710Smav const char *vendor = NULL; 874252277Sjimharris struct logpage_function *f; 875252277Sjimharris struct nvme_controller_data cdata; 876252277Sjimharris print_fn_t print_fn; 877252277Sjimharris 878328721Smav while ((ch = getopt(argc, argv, "bp:xv:")) != -1) { 879252277Sjimharris switch (ch) { 880328721Smav case 'b': 881328721Smav binflag = true; 882328721Smav break; 883252277Sjimharris case 'p': 884328725Smav if (strcmp(optarg, "help") == 0) 885328725Smav logpage_help(); 886328725Smav 887252277Sjimharris /* TODO: Add human-readable ASCII page IDs */ 888252277Sjimharris log_page = strtol(optarg, &p, 0); 889252277Sjimharris if (p != NULL && *p != '\0') { 890252277Sjimharris fprintf(stderr, 891252277Sjimharris "\"%s\" not valid log page id.\n", 892252277Sjimharris optarg); 893252277Sjimharris logpage_usage(); 894252277Sjimharris } 895252277Sjimharris pageflag = true; 896252277Sjimharris break; 897252277Sjimharris case 'x': 898252277Sjimharris hexflag = true; 899252277Sjimharris break; 900328710Smav case 'v': 901328725Smav if (strcmp(optarg, "help") == 0) 902328725Smav logpage_help(); 903328710Smav vendor = optarg; 904328710Smav break; 905252277Sjimharris } 906252277Sjimharris } 907252277Sjimharris 908252277Sjimharris if (!pageflag) { 909252277Sjimharris printf("Missing page_id (-p).\n"); 910252277Sjimharris logpage_usage(); 911252277Sjimharris } 912252277Sjimharris 913252277Sjimharris /* Check that a controller and/or namespace was specified. */ 914252277Sjimharris if (optind >= argc) 915252277Sjimharris logpage_usage(); 916252277Sjimharris 917253114Sjimharris if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 918253114Sjimharris ns_specified = true; 919253114Sjimharris parse_ns_str(argv[optind], cname, &nsid); 920253114Sjimharris open_dev(cname, &fd, 1, 1); 921253114Sjimharris } else { 922253114Sjimharris ns_specified = false; 923253114Sjimharris nsid = NVME_GLOBAL_NAMESPACE_TAG; 924253114Sjimharris open_dev(argv[optind], &fd, 1, 1); 925253114Sjimharris } 926253114Sjimharris 927285796Sjimharris read_controller_data(fd, &cdata); 928285796Sjimharris 929252277Sjimharris /* 930252277Sjimharris * The log page attribtues indicate whether or not the controller 931252277Sjimharris * supports the SMART/Health information log page on a per 932252277Sjimharris * namespace basis. 933252277Sjimharris */ 934253114Sjimharris if (ns_specified) { 935253114Sjimharris if (log_page != NVME_LOG_HEALTH_INFORMATION) 936253114Sjimharris errx(1, "log page %d valid only at controller level", 937253114Sjimharris log_page); 938253114Sjimharris if (cdata.lpa.ns_smart == 0) 939253114Sjimharris errx(1, 940253114Sjimharris "controller does not support per namespace " 941253114Sjimharris "smart/health information"); 942253114Sjimharris } 943252277Sjimharris 944328741Smav print_fn = print_log_hex; 945328672Smav size = DEFAULT_SIZE; 946328721Smav if (binflag) 947328721Smav print_fn = print_bin; 948328721Smav if (!binflag && !hexflag) { 949252277Sjimharris /* 950328710Smav * See if there is a pretty print function for the specified log 951328710Smav * page. If one isn't found, we just revert to the default 952328710Smav * (print_hex). If there was a vendor specified bt the user, and 953328710Smav * the page is vendor specific, don't match the print function 954328710Smav * unless the vendors match. 955252277Sjimharris */ 956328710Smav for (f = logfuncs; f->log_page > 0; f++) { 957328710Smav if (f->vendor != NULL && vendor != NULL && 958328710Smav strcmp(f->vendor, vendor) != 0) 959328710Smav continue; 960328710Smav if (log_page != f->log_page) 961328710Smav continue; 962328710Smav print_fn = f->print_fn; 963328710Smav size = f->size; 964328710Smav break; 965252277Sjimharris } 966252277Sjimharris } 967252277Sjimharris 968328672Smav if (log_page == NVME_LOG_ERROR) { 969252277Sjimharris size = sizeof(struct nvme_error_information_entry); 970252277Sjimharris size *= (cdata.elpe + 1); 971252277Sjimharris } 972252277Sjimharris 973328672Smav /* Read the log page */ 974252277Sjimharris buf = get_log_buffer(size); 975252277Sjimharris read_logpage(fd, log_page, nsid, buf, size); 976328741Smav print_fn(&cdata, buf, size); 977252277Sjimharris 978252277Sjimharris close(fd); 979253109Sjimharris exit(0); 980252277Sjimharris} 981