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