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