2 * Copyright (c) 2013 EMC Corp.
5 * Copyright (C) 2012-2013 Intel Corporation
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/ioccom.h>
46 #include "nvmecontrol.h"
48 #define DEFAULT_SIZE (4096)
49 #define MAX_FW_SLOTS (7)
51 typedef void (*print_fn_t
)(void *buf
, uint32_t size
);
54 get_log_buffer(uint32_t size
)
58 if ((buf
= malloc(size
)) == NULL
)
59 errx(1, "unable to malloc %u bytes", size
);
66 read_logpage(int fd
, uint8_t log_page
, int nsid
, void *payload
,
67 uint32_t payload_size
)
69 struct nvme_pt_command pt
;
71 memset(&pt
, 0, sizeof(pt
));
72 pt
.cmd
.opc
= NVME_OPC_GET_LOG_PAGE
;
74 pt
.cmd
.cdw10
= ((payload_size
/sizeof(uint32_t)) - 1) << 16;
75 pt
.cmd
.cdw10
|= log_page
;
77 pt
.len
= payload_size
;
80 if (ioctl(fd
, NVME_PASSTHROUGH_CMD
, &pt
) < 0)
81 err(1, "get log page request failed");
83 if (nvme_completion_is_error(&pt
.cpl
))
84 errx(1, "get log page request returned error");
88 print_log_error(void *buf
, uint32_t size
)
91 struct nvme_error_information_entry
*entry
= buf
;
92 struct nvme_status
*status
;
94 printf("Error Information Log\n");
95 printf("=====================\n");
97 if (entry
->error_count
== 0) {
98 printf("No error entries found\n");
102 nentries
= size
/sizeof(struct nvme_error_information_entry
);
103 for (i
= 0; i
< nentries
; i
++, entry
++) {
104 if (entry
->error_count
== 0)
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
);
128 print_log_health(void *buf
, uint32_t size __unused
)
130 struct nvme_health_information_page
*health
= buf
;
132 printf("SMART/Health Information Log\n");
133 printf("============================\n");
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",
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
);
159 * TODO: These are pretty ugly in hex. Is there a library that
160 * will convert 128-bit unsigned values to decimal?
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]);
195 print_log_firmware(void *buf
, uint32_t size __unused
)
199 struct nvme_firmware_page
*fw
= buf
;
201 printf("Firmware Slot Log\n");
202 printf("=================\n");
204 for (i
= 0; i
< MAX_FW_SLOTS
; i
++) {
205 printf("Slot %d: ", i
+ 1);
206 if (fw
->afi
.slot
== i
+ 1)
211 if (fw
->revision
[i
] == 0LLU)
214 if (isprint(*(char *)&fw
->revision
[i
]))
215 printf("[%s] %.8s\n", status
,
216 (char *)&fw
->revision
[i
]);
218 printf("[%s] %016jx\n", status
,
223 static struct logpage_function
{
227 {NVME_LOG_ERROR
, print_log_error
},
228 {NVME_LOG_HEALTH_INFORMATION
, print_log_health
},
229 {NVME_LOG_FIRMWARE_SLOT
, print_log_firmware
},
236 fprintf(stderr
, "usage:\n");
237 fprintf(stderr
, LOGPAGE_USAGE
);
242 logpage(int argc
, char *argv
[])
245 int log_page
= 0, pageflag
= false;
246 int hexflag
= false, ns_specified
;
251 struct logpage_function
*f
;
252 struct nvme_controller_data cdata
;
255 while ((ch
= getopt(argc
, argv
, "p:x")) != -1) {
258 /* TODO: Add human-readable ASCII page IDs */
259 log_page
= strtol(optarg
, &p
, 0);
260 if (p
!= NULL
&& *p
!= '\0') {
262 "\"%s\" not valid log page id.\n",
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)) {
270 "\"%s\" not valid log page id.\n",
283 printf("Missing page_id (-p).\n");
287 /* Check that a controller and/or namespace was specified. */
291 if (strstr(argv
[optind
], NVME_NS_PREFIX
) != NULL
) {
293 parse_ns_str(argv
[optind
], cname
, &nsid
);
294 open_dev(cname
, &fd
, 1, 1);
296 ns_specified
= false;
297 nsid
= NVME_GLOBAL_NAMESPACE_TAG
;
298 open_dev(argv
[optind
], &fd
, 1, 1);
301 read_controller_data(fd
, &cdata
);
304 * The log page attribtues indicate whether or not the controller
305 * supports the SMART/Health information log page on a per
309 if (log_page
!= NVME_LOG_HEALTH_INFORMATION
)
310 errx(1, "log page %d valid only at controller level",
312 if (cdata
.lpa
.ns_smart
== 0)
314 "controller does not support per namespace "
315 "smart/health information");
318 print_fn
= print_hex
;
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).
326 while (f
->log_page
> 0) {
327 if (log_page
== f
->log_page
) {
335 /* Read the log page */
338 size
= sizeof(struct nvme_error_information_entry
);
339 size
*= (cdata
.elpe
+ 1);
341 case NVME_LOG_HEALTH_INFORMATION
:
342 size
= sizeof(struct nvme_health_information_page
);
344 case NVME_LOG_FIRMWARE_SLOT
:
345 size
= sizeof(struct nvme_firmware_page
);
352 buf
= get_log_buffer(size
);
353 read_logpage(fd
, log_page
, nsid
, buf
, size
);