]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/logpage.c
MFC r350523, r350524: Add IOCTL to translate nvdX into nvmeY and NSID.
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / logpage.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/ioccom.h>
37
38 #include <ctype.h>
39 #include <err.h>
40 #include <fcntl.h>
41 #include <stdbool.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <sys/endian.h>
48
49 #include "nvmecontrol.h"
50
51 /* Tables for command line parsing */
52
53 static cmd_fn_t logpage;
54
55 #define NONE 0xffffffffu
56 static struct options {
57         bool            binary;
58         bool            hex;
59         uint32_t        page;
60         const char      *vendor;
61         const char      *dev;
62 } opt = {
63         .binary = false,
64         .hex = false,
65         .page = NONE,
66         .vendor = NULL,
67         .dev = NULL,
68 };
69
70 static const struct opts logpage_opts[] = {
71 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
72         OPT("binary", 'b', arg_none, opt, binary,
73             "Dump the log page as binary"),
74         OPT("hex", 'x', arg_none, opt, hex,
75             "Dump the log page as hex"),
76         OPT("page", 'p', arg_uint32, opt, page,
77             "Page to dump"),
78         OPT("vendor", 'v', arg_string, opt, vendor,
79             "Vendor specific formatting"),
80         { NULL, 0, arg_none, NULL, NULL }
81 };
82 #undef OPT
83
84 static const struct args logpage_args[] = {
85         { arg_string, &opt.dev, "<controller id|namespace id>" },
86         { arg_none, NULL, NULL },
87 };
88
89 static struct cmd logpage_cmd = {
90         .name = "logpage",
91         .fn = logpage,
92         .descr = "Print logpages in human-readable form",
93         .ctx_size = sizeof(opt),
94         .opts = logpage_opts,
95         .args = logpage_args,
96 };
97
98 CMD_COMMAND(logpage_cmd);
99
100 /* End of tables for command line parsing */
101
102 #define MAX_FW_SLOTS    (7)
103
104 static SLIST_HEAD(,logpage_function) logpages;
105
106 void
107 logpage_register(struct logpage_function *p)
108 {
109
110         SLIST_INSERT_HEAD(&logpages, p, link);
111 }
112
113 const char *
114 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
115 {
116         static char bad[32];
117         size_t i;
118
119         for (i = 0; i < kv_count; i++, kv++)
120                 if (kv->key == key)
121                         return kv->name;
122         snprintf(bad, sizeof(bad), "Attribute %#x", key);
123         return bad;
124 }
125
126 static void
127 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
128 {
129
130         print_hex(data, length);
131 }
132
133 static void
134 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
135 {
136
137         write(STDOUT_FILENO, data, length);
138 }
139
140 static void *
141 get_log_buffer(uint32_t size)
142 {
143         void    *buf;
144
145         if ((buf = malloc(size)) == NULL)
146                 errx(1, "unable to malloc %u bytes", size);
147
148         memset(buf, 0, size);
149         return (buf);
150 }
151
152 void
153 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
154     uint32_t payload_size)
155 {
156         struct nvme_pt_command  pt;
157         struct nvme_error_information_entry     *err_entry;
158         int i, err_pages;
159
160         memset(&pt, 0, sizeof(pt));
161         pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
162         pt.cmd.nsid = htole32(nsid);
163         pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
164         pt.cmd.cdw10 |= log_page;
165         pt.cmd.cdw10 = htole32(pt.cmd.cdw10);
166         pt.buf = payload;
167         pt.len = payload_size;
168         pt.is_read = 1;
169
170         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
171                 err(1, "get log page request failed");
172
173         /* Convert data to host endian */
174         switch (log_page) {
175         case NVME_LOG_ERROR:
176                 err_entry = (struct nvme_error_information_entry *)payload;
177                 err_pages = payload_size / sizeof(struct nvme_error_information_entry);
178                 for (i = 0; i < err_pages; i++)
179                         nvme_error_information_entry_swapbytes(err_entry++);
180                 break;
181         case NVME_LOG_HEALTH_INFORMATION:
182                 nvme_health_information_page_swapbytes(
183                     (struct nvme_health_information_page *)payload);
184                 break;
185         case NVME_LOG_FIRMWARE_SLOT:
186                 nvme_firmware_page_swapbytes(
187                     (struct nvme_firmware_page *)payload);
188                 break;
189         case INTEL_LOG_TEMP_STATS:
190                 intel_log_temp_stats_swapbytes(
191                     (struct intel_log_temp_stats *)payload);
192                 break;
193         default:
194                 break;
195         }
196
197         if (nvme_completion_is_error(&pt.cpl))
198                 errx(1, "get log page request returned error");
199 }
200
201 static void
202 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
203 {
204         int                                     i, nentries;
205         uint16_t                                status;
206         uint8_t                                 p, sc, sct, m, dnr;
207         struct nvme_error_information_entry     *entry = buf;
208
209         printf("Error Information Log\n");
210         printf("=====================\n");
211
212         if (entry->error_count == 0) {
213                 printf("No error entries found\n");
214                 return;
215         }
216
217         nentries = size/sizeof(struct nvme_error_information_entry);
218         for (i = 0; i < nentries; i++, entry++) {
219                 if (entry->error_count == 0)
220                         break;
221
222                 status = entry->status;
223
224                 p = NVME_STATUS_GET_P(status);
225                 sc = NVME_STATUS_GET_SC(status);
226                 sct = NVME_STATUS_GET_SCT(status);
227                 m = NVME_STATUS_GET_M(status);
228                 dnr = NVME_STATUS_GET_DNR(status);
229
230                 printf("Entry %02d\n", i + 1);
231                 printf("=========\n");
232                 printf(" Error count:          %ju\n", entry->error_count);
233                 printf(" Submission queue ID:  %u\n", entry->sqid);
234                 printf(" Command ID:           %u\n", entry->cid);
235                 /* TODO: Export nvme_status_string structures from kernel? */
236                 printf(" Status:\n");
237                 printf("  Phase tag:           %d\n", p);
238                 printf("  Status code:         %d\n", sc);
239                 printf("  Status code type:    %d\n", sct);
240                 printf("  More:                %d\n", m);
241                 printf("  DNR:                 %d\n", dnr);
242                 printf(" Error location:       %u\n", entry->error_location);
243                 printf(" LBA:                  %ju\n", entry->lba);
244                 printf(" Namespace ID:         %u\n", entry->nsid);
245                 printf(" Vendor specific info: %u\n", entry->vendor_specific);
246         }
247 }
248
249 void
250 print_temp(uint16_t t)
251 {
252         printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
253 }
254
255
256 static void
257 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
258 {
259         struct nvme_health_information_page *health = buf;
260         char cbuf[UINT128_DIG + 1];
261         uint8_t warning;
262         int i;
263
264         warning = health->critical_warning;
265
266         printf("SMART/Health Information Log\n");
267         printf("============================\n");
268
269         printf("Critical Warning State:         0x%02x\n", warning);
270         printf(" Available spare:               %d\n",
271             !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE));
272         printf(" Temperature:                   %d\n",
273             !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE));
274         printf(" Device reliability:            %d\n",
275             !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY));
276         printf(" Read only:                     %d\n",
277             !!(warning & NVME_CRIT_WARN_ST_READ_ONLY));
278         printf(" Volatile memory backup:        %d\n",
279             !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP));
280         printf("Temperature:                    ");
281         print_temp(health->temperature);
282         printf("Available spare:                %u\n",
283             health->available_spare);
284         printf("Available spare threshold:      %u\n",
285             health->available_spare_threshold);
286         printf("Percentage used:                %u\n",
287             health->percentage_used);
288
289         printf("Data units (512,000 byte) read: %s\n",
290             uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
291         printf("Data units written:             %s\n",
292             uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
293         printf("Host read commands:             %s\n",
294             uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
295         printf("Host write commands:            %s\n",
296             uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
297         printf("Controller busy time (minutes): %s\n",
298             uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
299         printf("Power cycles:                   %s\n",
300             uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
301         printf("Power on hours:                 %s\n",
302             uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
303         printf("Unsafe shutdowns:               %s\n",
304             uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
305         printf("Media errors:                   %s\n",
306             uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
307         printf("No. error info log entries:     %s\n",
308             uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
309
310         printf("Warning Temp Composite Time:    %d\n", health->warning_temp_time);
311         printf("Error Temp Composite Time:      %d\n", health->error_temp_time);
312         for (i = 0; i < 8; i++) {
313                 if (health->temp_sensor[i] == 0)
314                         continue;
315                 printf("Temperature Sensor %d:           ", i + 1);
316                 print_temp(health->temp_sensor[i]);
317         }
318 }
319
320 static void
321 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
322 {
323         int                             i, slots;
324         const char                      *status;
325         struct nvme_firmware_page       *fw = buf;
326         uint8_t                         afi_slot;
327         uint16_t                        oacs_fw;
328         uint8_t                         fw_num_slots;
329
330         afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT;
331         afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK;
332
333         oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
334                 NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
335         fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
336                 NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
337
338         printf("Firmware Slot Log\n");
339         printf("=================\n");
340
341         if (oacs_fw == 0)
342                 slots = 1;
343         else
344                 slots = MIN(fw_num_slots, MAX_FW_SLOTS);
345
346         for (i = 0; i < slots; i++) {
347                 printf("Slot %d: ", i + 1);
348                 if (afi_slot == i + 1)
349                         status = "  Active";
350                 else
351                         status = "Inactive";
352
353                 if (fw->revision[i] == 0LLU)
354                         printf("Empty\n");
355                 else
356                         if (isprint(*(char *)&fw->revision[i]))
357                                 printf("[%s] %.8s\n", status,
358                                     (char *)&fw->revision[i]);
359                         else
360                                 printf("[%s] %016jx\n", status,
361                                     fw->revision[i]);
362         }
363 }
364
365 /*
366  * Table of log page printer / sizing.
367  *
368  * Make sure you keep all the pages of one vendor together so -v help
369  * lists all the vendors pages.
370  */
371 NVME_LOGPAGE(error,
372     NVME_LOG_ERROR,                     NULL,   "Drive Error Log",
373     print_log_error,                    0);
374 NVME_LOGPAGE(health,
375     NVME_LOG_HEALTH_INFORMATION,        NULL,   "Health/SMART Data",
376     print_log_health,                   sizeof(struct nvme_health_information_page));
377 NVME_LOGPAGE(fw,
378     NVME_LOG_FIRMWARE_SLOT,             NULL,   "Firmware Information",
379     print_log_firmware,                 sizeof(struct nvme_firmware_page));
380
381 static void
382 logpage_help(void)
383 {
384         const struct logpage_function   *f;
385         const char                      *v;
386
387         fprintf(stderr, "\n");
388         fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
389         fprintf(stderr, "-------- ---------- ----------\n");
390         SLIST_FOREACH(f, &logpages, link) {
391                 v = f->vendor == NULL ? "-" : f->vendor;
392                 fprintf(stderr, "0x%02x     %-10s %s\n", f->log_page, v, f->name);
393         }
394
395         exit(1);
396 }
397
398 static void
399 logpage(const struct cmd *f, int argc, char *argv[])
400 {
401         int                             fd;
402         char                            *path;
403         uint32_t                        nsid, size;
404         void                            *buf;
405         const struct logpage_function   *lpf;
406         struct nvme_controller_data     cdata;
407         print_fn_t                      print_fn;
408         uint8_t                         ns_smart;
409
410         if (arg_parse(argc, argv, f))
411                 return;
412         if (opt.hex && opt.binary) {
413                 fprintf(stderr,
414                     "Can't specify both binary and hex\n");
415                 arg_help(argc, argv, f);
416         }
417         if (opt.vendor != NULL && strcmp(opt.vendor, "help") == 0)
418                 logpage_help();
419         if (opt.page == NONE) {
420                 fprintf(stderr, "Missing page_id (-p).\n");
421                 arg_help(argc, argv, f);
422         }
423         open_dev(opt.dev, &fd, 1, 1);
424         get_nsid(fd, &path, &nsid);
425         if (nsid == 0) {
426                 nsid = NVME_GLOBAL_NAMESPACE_TAG;
427         } else {
428                 close(fd);
429                 open_dev(path, &fd, 1, 1);
430         }
431         free(path);
432
433         read_controller_data(fd, &cdata);
434
435         ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
436                 NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
437
438         /*
439          * The log page attribtues indicate whether or not the controller
440          * supports the SMART/Health information log page on a per
441          * namespace basis.
442          */
443         if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
444                 if (opt.page != NVME_LOG_HEALTH_INFORMATION)
445                         errx(1, "log page %d valid only at controller level",
446                             opt.page);
447                 if (ns_smart == 0)
448                         errx(1,
449                             "controller does not support per namespace "
450                             "smart/health information");
451         }
452
453         print_fn = print_log_hex;
454         size = DEFAULT_SIZE;
455         if (opt.binary)
456                 print_fn = print_bin;
457         if (!opt.binary && !opt.hex) {
458                 /*
459                  * See if there is a pretty print function for the specified log
460                  * page.  If one isn't found, we just revert to the default
461                  * (print_hex). If there was a vendor specified by the user, and
462                  * the page is vendor specific, don't match the print function
463                  * unless the vendors match.
464                  */
465                 SLIST_FOREACH(lpf, &logpages, link) {
466                         if (lpf->vendor != NULL && opt.vendor != NULL &&
467                             strcmp(lpf->vendor, opt.vendor) != 0)
468                                 continue;
469                         if (opt.page != lpf->log_page)
470                                 continue;
471                         print_fn = lpf->print_fn;
472                         size = lpf->size;
473                         break;
474                 }
475         }
476
477         if (opt.page == NVME_LOG_ERROR) {
478                 size = sizeof(struct nvme_error_information_entry);
479                 size *= (cdata.elpe + 1);
480         }
481
482         /* Read the log page */
483         buf = get_log_buffer(size);
484         read_logpage(fd, opt.page, nsid, buf, size);
485         print_fn(&cdata, buf, size);
486
487         close(fd);
488         exit(0);
489 }