]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/logpage.c
MFC r308849 (by imp):
[FreeBSD/FreeBSD.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 #include <sys/endian.h>
46
47 #if _BYTE_ORDER != _LITTLE_ENDIAN
48 #error "Code only works on little endian machines"
49 #endif
50
51 #include "nvmecontrol.h"
52
53 #define DEFAULT_SIZE    (4096)
54 #define MAX_FW_SLOTS    (7)
55
56 typedef void (*print_fn_t)(void *buf, uint32_t size);
57
58
59 /*
60  * 128-bit integer augments to standard values
61  */
62 #define UINT128_DIG     39
63 typedef __uint128_t uint128_t;
64
65 static inline uint128_t
66 to128(void *p)
67 {
68         return *(uint128_t *)p;
69 }
70
71 static char *
72 uint128_to_str(uint128_t u, char *buf, size_t buflen)
73 {
74         char *end = buf + buflen - 1;
75
76         *end-- = '\0';
77         if (u == 0)
78                 *end-- = '0';
79         while (u && end >= buf) {
80                 *end-- = u % 10 + '0';
81                 u /= 10;
82         }
83         end++;
84         if (u != 0)
85                 return NULL;
86
87         return end;
88 }
89
90 static void *
91 get_log_buffer(uint32_t size)
92 {
93         void    *buf;
94
95         if ((buf = malloc(size)) == NULL)
96                 errx(1, "unable to malloc %u bytes", size);
97
98         memset(buf, 0, size);
99         return (buf);
100 }
101
102 void
103 read_logpage(int fd, uint8_t log_page, int nsid, void *payload, 
104     uint32_t payload_size)
105 {
106         struct nvme_pt_command  pt;
107
108         memset(&pt, 0, sizeof(pt));
109         pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
110         pt.cmd.nsid = nsid;
111         pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
112         pt.cmd.cdw10 |= log_page;
113         pt.buf = payload;
114         pt.len = payload_size;
115         pt.is_read = 1;
116
117         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
118                 err(1, "get log page request failed");
119
120         if (nvme_completion_is_error(&pt.cpl))
121                 errx(1, "get log page request returned error");
122 }
123
124 static void
125 print_log_error(void *buf, uint32_t size)
126 {
127         int                                     i, nentries;
128         struct nvme_error_information_entry     *entry = buf;
129         struct nvme_status                      *status;
130
131         printf("Error Information Log\n");
132         printf("=====================\n");
133
134         if (entry->error_count == 0) {
135                 printf("No error entries found\n");
136                 return;
137         }
138
139         nentries = size/sizeof(struct nvme_error_information_entry);
140         for (i = 0; i < nentries; i++, entry++) {
141                 if (entry->error_count == 0)
142                         break;
143
144                 status = &entry->status;
145                 printf("Entry %02d\n", i + 1);
146                 printf("=========\n");
147                 printf(" Error count:          %ju\n", entry->error_count);
148                 printf(" Submission queue ID:  %u\n", entry->sqid);
149                 printf(" Command ID:           %u\n", entry->cid);
150                 /* TODO: Export nvme_status_string structures from kernel? */
151                 printf(" Status:\n");
152                 printf("  Phase tag:           %d\n", status->p);
153                 printf("  Status code:         %d\n", status->sc);
154                 printf("  Status code type:    %d\n", status->sct);
155                 printf("  More:                %d\n", status->m);
156                 printf("  DNR:                 %d\n", status->dnr);
157                 printf(" Error location:       %u\n", entry->error_location);
158                 printf(" LBA:                  %ju\n", entry->lba);
159                 printf(" Namespace ID:         %u\n", entry->nsid);
160                 printf(" Vendor specific info: %u\n", entry->vendor_specific);
161         }
162 }
163
164 static void
165 print_temp(uint16_t t)
166 {
167         printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
168 }
169
170
171 static void
172 print_log_health(void *buf, uint32_t size __unused)
173 {
174         struct nvme_health_information_page *health = buf;
175         char cbuf[UINT128_DIG + 1];
176         int i;
177
178         printf("SMART/Health Information Log\n");
179         printf("============================\n");
180
181         printf("Critical Warning State:         0x%02x\n",
182             health->critical_warning.raw);
183         printf(" Available spare:               %d\n",
184             health->critical_warning.bits.available_spare);
185         printf(" Temperature:                   %d\n",
186             health->critical_warning.bits.temperature);
187         printf(" Device reliability:            %d\n",
188             health->critical_warning.bits.device_reliability);
189         printf(" Read only:                     %d\n",
190             health->critical_warning.bits.read_only);
191         printf(" Volatile memory backup:        %d\n",
192             health->critical_warning.bits.volatile_memory_backup);
193         printf("Temperature:                    ");
194         print_temp(health->temperature);
195         printf("Available spare:                %u\n",
196             health->available_spare);
197         printf("Available spare threshold:      %u\n",
198             health->available_spare_threshold);
199         printf("Percentage used:                %u\n",
200             health->percentage_used);
201
202         printf("Data units (512,000 byte) read: %s\n",
203             uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
204         printf("Data units written:             %s\n",
205             uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
206         printf("Host read commands:             %s\n",
207             uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
208         printf("Host write commands:            %s\n",
209             uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
210         printf("Controller busy time (minutes): %s\n",
211             uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
212         printf("Power cycles:                   %s\n",
213             uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
214         printf("Power on hours:                 %s\n",
215             uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
216         printf("Unsafe shutdowns:               %s\n",
217             uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
218         printf("Media errors:                   %s\n",
219             uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
220         printf("No. error info log entries:     %s\n",
221             uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
222
223         printf("Warning Temp Composite Time:    %d\n", health->warning_temp_time);
224         printf("Error Temp Composite Time:      %d\n", health->error_temp_time);
225         for (i = 0; i < 7; i++) {
226                 if (health->temp_sensor[i] == 0)
227                         continue;
228                 printf("Temperature Sensor %d:           ", i + 1);
229                 print_temp(health->temp_sensor[i]);
230         }
231 }
232
233 static void
234 print_log_firmware(void *buf, uint32_t size __unused)
235 {
236         int                             i;
237         const char                      *status;
238         struct nvme_firmware_page       *fw = buf;
239
240         printf("Firmware Slot Log\n");
241         printf("=================\n");
242
243         for (i = 0; i < MAX_FW_SLOTS; i++) {
244                 printf("Slot %d: ", i + 1);
245                 if (fw->afi.slot == i + 1)
246                         status = "  Active";
247                 else
248                         status = "Inactive";
249
250                 if (fw->revision[i] == 0LLU)
251                         printf("Empty\n");
252                 else
253                         if (isprint(*(char *)&fw->revision[i]))
254                                 printf("[%s] %.8s\n", status,
255                                     (char *)&fw->revision[i]);
256                         else
257                                 printf("[%s] %016jx\n", status,
258                                     fw->revision[i]);
259         }
260 }
261
262 static struct logpage_function {
263         uint8_t         log_page;
264         print_fn_t      print_fn;
265         size_t          size;
266 } logfuncs[] = {
267         {NVME_LOG_ERROR,                print_log_error,
268          0},
269         {NVME_LOG_HEALTH_INFORMATION,   print_log_health,
270          sizeof(struct nvme_health_information_page)},
271         {NVME_LOG_FIRMWARE_SLOT,        print_log_firmware,
272          sizeof(struct nvme_firmware_page)},
273         {0,                             NULL,
274          0},
275 };
276
277 static void
278 logpage_usage(void)
279 {
280         fprintf(stderr, "usage:\n");
281         fprintf(stderr, LOGPAGE_USAGE);
282         exit(1);
283 }
284
285 void
286 logpage(int argc, char *argv[])
287 {
288         int                             fd, nsid;
289         int                             log_page = 0, pageflag = false;
290         int                             hexflag = false, ns_specified;
291         char                            ch, *p;
292         char                            cname[64];
293         uint32_t                        size;
294         void                            *buf;
295         struct logpage_function         *f;
296         struct nvme_controller_data     cdata;
297         print_fn_t                      print_fn;
298
299         while ((ch = getopt(argc, argv, "p:x")) != -1) {
300                 switch (ch) {
301                 case 'p':
302                         /* TODO: Add human-readable ASCII page IDs */
303                         log_page = strtol(optarg, &p, 0);
304                         if (p != NULL && *p != '\0') {
305                                 fprintf(stderr,
306                                     "\"%s\" not valid log page id.\n",
307                                     optarg);
308                                 logpage_usage();
309                         /* TODO: Define valid log page id ranges in nvme.h? */
310                         } else if (log_page == 0 ||
311                                    (log_page >= 0x04 && log_page <= 0x7F) ||
312                                    (log_page >= 0x80 && log_page <= 0xBF)) {
313                                 fprintf(stderr,
314                                     "\"%s\" not valid log page id.\n",
315                                     optarg);
316                                 logpage_usage();
317                         }
318                         pageflag = true;
319                         break;
320                 case 'x':
321                         hexflag = true;
322                         break;
323                 }
324         }
325
326         if (!pageflag) {
327                 printf("Missing page_id (-p).\n");
328                 logpage_usage();
329         }
330
331         /* Check that a controller and/or namespace was specified. */
332         if (optind >= argc)
333                 logpage_usage();
334
335         if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
336                 ns_specified = true;
337                 parse_ns_str(argv[optind], cname, &nsid);
338                 open_dev(cname, &fd, 1, 1);
339         } else {
340                 ns_specified = false;
341                 nsid = NVME_GLOBAL_NAMESPACE_TAG;
342                 open_dev(argv[optind], &fd, 1, 1);
343         }
344
345         read_controller_data(fd, &cdata);
346
347         /*
348          * The log page attribtues indicate whether or not the controller
349          * supports the SMART/Health information log page on a per
350          * namespace basis.
351          */
352         if (ns_specified) {
353                 if (log_page != NVME_LOG_HEALTH_INFORMATION)
354                         errx(1, "log page %d valid only at controller level",
355                             log_page);
356                 if (cdata.lpa.ns_smart == 0)
357                         errx(1,
358                             "controller does not support per namespace "
359                             "smart/health information");
360         }
361
362         print_fn = print_hex;
363         size = DEFAULT_SIZE;
364         if (!hexflag) {
365                 /*
366                  * See if there is a pretty print function for the
367                  *  specified log page.  If one isn't found, we
368                  *  just revert to the default (print_hex).
369                  */
370                 f = logfuncs;
371                 while (f->log_page > 0) {
372                         if (log_page == f->log_page) {
373                                 print_fn = f->print_fn;
374                                 size = f->size;
375                                 break;
376                         }
377                         f++;
378                 }
379         }
380
381         if (log_page == NVME_LOG_ERROR) {
382                 size = sizeof(struct nvme_error_information_entry);
383                 size *= (cdata.elpe + 1);
384         }
385
386         /* Read the log page */
387         buf = get_log_buffer(size);
388         read_logpage(fd, log_page, nsid, buf, size);
389         print_fn(buf, size);
390
391         close(fd);
392         exit(0);
393 }