Annotation of src/sbin/nvmectl/logpage.c, Revision 1.8
1.8 ! andvar 1: /* $NetBSD: logpage.c,v 1.7 2018/04/18 10:11:44 nonaka Exp $ */
1.1 nonaka 2:
3: /*-
1.6 nonaka 4: * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5: *
1.1 nonaka 6: * Copyright (c) 2013 EMC Corp.
7: * All rights reserved.
8: *
9: * Copyright (C) 2012-2013 Intel Corporation
10: * All rights reserved.
11: *
12: * Redistribution and use in source and binary forms, with or without
13: * modification, are permitted provided that the following conditions
14: * are met:
15: * 1. Redistributions of source code must retain the above copyright
16: * notice, this list of conditions and the following disclaimer.
17: * 2. Redistributions in binary form must reproduce the above copyright
18: * notice, this list of conditions and the following disclaimer in the
19: * documentation and/or other materials provided with the distribution.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: */
33:
34: #include <sys/cdefs.h>
35: #ifndef lint
1.8 ! andvar 36: __RCSID("$NetBSD: logpage.c,v 1.7 2018/04/18 10:11:44 nonaka Exp $");
1.1 nonaka 37: #if 0
1.7 nonaka 38: __FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 329824 2018-02-22 13:32:31Z wma $");
1.1 nonaka 39: #endif
40: #endif
41:
42: #include <sys/param.h>
43: #include <sys/ioccom.h>
1.4 nonaka 44: #include <sys/endian.h>
1.1 nonaka 45:
46: #include <ctype.h>
47: #include <err.h>
48: #include <fcntl.h>
49: #include <stdbool.h>
50: #include <stddef.h>
51: #include <stdio.h>
52: #include <stdlib.h>
53: #include <string.h>
54: #include <unistd.h>
55:
56: #include "nvmectl.h"
57:
58: #define DEFAULT_SIZE (4096)
59: #define MAX_FW_SLOTS (7)
60:
1.6 nonaka 61: typedef void (*print_fn_t)(const struct nvm_identify_controller *cdata, void *buf,
62: uint32_t size);
1.1 nonaka 63:
1.4 nonaka 64: struct kv_name {
65: uint32_t key;
66: const char *name;
67: };
68:
69: static const char *
70: kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
71: {
72: static char bad[32];
73: size_t i;
74:
75: for (i = 0; i < kv_count; i++, kv++)
76: if (kv->key == key)
77: return kv->name;
78: snprintf(bad, sizeof(bad), "Attribute %#x", key);
79: return bad;
80: }
81:
82: static void
1.6 nonaka 83: print_log_hex(const struct nvm_identify_controller *cdata __unused, void *data,
84: uint32_t length)
1.4 nonaka 85: {
1.6 nonaka 86: print_hex(data, length);
1.4 nonaka 87: }
88:
1.6 nonaka 89: static void
90: print_bin(const struct nvm_identify_controller *cdata __unused, void *data,
91: uint32_t length)
1.4 nonaka 92: {
1.6 nonaka 93: write(STDOUT_FILENO, data, length);
1.4 nonaka 94: }
95:
1.1 nonaka 96: static void *
97: get_log_buffer(uint32_t size)
98: {
99: void *buf;
100:
101: if ((buf = malloc(size)) == NULL)
102: errx(1, "unable to malloc %u bytes", size);
103:
104: memset(buf, 0, size);
105: return (buf);
106: }
107:
108: void
109: read_logpage(int fd, uint8_t log_page, int nsid, void *payload,
110: uint32_t payload_size)
111: {
112: struct nvme_pt_command pt;
113:
114: memset(&pt, 0, sizeof(pt));
115: pt.cmd.opcode = NVM_ADMIN_GET_LOG_PG;
116: pt.cmd.nsid = nsid;
117: pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
118: pt.cmd.cdw10 |= log_page;
119: pt.buf = payload;
120: pt.len = payload_size;
121: pt.is_read = 1;
122:
123: if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
124: err(1, "get log page request failed");
125:
126: if (nvme_completion_is_error(&pt.cpl))
127: errx(1, "get log page request returned error");
128: }
129:
130: static void
1.7 nonaka 131: nvme_error_information_entry_swapbytes(struct nvme_error_information_entry *e)
132: {
133: #if _BYTE_ORDER != _LITTLE_ENDIAN
134: e->error_count = le64toh(e->error_count);
135: e->sqid = le16toh(e->sqid);
136: e->cid = le16toh(e->cid);
137: e->status = le16toh(e->status);
138: e->error_location = le16toh(e->error_location);
139: e->lba = le64toh(e->lba);
140: e->nsid = le32toh(e->nsid);
141: e->command_specific = le64toh(e->command_specific);
142: #endif
143: }
144:
145: static void
1.6 nonaka 146: print_log_error(const struct nvm_identify_controller *cdata __unused, void *buf,
147: uint32_t size)
1.1 nonaka 148: {
149: int i, nentries;
150: struct nvme_error_information_entry *entry = buf;
151:
1.7 nonaka 152: /* Convert data to host endian */
153: nvme_error_information_entry_swapbytes(entry);
154:
1.1 nonaka 155: printf("Error Information Log\n");
156: printf("=====================\n");
157:
158: if (entry->error_count == 0) {
159: printf("No error entries found\n");
160: return;
161: }
162:
163: nentries = size/sizeof(struct nvme_error_information_entry);
164: for (i = 0; i < nentries; i++, entry++) {
165: if (entry->error_count == 0)
166: break;
167:
168: printf("Entry %02d\n", i + 1);
169: printf("=========\n");
170: printf(" Error count: %ju\n", entry->error_count);
171: printf(" Submission queue ID: %u\n", entry->sqid);
172: printf(" Command ID: %u\n", entry->cid);
173: /* TODO: Export nvme_status_string structures from kernel? */
174: printf(" Status:\n");
175: printf(" Phase tag: %d\n",
176: (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_PHASE));
177: printf(" Status code: %d\n",
178: (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SC_MASK));
179: printf(" Status code type: %d\n",
180: (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SCT_MASK));
181: printf(" More: %d\n",
182: (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_M));
183: printf(" DNR: %d\n",
184: (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_DNR));
185: printf(" Error location: %u\n", entry->error_location);
186: printf(" LBA: %ju\n", entry->lba);
187: printf(" Namespace ID: %u\n", entry->nsid);
188: printf(" Vendor specific info: %u\n", entry->vendor_specific);
189: printf(" Command specific info: %ju\n",
190: entry->command_specific);
191: }
192: }
193:
194: static void
1.4 nonaka 195: print_temp(uint16_t t)
196: {
197: printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15,
198: (float)t * 9 / 5 - 459.67);
199: }
200:
201: static void
1.7 nonaka 202: nvme_health_information_page_swapbytes(struct nvme_health_information_page *e)
203: {
204: #if _BYTE_ORDER != _LITTLE_ENDIAN
205: u_int i;
206:
207: e->composite_temperature = le16toh(e->composite_temperature);
208: nvme_le128toh(e->data_units_read);
209: nvme_le128toh(e->data_units_written);
210: nvme_le128toh(e->host_read_commands);
211: nvme_le128toh(e->host_write_commands);
212: nvme_le128toh(e->controller_busy_time);
213: nvme_le128toh(e->power_cycles);
214: nvme_le128toh(e->power_on_hours);
215: nvme_le128toh(e->unsafe_shutdowns);
216: nvme_le128toh(e->media_errors);
217: nvme_le128toh(e->num_error_info_log_entries);
218: e->warning_temp_time = le32toh(e->warning_temp_time);
219: e->error_temp_time = le32toh(e->error_temp_time);
220: for (i = 0; i < __arraycount(e->temp_sensor); i++)
221: e->temp_sensor[i] = le16toh(e->temp_sensor[i]);
222: #endif
223: }
224:
225: static void
1.6 nonaka 226: print_log_health(const struct nvm_identify_controller *cdata __unused, void *buf,
227: uint32_t size __unused)
1.1 nonaka 228: {
229: struct nvme_health_information_page *health = buf;
1.7 nonaka 230: u_int i;
231:
232: /* Convert data to host endian */
233: nvme_health_information_page_swapbytes(health);
1.1 nonaka 234:
235: printf("SMART/Health Information Log\n");
236: printf("============================\n");
237:
238: printf("Critical Warning State: 0x%02x\n",
239: health->critical_warning);
240: printf(" Available spare: %d\n",
241: (uint8_t)__SHIFTOUT(health->critical_warning,
242: NVME_HEALTH_PAGE_CW_AVAIL_SPARE));
243: printf(" Temperature: %d\n",
244: (uint8_t)__SHIFTOUT(health->critical_warning,
245: NVME_HEALTH_PAGE_CW_TEMPERTURE));
246: printf(" Device reliability: %d\n",
247: (uint8_t)__SHIFTOUT(health->critical_warning,
248: NVME_HEALTH_PAGE_CW_DEVICE_RELIABLITY));
249: printf(" Read only: %d\n",
250: (uint8_t)__SHIFTOUT(health->critical_warning,
251: NVME_HEALTH_PAGE_CW_READ_ONLY));
252: printf(" Volatile memory backup: %d\n",
253: (uint8_t)__SHIFTOUT(health->critical_warning,
254: NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP));
1.4 nonaka 255: printf("Temperature: ");
256: print_temp(health->composite_temperature);
1.1 nonaka 257: printf("Available spare: %u\n",
258: health->available_spare);
259: printf("Available spare threshold: %u\n",
260: health->available_spare_threshold);
261: printf("Percentage used: %u\n",
262: health->percentage_used);
263:
1.4 nonaka 264: print_bignum("Data units (512 byte) read:", health->data_units_read, "");
265: print_bignum("Data units (512 byte) written:", health->data_units_written,
266: "");
267: print_bignum("Host read commands:", health->host_read_commands, "");
268: print_bignum("Host write commands:", health->host_write_commands, "");
269: print_bignum("Controller busy time (minutes):", health->controller_busy_time,
270: "");
271: print_bignum("Power cycles:", health->power_cycles, "");
272: print_bignum("Power on hours:", health->power_on_hours, "");
273: print_bignum("Unsafe shutdowns:", health->unsafe_shutdowns, "");
274: print_bignum("Media errors:", health->media_errors, "");
1.3 nonaka 275: print_bignum("No. error info log entries:",
276: health->num_error_info_log_entries, "");
1.4 nonaka 277:
278: printf("Warning Temp Composite Time: %d\n", health->warning_temp_time);
279: printf("Error Temp Composite Time: %d\n", health->error_temp_time);
1.7 nonaka 280: for (i = 0; i < __arraycount(health->temp_sensor); i++) {
1.4 nonaka 281: if (health->temp_sensor[i] == 0)
282: continue;
283: printf("Temperature Sensor %d: ", i + 1);
284: print_temp(health->temp_sensor[i]);
285: }
1.1 nonaka 286: }
287:
288: static void
1.7 nonaka 289: nvme_firmware_page_swapbytes(struct nvme_firmware_page *e)
290: {
291: #if _BYTE_ORDER != _LITTLE_ENDIAN
292: u_int i;
293:
294: for (i = 0; i < __arraycount(e->revision); i++)
295: e->revision[i] = le64toh(e->revision[i]);
296: #endif
297: }
298:
299: static void
1.6 nonaka 300: print_log_firmware(const struct nvm_identify_controller *cdata, void *buf,
301: uint32_t size __unused)
1.1 nonaka 302: {
1.6 nonaka 303: u_int i, slots;
1.1 nonaka 304: const char *status;
305: struct nvme_firmware_page *fw = buf;
306:
1.7 nonaka 307: /* Convert data to host endian */
308: nvme_firmware_page_swapbytes(fw);
309:
1.1 nonaka 310: printf("Firmware Slot Log\n");
311: printf("=================\n");
312:
1.6 nonaka 313: if (!(cdata->oacs & NVME_ID_CTRLR_OACS_FW))
314: slots = 1;
315: else
316: slots = MIN(__SHIFTOUT(cdata->frmw, NVME_ID_CTRLR_FRMW_NSLOT),
317: MAX_FW_SLOTS);
318:
319: for (i = 0; i < slots; i++) {
1.1 nonaka 320: printf("Slot %d: ", i + 1);
321: if (__SHIFTOUT(fw->afi, NVME_FW_PAGE_AFI_SLOT) == i + 1)
322: status = " Active";
323: else
324: status = "Inactive";
325:
326: if (fw->revision[i] == 0LLU)
327: printf("Empty\n");
328: else
329: if (isprint(*(uint8_t *)&fw->revision[i]))
330: printf("[%s] %.8s\n", status,
331: (char *)&fw->revision[i]);
332: else
333: printf("[%s] %016jx\n", status,
334: fw->revision[i]);
335: }
336: }
337:
1.4 nonaka 338: /*
339: * Intel specific log pages from
340: * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
341: *
342: * Though the version as of this date has a typo for the size of log page 0xca,
343: * offset 147: it is only 1 byte, not 6.
344: */
345: static void
1.7 nonaka 346: intel_log_temp_stats_swapbytes(struct intel_log_temp_stats *e)
347: {
348: #if _BYTE_ORDER != _LITTLE_ENDIAN
349: e->current = le64toh(e->current);
350: e->overtemp_flag_last = le64toh(e->overtemp_flag_last);
351: e->overtemp_flag_life = le64toh(e->overtemp_flag_life);
352: e->max_temp = le64toh(e->max_temp);
353: e->min_temp = le64toh(e->min_temp);
354: e->max_oper_temp = le64toh(e->max_oper_temp);
355: e->min_oper_temp = le64toh(e->min_oper_temp);
356: e->est_offset = le64toh(e->est_offset);
357: #endif
358: }
359:
360: static void
1.6 nonaka 361: print_intel_temp_stats(const struct nvm_identify_controller *cdata __unused,
362: void *buf, uint32_t size __unused)
1.4 nonaka 363: {
364: struct intel_log_temp_stats *temp = buf;
365:
1.7 nonaka 366: /* Convert data to host endian */
367: intel_log_temp_stats_swapbytes(temp);
368:
1.4 nonaka 369: printf("Intel Temperature Log\n");
370: printf("=====================\n");
371:
372: printf("Current: ");
373: print_temp(temp->current);
374: printf("Overtemp Last Flags %#jx\n",
375: (uintmax_t)temp->overtemp_flag_last);
376: printf("Overtemp Lifetime Flags %#jx\n",
377: (uintmax_t)temp->overtemp_flag_life);
378: printf("Max Temperature ");
379: print_temp(temp->max_temp);
380: printf("Min Temperature ");
381: print_temp(temp->min_temp);
382: printf("Max Operating Temperature ");
383: print_temp(temp->max_oper_temp);
384: printf("Min Operating Temperature ");
385: print_temp(temp->min_oper_temp);
386: printf("Estimated Temperature Offset: %ju C/K\n",
387: (uintmax_t)temp->est_offset);
388: }
389:
390: /*
391: * Format from Table 22, section 5.7 IO Command Latency Statistics.
392: * Read and write stats pages have identical encoding.
393: */
394: static void
1.6 nonaka 395: print_intel_read_write_lat_log(const struct nvm_identify_controller *cdata __unused,
396: void *buf, uint32_t size __unused)
1.4 nonaka 397: {
398: const char *walker = buf;
399: int i;
400:
401: printf("Major: %d\n", le16dec(walker + 0));
402: printf("Minor: %d\n", le16dec(walker + 2));
403: for (i = 0; i < 32; i++)
404: printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32,
405: (uintmax_t)le32dec(walker + 4 + i * 4));
406: for (i = 1; i < 32; i++)
407: printf("%4dms-%4dms: %ju\n", i, i + 1,
408: (uintmax_t)le32dec(walker + 132 + i * 4));
409: for (i = 1; i < 32; i++)
410: printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32,
411: (uintmax_t)le32dec(walker + 256 + i * 4));
412: }
413:
414: static void
1.6 nonaka 415: print_intel_read_lat_log(const struct nvm_identify_controller *cdata, void *buf,
416: uint32_t size)
1.4 nonaka 417: {
418:
419: printf("Intel Read Latency Log\n");
420: printf("======================\n");
1.6 nonaka 421: print_intel_read_write_lat_log(cdata, buf, size);
1.4 nonaka 422: }
423:
424: static void
1.6 nonaka 425: print_intel_write_lat_log(const struct nvm_identify_controller *cdata, void *buf,
426: uint32_t size)
1.4 nonaka 427: {
428:
429: printf("Intel Write Latency Log\n");
430: printf("=======================\n");
1.6 nonaka 431: print_intel_read_write_lat_log(cdata, buf, size);
1.4 nonaka 432: }
433:
434: /*
435: * Table 19. 5.4 SMART Attributes.
436: * Samsung also implements this and some extra data not documented.
437: */
438: static void
1.6 nonaka 439: print_intel_add_smart(const struct nvm_identify_controller *cdata __unused,
440: void *buf, uint32_t size __unused)
1.4 nonaka 441: {
442: uint8_t *walker = buf;
443: uint8_t *end = walker + 150;
444: const char *name;
445: uint64_t raw;
446: uint8_t normalized;
447:
448: static struct kv_name kv[] = {
449: { 0xab, "Program Fail Count" },
450: { 0xac, "Erase Fail Count" },
451: { 0xad, "Wear Leveling Count" },
452: { 0xb8, "End to End Error Count" },
453: { 0xc7, "CRC Error Count" },
454: { 0xe2, "Timed: Media Wear" },
455: { 0xe3, "Timed: Host Read %" },
456: { 0xe4, "Timed: Elapsed Time" },
457: { 0xea, "Thermal Throttle Status" },
458: { 0xf0, "Retry Buffer Overflows" },
459: { 0xf3, "PLL Lock Loss Count" },
460: { 0xf4, "NAND Bytes Written" },
461: { 0xf5, "Host Bytes Written" },
462: };
463:
464: printf("Additional SMART Data Log\n");
465: printf("=========================\n");
466: /*
467: * walker[0] = Key
468: * walker[1,2] = reserved
469: * walker[3] = Normalized Value
470: * walker[4] = reserved
471: * walker[5..10] = Little Endian Raw value
472: * (or other represenations)
473: * walker[11] = reserved
474: */
475: while (walker < end) {
476: name = kv_lookup(kv, __arraycount(kv), *walker);
477: normalized = walker[3];
478: raw = le48dec(walker + 5);
479: switch (*walker){
480: case 0:
481: break;
482: case 0xad:
483: printf("%-32s: %3d min: %u max: %u ave: %u\n", name,
484: normalized, le16dec(walker + 5), le16dec(walker + 7),
485: le16dec(walker + 9));
486: break;
487: case 0xe2:
488: printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
489: break;
490: case 0xea:
491: printf("%-32s: %3d %d%% %d times\n", name, normalized,
492: walker[5], le32dec(walker+6));
493: break;
494: default:
495: printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
496: break;
497: }
498: walker += 12;
499: }
500: }
501:
502: /*
503: * HGST's 0xc1 page. This is a grab bag of additional data. Please see
504: * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
505: * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
506: * Appendix A for details
507: */
508:
1.6 nonaka 509: typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res,
510: uint32_t size);
1.4 nonaka 511:
512: struct subpage_print {
513: uint16_t key;
514: subprint_fn_t fn;
515: };
516:
517: static void print_hgst_info_write_errors(void *, uint16_t, uint8_t, uint32_t);
518: static void print_hgst_info_read_errors(void *, uint16_t, uint8_t, uint32_t);
519: static void print_hgst_info_verify_errors(void *, uint16_t, uint8_t, uint32_t);
520: static void print_hgst_info_self_test(void *, uint16_t, uint8_t, uint32_t);
521: static void print_hgst_info_background_scan(void *, uint16_t, uint8_t, uint32_t);
522: static void print_hgst_info_erase_errors(void *, uint16_t, uint8_t, uint32_t);
523: static void print_hgst_info_erase_counts(void *, uint16_t, uint8_t, uint32_t);
524: static void print_hgst_info_temp_history(void *, uint16_t, uint8_t, uint32_t);
525: static void print_hgst_info_ssd_perf(void *, uint16_t, uint8_t, uint32_t);
526: static void print_hgst_info_firmware_load(void *, uint16_t, uint8_t, uint32_t);
527:
528: static struct subpage_print hgst_subpage[] = {
529: { 0x02, print_hgst_info_write_errors },
530: { 0x03, print_hgst_info_read_errors },
531: { 0x05, print_hgst_info_verify_errors },
532: { 0x10, print_hgst_info_self_test },
533: { 0x15, print_hgst_info_background_scan },
534: { 0x30, print_hgst_info_erase_errors },
535: { 0x31, print_hgst_info_erase_counts },
536: { 0x32, print_hgst_info_temp_history },
537: { 0x37, print_hgst_info_ssd_perf },
538: { 0x38, print_hgst_info_firmware_load },
539: };
540:
541: /* Print a subpage that is basically just key value pairs */
542: static void
543: print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
544: const struct kv_name *kv, size_t kv_count)
545: {
546: uint8_t *wsp, *esp;
547: uint16_t ptype;
548: uint8_t plen;
549: uint64_t param;
550: int i;
551:
552: wsp = buf;
553: esp = wsp + size;
554: while (wsp < esp) {
555: ptype = le16dec(wsp);
556: wsp += 2;
557: wsp++; /* Flags, just ignore */
558: plen = *wsp++;
559: param = 0;
560: for (i = 0; i < plen; i++)
561: param |= (uint64_t)*wsp++ << (i * 8);
562: printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype),
563: (uintmax_t)param);
564: }
565: }
566:
567: static void
568: print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused,
569: uint32_t size)
570: {
571: static const struct kv_name kv[] = {
572: { 0x0000, "Corrected Without Delay" },
573: { 0x0001, "Corrected Maybe Delayed" },
574: { 0x0002, "Re-Writes" },
575: { 0x0003, "Errors Corrected" },
576: { 0x0004, "Correct Algorithm Used" },
577: { 0x0005, "Bytes Processed" },
578: { 0x0006, "Uncorrected Errors" },
579: { 0x8000, "Flash Write Commands" },
580: { 0x8001, "HGST Special" },
581: };
582:
583: printf("Write Errors Subpage:\n");
584: print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
585: }
586:
587: static void
588: print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused,
589: uint32_t size)
590: {
591: static const struct kv_name kv[] = {
592: { 0x0000, "Corrected Without Delay" },
593: { 0x0001, "Corrected Maybe Delayed" },
594: { 0x0002, "Re-Reads" },
595: { 0x0003, "Errors Corrected" },
596: { 0x0004, "Correct Algorithm Used" },
597: { 0x0005, "Bytes Processed" },
598: { 0x0006, "Uncorrected Errors" },
599: { 0x8000, "Flash Read Commands" },
600: { 0x8001, "XOR Recovered" },
601: { 0x8002, "Total Corrected Bits" },
602: };
603:
604: printf("Read Errors Subpage:\n");
605: print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
606: }
607:
608: static void
609: print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused,
610: uint32_t size)
611: {
612: static const struct kv_name kv[] = {
613: { 0x0000, "Corrected Without Delay" },
614: { 0x0001, "Corrected Maybe Delayed" },
615: { 0x0002, "Re-Reads" },
616: { 0x0003, "Errors Corrected" },
617: { 0x0004, "Correct Algorithm Used" },
618: { 0x0005, "Bytes Processed" },
619: { 0x0006, "Uncorrected Errors" },
620: { 0x8000, "Commands Processed" },
621: };
622:
623: printf("Verify Errors Subpage:\n");
624: print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
625: }
626:
627: static void
628: print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused,
629: uint32_t size)
630: {
631: size_t i;
632: uint8_t *walker = buf;
633: uint16_t code, hrs;
634: uint32_t lba;
635:
636: printf("Self Test Subpage:\n");
637: for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */
638: code = le16dec(walker);
639: walker += 2;
640: walker++; /* Ignore fixed flags */
641: if (*walker == 0) /* Last entry is zero length */
642: break;
643: if (*walker++ != 0x10) {
644: printf("Bad length for self test report\n");
645: return;
646: }
647: printf(" %-30s: %d\n", "Recent Test", code);
648: printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
649: printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
650: walker++;
651: printf(" %-28s: %#x\n", "Self-Test Number", *walker++);
652: hrs = le16dec(walker);
653: walker += 2;
654: lba = le32dec(walker);
655: walker += 4;
656: printf(" %-28s: %u\n", "Total Power On Hrs", hrs);
657: printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba,
658: (uintmax_t)lba);
659: printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
660: printf(" %-28s: %#x\n", "Additional Sense Code", *walker++);
661: printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
662: printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++);
663: }
664: }
665:
666: static void
667: print_hgst_info_background_scan(void *buf, uint16_t subtype __unused,
668: uint8_t res __unused, uint32_t size)
669: {
670: uint8_t *walker = buf;
671: uint8_t status;
672: uint16_t code, nscan, progress;
673: uint32_t pom, nand;
674:
675: printf("Background Media Scan Subpage:\n");
676: /* Decode the header */
677: code = le16dec(walker);
678: walker += 2;
679: walker++; /* Ignore fixed flags */
680: if (*walker++ != 0x10) {
681: printf("Bad length for background scan header\n");
682: return;
683: }
684: if (code != 0) {
1.8 ! andvar 685: printf("Expected code 0, found code %#x\n", code);
1.4 nonaka 686: return;
687: }
688: pom = le32dec(walker);
689: walker += 4;
690: walker++; /* Reserved */
691: status = *walker++;
692: nscan = le16dec(walker);
693: walker += 2;
694: progress = le16dec(walker);
695: walker += 2;
696: walker += 6; /* Reserved */
697: printf(" %-30s: %d\n", "Power On Minutes", pom);
698: printf(" %-30s: %x (%s)\n", "BMS Status", status,
699: status == 0 ? "idle" : (status == 1 ? "active" :
700: (status == 8 ? "suspended" : "unknown")));
701: printf(" %-30s: %d\n", "Number of BMS", nscan);
702: printf(" %-30s: %d\n", "Progress Current BMS", progress);
703: /* Report retirements */
704: if (walker - (uint8_t *)buf != 20) {
705: printf("Coding error, offset not 20\n");
706: return;
707: }
708: size -= 20;
709: printf(" %-30s: %d\n", "BMS retirements", size / 0x18);
710: while (size > 0) {
711: code = le16dec(walker);
712: walker += 2;
713: walker++;
714: if (*walker++ != 0x14) {
715: printf("Bad length parameter\n");
716: return;
717: }
718: pom = le32dec(walker);
719: walker += 4;
720: /*
721: * Spec sheet says the following are hard coded, if true, just
722: * print the NAND retirement.
723: */
724: if (walker[0] == 0x41 &&
725: walker[1] == 0x0b &&
726: walker[2] == 0x01 &&
727: walker[3] == 0x00 &&
728: walker[4] == 0x00 &&
729: walker[5] == 0x00 &&
730: walker[6] == 0x00 &&
731: walker[7] == 0x00) {
732: walker += 8;
733: walker += 4; /* Skip reserved */
734: nand = le32dec(walker);
735: walker += 4;
736: printf(" %-30s: %d\n", "Retirement number", code);
737: printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
738: } else {
739: printf("Parameter %#x entry corrupt\n", code);
740: walker += 16;
741: }
742: }
743: }
744:
745: static void
746: print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused,
747: uint8_t res __unused, uint32_t size)
748: {
749: static const struct kv_name kv[] = {
750: { 0x0000, "Corrected Without Delay" },
751: { 0x0001, "Corrected Maybe Delayed" },
752: { 0x0002, "Re-Erase" },
753: { 0x0003, "Errors Corrected" },
754: { 0x0004, "Correct Algorithm Used" },
755: { 0x0005, "Bytes Processed" },
756: { 0x0006, "Uncorrected Errors" },
757: { 0x8000, "Flash Erase Commands" },
758: { 0x8001, "Mfg Defect Count" },
759: { 0x8002, "Grown Defect Count" },
760: { 0x8003, "Erase Count -- User" },
761: { 0x8004, "Erase Count -- System" },
762: };
763:
764: printf("Erase Errors Subpage:\n");
765: print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv));
766: }
767:
768: static void
769: print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused,
770: uint32_t size)
771: {
772: /* My drive doesn't export this -- so not coding up */
773: printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
774: }
775:
776: static void
777: print_hgst_info_temp_history(void *buf, uint16_t subtype __unused,
778: uint8_t res __unused, uint32_t size __unused)
779: {
780: uint8_t *walker = buf;
781: uint32_t min;
782:
783: printf("Temperature History:\n");
784: printf(" %-30s: %d C\n", "Current Temperature", *walker++);
785: printf(" %-30s: %d C\n", "Reference Temperature", *walker++);
786: printf(" %-30s: %d C\n", "Maximum Temperature", *walker++);
787: printf(" %-30s: %d C\n", "Minimum Temperature", *walker++);
788: min = le32dec(walker);
789: walker += 4;
790: printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
791: min = le32dec(walker);
792: walker += 4;
793: printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60,
794: min % 60);
795: min = le32dec(walker);
796: walker += 4;
797: printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
798: }
799:
800: static void
801: print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res,
802: uint32_t size __unused)
803: {
804: uint8_t *walker = buf;
805: uint64_t val;
806:
807: printf("SSD Performance Subpage Type %d:\n", res);
808: val = le64dec(walker);
809: walker += 8;
810: printf(" %-30s: %ju\n", "Host Read Commands", val);
811: val = le64dec(walker);
812: walker += 8;
813: printf(" %-30s: %ju\n", "Host Read Blocks", val);
814: val = le64dec(walker);
815: walker += 8;
816: printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val);
817: val = le64dec(walker);
818: walker += 8;
819: printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
820: val = le64dec(walker);
821: walker += 8;
822: printf(" %-30s: %ju\n", "Host Read Commands Stalled", val);
823: val = le64dec(walker);
824: walker += 8;
825: printf(" %-30s: %ju\n", "Host Write Commands", val);
826: val = le64dec(walker);
827: walker += 8;
828: printf(" %-30s: %ju\n", "Host Write Blocks", val);
829: val = le64dec(walker);
830: walker += 8;
831: printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val);
832: val = le64dec(walker);
833: walker += 8;
834: printf(" %-30s: %ju\n", "Host Write Odd End Commands", val);
835: val = le64dec(walker);
836: walker += 8;
837: printf(" %-30s: %ju\n", "Host Write Commands Stalled", val);
838: val = le64dec(walker);
839: walker += 8;
840: printf(" %-30s: %ju\n", "NAND Read Commands", val);
841: val = le64dec(walker);
842: walker += 8;
843: printf(" %-30s: %ju\n", "NAND Read Blocks", val);
844: val = le64dec(walker);
845: walker += 8;
846: printf(" %-30s: %ju\n", "NAND Write Commands", val);
847: val = le64dec(walker);
848: walker += 8;
849: printf(" %-30s: %ju\n", "NAND Write Blocks", val);
850: val = le64dec(walker);
851: walker += 8;
852: printf(" %-30s: %ju\n", "NAND Read Before Writes", val);
853: }
854:
855: static void
856: print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused,
857: uint8_t res __unused, uint32_t size __unused)
858: {
859: uint8_t *walker = buf;
860:
861: printf("Firmware Load Subpage:\n");
862: printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker));
863: }
864:
865: static void
866: kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size,
867: struct subpage_print *sp, size_t nsp)
868: {
869: size_t i;
870:
871: for (i = 0; i < nsp; i++, sp++) {
872: if (sp->key == subtype) {
873: sp->fn(buf, subtype, res, size);
874: return;
875: }
876: }
877: printf("No handler for page type %x\n", subtype);
878: }
879:
880: static void
1.6 nonaka 881: print_hgst_info_log(const struct nvm_identify_controller *cdata __unused, void *buf,
882: uint32_t size __unused)
1.4 nonaka 883: {
884: uint8_t *walker, *end, *subpage;
885: int pages __unused;
886: uint16_t len;
887: uint8_t subtype, res;
888:
889: printf("HGST Extra Info Log\n");
890: printf("===================\n");
891:
892: walker = buf;
893: pages = *walker++;
894: walker++;
895: len = le16dec(walker);
896: walker += 2;
897: end = walker + len; /* Length is exclusive of this header */
898:
899: while (walker < end) {
900: subpage = walker + 4;
901: subtype = *walker++ & 0x3f; /* subtype */
902: res = *walker++; /* Reserved */
903: len = le16dec(walker);
904: walker += len + 2; /* Length, not incl header */
905: if (walker > end) {
906: printf("Ooops! Off the end of the list\n");
907: break;
908: }
909: kv_indirect(subpage, subtype, res, len, hgst_subpage,
910: __arraycount(hgst_subpage));
911: }
912: }
913:
914: /*
915: * Table of log page printer / sizing.
916: *
917: * This includes Intel specific pages that are widely implemented.
918: * Make sure you keep all the pages of one vendor together so -v help
919: * lists all the vendors pages.
920: */
1.1 nonaka 921: static struct logpage_function {
922: uint8_t log_page;
1.4 nonaka 923: const char *vendor;
924: const char *name;
925: print_fn_t print_fn;
926: size_t size;
1.1 nonaka 927: } logfuncs[] = {
1.4 nonaka 928: {NVME_LOG_ERROR, NULL, "Drive Error Log",
929: print_log_error, 0},
930: {NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data",
931: print_log_health, sizeof(struct nvme_health_information_page)},
932: {NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
933: print_log_firmware, sizeof(struct nvme_firmware_page)},
934: {HGST_INFO_LOG, "hgst", "Detailed Health/SMART",
935: print_hgst_info_log, DEFAULT_SIZE},
936: {HGST_INFO_LOG, "wds", "Detailed Health/SMART",
937: print_hgst_info_log, DEFAULT_SIZE},
938: {INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats",
939: print_intel_temp_stats, sizeof(struct intel_log_temp_stats)},
940: {INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies",
941: print_intel_read_lat_log, DEFAULT_SIZE},
942: {INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies",
943: print_intel_write_lat_log, DEFAULT_SIZE},
944: {INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data",
945: print_intel_add_smart, DEFAULT_SIZE},
946: {INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data",
947: print_intel_add_smart, DEFAULT_SIZE},
948:
949: {0, NULL, NULL, NULL, 0},
1.1 nonaka 950: };
951:
1.2 joerg 952: __dead static void
1.1 nonaka 953: logpage_usage(void)
954: {
955: fprintf(stderr, "usage:\n");
1.5 jdolecek 956: fprintf(stderr, "\t%s " LOGPAGE_USAGE, getprogname());
1.1 nonaka 957: exit(1);
958: }
959:
1.4 nonaka 960: __dead static void
961: logpage_help(void)
962: {
963: struct logpage_function *f;
964: const char *v;
965:
966: fprintf(stderr, "\n");
967: fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
968: fprintf(stderr, "-------- ---------- ----------\n");
969: for (f = logfuncs; f->log_page > 0; f++) {
970: v = f->vendor == NULL ? "-" : f->vendor;
971: fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name);
972: }
973:
974: exit(1);
975: }
976:
1.1 nonaka 977: void
978: logpage(int argc, char *argv[])
979: {
980: int fd, nsid;
981: int log_page = 0, pageflag = false;
1.4 nonaka 982: int binflag = false, hexflag = false, ns_specified;
1.1 nonaka 983: int ch;
984: char *p;
985: char cname[64];
986: uint32_t size;
987: void *buf;
1.4 nonaka 988: const char *vendor = NULL;
1.1 nonaka 989: struct logpage_function *f;
990: struct nvm_identify_controller cdata;
991: print_fn_t print_fn;
992:
1.4 nonaka 993: while ((ch = getopt(argc, argv, "bp:xv:")) != -1) {
1.1 nonaka 994: switch (ch) {
1.4 nonaka 995: case 'b':
996: binflag = true;
997: break;
1.1 nonaka 998: case 'p':
1.4 nonaka 999: if (strcmp(optarg, "help") == 0)
1000: logpage_help();
1001:
1.1 nonaka 1002: /* TODO: Add human-readable ASCII page IDs */
1003: log_page = strtol(optarg, &p, 0);
1004: if (p != NULL && *p != '\0') {
1005: fprintf(stderr,
1006: "\"%s\" not valid log page id.\n",
1007: optarg);
1008: logpage_usage();
1009: }
1010: pageflag = true;
1011: break;
1012: case 'x':
1013: hexflag = true;
1014: break;
1.4 nonaka 1015: case 'v':
1016: if (strcmp(optarg, "help") == 0)
1017: logpage_help();
1018: vendor = optarg;
1019: break;
1.1 nonaka 1020: }
1021: }
1022:
1023: if (!pageflag) {
1024: printf("Missing page_id (-p).\n");
1025: logpage_usage();
1026: }
1027:
1028: /* Check that a controller and/or namespace was specified. */
1029: if (optind >= argc)
1030: logpage_usage();
1031:
1032: if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
1033: ns_specified = true;
1034: parse_ns_str(argv[optind], cname, &nsid);
1035: open_dev(cname, &fd, 1, 1);
1036: } else {
1037: ns_specified = false;
1038: nsid = 0xffffffff;
1039: open_dev(argv[optind], &fd, 1, 1);
1040: }
1041:
1042: read_controller_data(fd, &cdata);
1043:
1044: /*
1.8 ! andvar 1045: * The log page attributes indicate whether or not the controller
1.1 nonaka 1046: * supports the SMART/Health information log page on a per
1047: * namespace basis.
1048: */
1049: if (ns_specified) {
1050: if (log_page != NVME_LOG_HEALTH_INFORMATION)
1051: errx(1, "log page %d valid only at controller level",
1052: log_page);
1053: if (!(cdata.lpa & NVME_ID_CTRLR_LPA_NS_SMART))
1054: errx(1,
1055: "controller does not support per namespace "
1056: "smart/health information");
1057: }
1058:
1.6 nonaka 1059: print_fn = print_log_hex;
1.4 nonaka 1060: size = DEFAULT_SIZE;
1061: if (binflag)
1062: print_fn = print_bin;
1063: if (!binflag && !hexflag) {
1.1 nonaka 1064: /*
1.4 nonaka 1065: * See if there is a pretty print function for the specified log
1066: * page. If one isn't found, we just revert to the default
1067: * (print_hex). If there was a vendor specified bt the user, and
1068: * the page is vendor specific, don't match the print function
1069: * unless the vendors match.
1.1 nonaka 1070: */
1.4 nonaka 1071: for (f = logfuncs; f->log_page > 0; f++) {
1072: if (f->vendor != NULL && vendor != NULL &&
1073: strcmp(f->vendor, vendor) != 0)
1074: continue;
1075: if (log_page != f->log_page)
1076: continue;
1077: print_fn = f->print_fn;
1078: size = f->size;
1079: break;
1.1 nonaka 1080: }
1081: }
1082:
1.4 nonaka 1083: if (log_page == NVME_LOG_ERROR) {
1.1 nonaka 1084: size = sizeof(struct nvme_error_information_entry);
1085: size *= (cdata.elpe + 1);
1086: }
1087:
1.4 nonaka 1088: /* Read the log page */
1.1 nonaka 1089: buf = get_log_buffer(size);
1090: read_logpage(fd, log_page, nsid, buf, size);
1.6 nonaka 1091: print_fn(&cdata, buf, size);
1.1 nonaka 1092:
1093: close(fd);
1094: exit(0);
1095: }
CVSweb <webmaster@jp.NetBSD.org>