nvmecontrol.c revision 249067
1/*-
2 * Copyright (C) 2012 Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 249067 2013-04-03 20:52:17Z jimharris $");
29
30#include <sys/param.h>
31#include <sys/ioccom.h>
32#include <sys/stat.h>
33
34#include <dev/nvme/nvme.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#define DEVLIST_USAGE							       \
48"       nvmecontrol devlist\n"
49
50#define IDENTIFY_USAGE							       \
51"       nvmecontrol identify <controller id|namespace id>\n"
52
53#define PERFTEST_USAGE							       \
54"       nvmecontrol perftest <-n num_threads> <-o read|write>\n"	       \
55"                            <-s size_in_bytes> <-t time_in_seconds>\n"	       \
56"                            <-i intr|wait> [-f refthread] [-p]\n"	       \
57"                            <namespace id>\n"
58
59#define RESET_USAGE							       \
60"       nvmecontrol reset <controller id>\n"
61
62static void perftest_usage(void);
63
64static void
65usage(void)
66{
67	fprintf(stderr, "usage:\n");
68	fprintf(stderr, DEVLIST_USAGE);
69	fprintf(stderr, IDENTIFY_USAGE);
70	fprintf(stderr, RESET_USAGE);
71	fprintf(stderr, PERFTEST_USAGE);
72	exit(EX_USAGE);
73}
74
75static void
76print_controller_hex(struct nvme_controller_data *cdata, uint32_t length)
77{
78	uint32_t	*p;
79	uint32_t	i, j;
80
81	p = (uint32_t *)cdata;
82	length /= sizeof(uint32_t);
83
84	for (i = 0; i < length; i+=8) {
85		printf("%03x: ", i*4);
86		for (j = 0; j < 8; j++)
87			printf("%08x ", p[i+j]);
88		printf("\n");
89	}
90
91	printf("\n");
92}
93
94static void
95print_controller(struct nvme_controller_data *cdata)
96{
97	printf("Controller Capabilities/Features\n");
98	printf("================================\n");
99	printf("Vendor ID:                  %04x\n", cdata->vid);
100	printf("Subsystem Vendor ID:        %04x\n", cdata->ssvid);
101	printf("Serial Number:              %s\n", cdata->sn);
102	printf("Model Number:               %s\n", cdata->mn);
103	printf("Firmware Version:           %s\n", cdata->fr);
104	printf("Recommended Arb Burst:      %d\n", cdata->rab);
105	printf("IEEE OUI Identifier:        %02x %02x %02x\n",
106		cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
107	printf("Multi-Interface Cap:        %02x\n", cdata->mic);
108	/* TODO: Use CAP.MPSMIN to determine true memory page size. */
109	printf("Max Data Transfer Size:     ");
110	if (cdata->mdts == 0)
111		printf("Unlimited\n");
112	else
113		printf("%d\n", PAGE_SIZE * (1 << cdata->mdts));
114	printf("\n");
115
116	printf("Admin Command Set Attributes\n");
117	printf("============================\n");
118	printf("Security Send/Receive:       %s\n",
119		cdata->oacs.security ? "Supported" : "Not Supported");
120	printf("Format NVM:                  %s\n",
121		cdata->oacs.format ? "Supported" : "Not Supported");
122	printf("Firmware Activate/Download:  %s\n",
123		cdata->oacs.firmware ? "Supported" : "Not Supported");
124	printf("Abort Command Limit:         %d\n", cdata->acl+1);
125	printf("Async Event Request Limit:   %d\n", cdata->aerl+1);
126	printf("Number of Firmware Slots:    ");
127	if (cdata->oacs.firmware != 0)
128		printf("%d\n", cdata->frmw.num_slots);
129	else
130		printf("N/A\n");
131	printf("Firmware Slot 1 Read-Only:   ");
132	if (cdata->oacs.firmware != 0)
133		printf("%s\n", cdata->frmw.slot1_ro ? "Yes" : "No");
134	else
135		printf("N/A\n");
136	printf("Per-Namespace SMART Log:     %s\n",
137		cdata->lpa.ns_smart ? "Yes" : "No");
138	printf("Error Log Page Entries:      %d\n", cdata->elpe+1);
139	printf("Number of Power States:      %d\n", cdata->npss+1);
140	printf("\n");
141
142	printf("NVM Command Set Attributes\n");
143	printf("==========================\n");
144	printf("Submission Queue Entry Size\n");
145	printf("  Max:                       %d\n", 1 << cdata->sqes.max);
146	printf("  Min:                       %d\n", 1 << cdata->sqes.min);
147	printf("Completion Queue Entry Size\n");
148	printf("  Max:                       %d\n", 1 << cdata->cqes.max);
149	printf("  Min:                       %d\n", 1 << cdata->cqes.min);
150	printf("Number of Namespaces:        %d\n", cdata->nn);
151	printf("Compare Command:             %s\n",
152		cdata->oncs.compare ? "Supported" : "Not Supported");
153	printf("Write Uncorrectable Command: %s\n",
154		cdata->oncs.write_unc ? "Supported" : "Not Supported");
155	printf("Dataset Management Command:  %s\n",
156		cdata->oncs.dsm ? "Supported" : "Not Supported");
157	printf("Volatile Write Cache:        %s\n",
158		cdata->vwc.present ? "Present" : "Not Present");
159}
160
161static void
162print_namespace_hex(struct nvme_namespace_data *nsdata, uint32_t length)
163{
164	uint32_t	*p;
165	uint32_t	i, j;
166
167	p = (uint32_t *)nsdata;
168	length /= sizeof(uint32_t);
169
170	for (i = 0; i < length; i+=8) {
171		printf("%03x: ", i*4);
172		for (j = 0; j < 8; j++)
173			printf("%08x ", p[i+j]);
174		printf("\n");
175	}
176
177	printf("\n");
178}
179
180static void
181print_namespace(struct nvme_namespace_data *nsdata)
182{
183	uint32_t	i;
184
185	printf("Size (in LBAs):              %lld (%lldM)\n",
186		(long long)nsdata->nsze,
187		(long long)nsdata->nsze / 1024 / 1024);
188	printf("Capacity (in LBAs):          %lld (%lldM)\n",
189		(long long)nsdata->ncap,
190		(long long)nsdata->ncap / 1024 / 1024);
191	printf("Utilization (in LBAs):       %lld (%lldM)\n",
192		(long long)nsdata->nuse,
193		(long long)nsdata->nuse / 1024 / 1024);
194	printf("Thin Provisioning:           %s\n",
195		nsdata->nsfeat.thin_prov ? "Supported" : "Not Supported");
196	printf("Number of LBA Formats:       %d\n", nsdata->nlbaf+1);
197	printf("Current LBA Format:          LBA Format #%d\n",
198		nsdata->flbas.format);
199	for (i = 0; i <= nsdata->nlbaf; i++) {
200		printf("LBA Format #%d:\n", i);
201		printf("  LBA Data Size:             %d\n",
202			1 << nsdata->lbaf[i].lbads);
203	}
204}
205
206static uint32_t
207ns_get_sector_size(struct nvme_namespace_data *nsdata)
208{
209
210	return (1 << nsdata->lbaf[0].lbads);
211}
212
213
214static void
215devlist(int argc, char *argv[])
216{
217	struct nvme_controller_data	cdata;
218	struct nvme_namespace_data	nsdata;
219	struct stat			devstat;
220	char				name[64], path[64];
221	uint32_t			i;
222	int				ch, ctrlr, exit_code, fd, found;
223
224	exit_code = EX_OK;
225
226	while ((ch = getopt(argc, argv, "")) != -1) {
227		switch ((char)ch) {
228		default:
229			usage();
230		}
231	}
232
233	ctrlr = -1;
234	found = 0;
235
236	while (1) {
237		ctrlr++;
238		sprintf(name, "nvme%d", ctrlr);
239		sprintf(path, "/dev/%s", name);
240
241		if (stat(path, &devstat) != 0)
242			break;
243
244		found++;
245
246		fd = open(path, O_RDWR);
247		if (fd < 0) {
248			printf("Could not open %s. errno=%d (%s)\n", path,
249			    errno, strerror(errno));
250			exit_code = EX_NOPERM;
251			continue;
252		}
253
254		if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) < 0) {
255			printf("Identify request to %s failed. errno=%d (%s)\n",
256			    path, errno, strerror(errno));
257			exit_code = EX_IOERR;
258			continue;
259		}
260
261		printf("%6s: %s\n", name, cdata.mn);
262
263		for (i = 0; i < cdata.nn; i++) {
264			sprintf(name, "nvme%dns%d", ctrlr, i+1);
265			sprintf(path, "/dev/%s", name);
266
267			fd = open(path, O_RDWR);
268			if (fd < 0) {
269				printf("Could not open %s. errno=%d (%s)\n",
270				    path, errno, strerror(errno));
271				exit_code = EX_NOPERM;
272				continue;
273			}
274			if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) < 0) {
275				printf("Identify request to %s failed. "
276				    "errno=%d (%s)\n", path, errno,
277				    strerror(errno));
278				exit_code = EX_IOERR;
279				continue;
280			}
281			printf("  %10s (%lldGB)\n",
282				name,
283				nsdata.nsze *
284				(long long)ns_get_sector_size(&nsdata) /
285				1024 / 1024 / 1024);
286		}
287	}
288
289	if (found == 0)
290		printf("No NVMe controllers found.\n");
291
292	exit(exit_code);
293}
294
295static void
296identify_ctrlr(int argc, char *argv[])
297{
298	struct nvme_controller_data	cdata;
299	struct stat			devstat;
300	char				path[64];
301	int				ch, fd, hexflag = 0, hexlength;
302	int				verboseflag = 0;
303
304	while ((ch = getopt(argc, argv, "vx")) != -1) {
305		switch ((char)ch) {
306		case 'v':
307			verboseflag = 1;
308			break;
309		case 'x':
310			hexflag = 1;
311			break;
312		default:
313			usage();
314		}
315	}
316
317	sprintf(path, "/dev/%s", argv[optind]);
318
319	if (stat(path, &devstat) < 0) {
320		printf("Invalid device node %s. errno=%d (%s)\n", path, errno,
321		    strerror(errno));
322		exit(EX_IOERR);
323	}
324
325	fd = open(path, O_RDWR);
326	if (fd < 0) {
327		printf("Could not open %s. errno=%d (%s)\n", path, errno,
328		    strerror(errno));
329		exit(EX_NOPERM);
330	}
331
332	if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) < 0) {
333		printf("Identify request to %s failed. errno=%d (%s)\n", path,
334		    errno, strerror(errno));
335		exit(EX_IOERR);
336	}
337
338	if (hexflag == 1) {
339		if (verboseflag == 1)
340			hexlength = sizeof(struct nvme_controller_data);
341		else
342			hexlength = offsetof(struct nvme_controller_data,
343			    reserved5);
344		print_controller_hex(&cdata, hexlength);
345		exit(EX_OK);
346	}
347
348	if (verboseflag == 1) {
349		printf("-v not currently supported without -x.\n");
350		usage();
351	}
352
353	print_controller(&cdata);
354	exit(EX_OK);
355}
356
357static void
358identify_ns(int argc, char *argv[])
359{
360	struct nvme_namespace_data	nsdata;
361	struct stat			devstat;
362	char				path[64];
363	int				ch, fd, hexflag = 0, hexlength;
364	int				verboseflag = 0;
365
366	while ((ch = getopt(argc, argv, "vx")) != -1) {
367		switch ((char)ch) {
368		case 'v':
369			verboseflag = 1;
370			break;
371		case 'x':
372			hexflag = 1;
373			break;
374		default:
375			usage();
376		}
377	}
378
379	sprintf(path, "/dev/%s", argv[optind]);
380
381	if (stat(path, &devstat) < 0) {
382		printf("Invalid device node %s. errno=%d (%s)\n", path, errno,
383		    strerror(errno));
384		exit(EX_IOERR);
385	}
386
387	fd = open(path, O_RDWR);
388	if (fd < 0) {
389		printf("Could not open %s. errno=%d (%s)\n", path, errno,
390		    strerror(errno));
391		exit(EX_NOPERM);
392	}
393
394	if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) < 0) {
395		printf("Identify request to %s failed. errno=%d (%s)\n", path,
396		    errno, strerror(errno));
397		exit(EX_IOERR);
398	}
399
400	if (hexflag == 1) {
401		if (verboseflag == 1)
402			hexlength = sizeof(struct nvme_namespace_data);
403		else
404			hexlength = offsetof(struct nvme_namespace_data,
405			    reserved6);
406		print_namespace_hex(&nsdata, hexlength);
407		exit(EX_OK);
408	}
409
410	if (verboseflag == 1) {
411		printf("-v not currently supported without -x.\n");
412		usage();
413	}
414
415	print_namespace(&nsdata);
416	exit(EX_OK);
417}
418
419static void
420identify(int argc, char *argv[])
421{
422	char	*target;
423
424	if (argc < 2)
425		usage();
426
427	while (getopt(argc, argv, "vx") != -1) ;
428
429	target = argv[optind];
430
431	/* Specified device node must have "nvme" in it. */
432	if (strstr(argv[optind], "nvme") == NULL) {
433		printf("Invalid device node '%s'.\n", argv[optind]);
434		exit(EX_IOERR);
435	}
436
437	optreset = 1;
438	optind = 1;
439
440	/*
441	 * If device node contains "ns", we consider it a namespace,
442	 *  otherwise, consider it a controller.
443	 */
444	if (strstr(target, "ns") == NULL)
445		identify_ctrlr(argc, argv);
446	else
447		identify_ns(argc, argv);
448}
449
450static void
451print_perftest(struct nvme_io_test *io_test, bool perthread)
452{
453	uint32_t i, io_completed = 0, iops, mbps;
454
455	for (i = 0; i < io_test->num_threads; i++)
456		io_completed += io_test->io_completed[i];
457
458	iops = io_completed/io_test->time;
459	mbps = iops * io_test->size / (1024*1024);
460
461	printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7d MB/s: %4d\n",
462	    io_test->num_threads, io_test->size,
463	    io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
464	    io_test->time, iops, mbps);
465
466	if (perthread)
467		for (i = 0; i < io_test->num_threads; i++)
468			printf("\t%3d: %8d IO/s\n", i,
469			    io_test->io_completed[i]/io_test->time);
470
471	exit(1);
472}
473
474static void
475perftest_usage(void)
476{
477	fprintf(stderr, "usage:\n");
478	fprintf(stderr, PERFTEST_USAGE);
479	exit(EX_USAGE);
480}
481
482static void
483perftest(int argc, char *argv[])
484{
485	struct nvme_io_test		io_test;
486	int				fd;
487	char				ch;
488	char				*p;
489	const char			*name;
490	char				path[64];
491	u_long				ioctl_cmd = NVME_IO_TEST;
492	bool				nflag, oflag, sflag, tflag;
493	int				perthread = 0;
494
495	nflag = oflag = sflag = tflag = false;
496	name = NULL;
497
498	memset(&io_test, 0, sizeof(io_test));
499
500	while ((ch = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
501		switch (ch) {
502		case 'f':
503			if (!strcmp(optarg, "refthread"))
504				io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
505			break;
506		case 'i':
507			if (!strcmp(optarg, "bio") ||
508			    !strcmp(optarg, "wait"))
509				ioctl_cmd = NVME_BIO_TEST;
510			else if (!strcmp(optarg, "io") ||
511				 !strcmp(optarg, "intr"))
512				ioctl_cmd = NVME_IO_TEST;
513			break;
514		case 'n':
515			nflag = true;
516			io_test.num_threads = strtoul(optarg, &p, 0);
517			if (p != NULL && *p != '\0') {
518				fprintf(stderr,
519				    "\"%s\" not valid number of threads.\n",
520				    optarg);
521				perftest_usage();
522			} else if (io_test.num_threads == 0 ||
523				   io_test.num_threads > 128) {
524				fprintf(stderr,
525				    "\"%s\" not valid number of threads.\n",
526				    optarg);
527				perftest_usage();
528			}
529			break;
530		case 'o':
531			oflag = true;
532			if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
533				io_test.opc = NVME_OPC_READ;
534			else if (!strcmp(optarg, "write") ||
535				 !strcmp(optarg, "WRITE"))
536				io_test.opc = NVME_OPC_WRITE;
537			else {
538				fprintf(stderr, "\"%s\" not valid opcode.\n",
539				    optarg);
540				perftest_usage();
541			}
542			break;
543		case 'p':
544			perthread = 1;
545			break;
546		case 's':
547			sflag = true;
548			io_test.size = strtoul(optarg, &p, 0);
549			if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
550				// do nothing
551			} else if (toupper(*p) == 'K') {
552				io_test.size *= 1024;
553			} else if (toupper(*p) == 'M') {
554				io_test.size *= 1024 * 1024;
555			} else {
556				fprintf(stderr, "\"%s\" not valid size.\n",
557				    optarg);
558				perftest_usage();
559			}
560			break;
561		case 't':
562			tflag = true;
563			io_test.time = strtoul(optarg, &p, 0);
564			if (p != NULL && *p != '\0') {
565				fprintf(stderr,
566				    "\"%s\" not valid time duration.\n",
567				    optarg);
568				perftest_usage();
569			}
570			break;
571		}
572	}
573
574	name = argv[optind];
575
576	if (!nflag || !oflag || !sflag || !tflag || name == NULL)
577		perftest_usage();
578
579	sprintf(path, "/dev/%s", name);
580
581	fd = open(path, O_RDWR);
582	if (fd < 0) {
583		fprintf(stderr, "%s not valid device. errno=%d (%s)\n", path,
584		    errno, strerror(errno));
585		perftest_usage();
586	}
587
588	if (ioctl(fd, ioctl_cmd, &io_test) < 0) {
589		fprintf(stderr, "NVME_IO_TEST failed. errno=%d (%s)\n", errno,
590		    strerror(errno));
591		exit(EX_IOERR);
592	}
593
594	print_perftest(&io_test, perthread);
595	exit(EX_OK);
596}
597
598static void
599reset_ctrlr(int argc, char *argv[])
600{
601	struct stat			devstat;
602	char				path[64];
603	int				ch, fd;
604
605	while ((ch = getopt(argc, argv, "")) != -1) {
606		switch ((char)ch) {
607		default:
608			usage();
609		}
610	}
611
612	sprintf(path, "/dev/%s", argv[optind]);
613
614	if (stat(path, &devstat) < 0) {
615		printf("Invalid device node %s. errno=%d (%s)\n", path, errno,
616		    strerror(errno));
617		exit(EX_IOERR);
618	}
619
620	fd = open(path, O_RDWR);
621	if (fd < 0) {
622		printf("Could not open %s. errno=%d (%s)\n", path, errno,
623		    strerror(errno));
624		exit(EX_NOPERM);
625	}
626
627	if (ioctl(fd, NVME_RESET_CONTROLLER) < 0) {
628		printf("Reset request to %s failed. errno=%d (%s)\n", path,
629		    errno, strerror(errno));
630		exit(EX_IOERR);
631	}
632
633	exit(EX_OK);
634}
635
636int
637main(int argc, char *argv[])
638{
639
640	if (argc < 2)
641		usage();
642
643	if (strcmp(argv[1], "devlist") == 0)
644		devlist(argc-1, &argv[1]);
645	else if (strcmp(argv[1], "identify") == 0)
646		identify(argc-1, &argv[1]);
647	else if (strcmp(argv[1], "perftest") == 0)
648		perftest(argc-1, &argv[1]);
649	else if (strcmp(argv[1], "reset") == 0)
650		reset_ctrlr(argc-1, &argv[1]);
651
652	usage();
653
654	return (0);
655}
656