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