nvmecontrol.c revision 240620
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 240620 2012-09-17 21:26:55Z 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
59static void perftest_usage(void);
60
61static void
62usage(void)
63{
64	fprintf(stderr, "usage:\n");
65	fprintf(stderr, DEVLIST_USAGE);
66	fprintf(stderr, IDENTIFY_USAGE);
67	fprintf(stderr, PERFTEST_USAGE);
68	exit(EX_USAGE);
69}
70
71static void
72print_controller_hex(struct nvme_controller_data *cdata, uint32_t length)
73{
74	uint32_t	*p;
75	uint32_t	i, j;
76
77	p = (uint32_t *)cdata;
78	length /= sizeof(uint32_t);
79
80	for (i = 0; i < length; i+=8) {
81		printf("%03x: ", i*4);
82		for (j = 0; j < 8; j++)
83			printf("%08x ", p[i+j]);
84		printf("\n");
85	}
86
87	printf("\n");
88}
89
90static void
91print_controller(struct nvme_controller_data *cdata)
92{
93	printf("Controller Capabilities/Features\n");
94	printf("================================\n");
95	printf("Vendor ID:                  %04x\n", cdata->vid);
96	printf("Subsystem Vendor ID:        %04x\n", cdata->ssvid);
97	printf("Serial Number:              %s\n", cdata->sn);
98	printf("Model Number:               %s\n", cdata->mn);
99	printf("Firmware Version:           %s\n", cdata->fr);
100	printf("Recommended Arb Burst:      %d\n", cdata->rab);
101	printf("IEEE OUI Identifier:        %02x %02x %02x\n",
102		cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
103	printf("Multi-Interface Cap:        %02x\n", cdata->mic);
104	/* TODO: Use CAP.MPSMIN to determine true memory page size. */
105	printf("Max Data Transfer Size:     ");
106	if (cdata->mdts == 0)
107		printf("Unlimited\n");
108	else
109		printf("%d\n", PAGE_SIZE * (1 << cdata->mdts));
110	printf("\n");
111
112	printf("Admin Command Set Attributes\n");
113	printf("============================\n");
114	printf("Security Send/Receive:       %s\n",
115		cdata->oacs.security ? "Supported" : "Not Supported");
116	printf("Format NVM:                  %s\n",
117		cdata->oacs.format ? "Supported" : "Not Supported");
118	printf("Firmware Activate/Download:  %s\n",
119		cdata->oacs.firmware ? "Supported" : "Not Supported");
120	printf("Abort Command Limit:         %d\n", cdata->acl+1);
121	printf("Async Event Request Limit:   %d\n", cdata->aerl+1);
122	printf("Number of Firmware Slots:    ");
123	if (cdata->oacs.firmware != 0)
124		printf("%d\n", cdata->frmw.num_slots);
125	else
126		printf("N/A\n");
127	printf("Firmware Slot 1 Read-Only:   ");
128	if (cdata->oacs.firmware != 0)
129		printf("%s\n", cdata->frmw.slot1_ro ? "Yes" : "No");
130	else
131		printf("N/A\n");
132	printf("Per-Namespace SMART Log:     %s\n",
133		cdata->lpa.ns_smart ? "Yes" : "No");
134	printf("Error Log Page Entries:      %d\n", cdata->elpe+1);
135	printf("Number of Power States:      %d\n", cdata->npss+1);
136	printf("\n");
137
138	printf("NVM Command Set Attributes\n");
139	printf("==========================\n");
140	printf("Submission Queue Entry Size\n");
141	printf("  Max:                       %d\n", 1 << cdata->sqes.max);
142	printf("  Min:                       %d\n", 1 << cdata->sqes.min);
143	printf("Completion Queue Entry Size\n");
144	printf("  Max:                       %d\n", 1 << cdata->cqes.max);
145	printf("  Min:                       %d\n", 1 << cdata->cqes.min);
146	printf("Number of Namespaces:        %d\n", cdata->nn);
147	printf("Compare Command:             %s\n",
148		cdata->oncs.compare ? "Supported" : "Not Supported");
149	printf("Write Uncorrectable Command: %s\n",
150		cdata->oncs.write_unc ? "Supported" : "Not Supported");
151	printf("Dataset Management Command:  %s\n",
152		cdata->oncs.dsm ? "Supported" : "Not Supported");
153	printf("Volatile Write Cache:        %s\n",
154		cdata->vwc.present ? "Present" : "Not Present");
155}
156
157static void
158print_namespace_hex(struct nvme_namespace_data *nsdata, uint32_t length)
159{
160	uint32_t	*p;
161	uint32_t	i, j;
162
163	p = (uint32_t *)nsdata;
164	length /= sizeof(uint32_t);
165
166	for (i = 0; i < length; i+=8) {
167		printf("%03x: ", i*4);
168		for (j = 0; j < 8; j++)
169			printf("%08x ", p[i+j]);
170		printf("\n");
171	}
172
173	printf("\n");
174}
175
176static void
177print_namespace(struct nvme_namespace_data *nsdata)
178{
179	uint32_t	i;
180
181	printf("Size (in LBAs):              %lld (%lldM)\n",
182		(long long)nsdata->nsze,
183		(long long)nsdata->nsze / 1024 / 1024);
184	printf("Capacity (in LBAs):          %lld (%lldM)\n",
185		(long long)nsdata->ncap,
186		(long long)nsdata->ncap / 1024 / 1024);
187	printf("Utilization (in LBAs):       %lld (%lldM)\n",
188		(long long)nsdata->nuse,
189		(long long)nsdata->nuse / 1024 / 1024);
190	printf("Thin Provisioning:           %s\n",
191		nsdata->nsfeat.thin_prov ? "Supported" : "Not Supported");
192	printf("Number of LBA Formats:       %d\n", nsdata->nlbaf+1);
193	printf("Current LBA Format:          LBA Format #%d\n",
194		nsdata->flbas.format);
195	for (i = 0; i <= nsdata->nlbaf; i++) {
196		printf("LBA Format #%d:\n", i);
197		printf("  LBA Data Size:             %d\n",
198			1 << nsdata->lbaf[i].lbads);
199	}
200}
201
202static uint32_t
203ns_get_sector_size(struct nvme_namespace_data *nsdata)
204{
205
206	return (1 << nsdata->lbaf[0].lbads);
207}
208
209
210static void
211devlist(int argc, char *argv[])
212{
213	struct nvme_controller_data	cdata;
214	struct nvme_namespace_data	nsdata;
215	struct stat			devstat;
216	char				name[64], path[64];
217	uint32_t			i;
218	int				ch, ctrlr, exit_code, fd, found;
219
220	exit_code = EX_OK;
221
222	while ((ch = getopt(argc, argv, "")) != -1) {
223		switch ((char)ch) {
224		default:
225			usage();
226		}
227	}
228
229	ctrlr = -1;
230	found = 0;
231
232	while (1) {
233		ctrlr++;
234		sprintf(name, "nvme%d", ctrlr);
235		sprintf(path, "/dev/%s", name);
236
237		if (stat(path, &devstat) != 0)
238			break;
239
240		found++;
241
242		fd = open(path, O_RDWR);
243		if (fd < 0) {
244			printf("Could not open %s.\n", path);
245			exit_code = EX_NOPERM;
246			continue;
247		}
248
249		if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) == -1) {
250			printf("ioctl to %s failed.\n", path);
251			exit_code = EX_IOERR;
252			continue;
253		}
254
255		printf("%6s: %s\n", name, cdata.mn);
256
257		for (i = 0; i < cdata.nn; i++) {
258			sprintf(name, "nvme%dns%d", ctrlr, i+1);
259			sprintf(path, "/dev/%s", name);
260
261			fd = open(path, O_RDWR);
262			if (fd < 0) {
263				printf("Could not open %s.\n", path);
264				exit_code = EX_NOPERM;
265				continue;
266			}
267			if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) == -1) {
268				printf("ioctl to %s failed.\n", path);
269				exit_code = EX_IOERR;
270				continue;
271			}
272			printf("  %10s (%lldGB)\n",
273				name,
274				nsdata.nsze *
275				(long long)ns_get_sector_size(&nsdata) /
276				1024 / 1024 / 1024);
277		}
278	}
279
280	if (found == 0)
281		printf("No NVMe controllers found.\n");
282
283	exit(exit_code);
284}
285
286static void
287identify_ctrlr(int argc, char *argv[])
288{
289	struct nvme_controller_data	cdata;
290	struct stat			devstat;
291	char				path[64];
292	int				ch, fd, hexflag = 0, hexlength;
293	int				verboseflag = 0;
294
295	while ((ch = getopt(argc, argv, "vx")) != -1) {
296		switch ((char)ch) {
297		case 'v':
298			verboseflag = 1;
299			break;
300		case 'x':
301			hexflag = 1;
302			break;
303		default:
304			usage();
305		}
306	}
307
308	sprintf(path, "/dev/%s", argv[optind]);
309
310	if (stat(path, &devstat) != 0) {
311		printf("Invalid device node '%s'.\n", path);
312		exit(EX_IOERR);
313	}
314
315	fd = open(path, O_RDWR);
316	if (fd < 0) {
317		printf("Could not open %s.\n", path);
318		exit(EX_NOPERM);
319	}
320
321	if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) == -1) {
322		printf("ioctl to %s failed.\n", path);
323		exit(EX_IOERR);
324	}
325
326	if (hexflag == 1) {
327		if (verboseflag == 1)
328			hexlength = sizeof(struct nvme_controller_data);
329		else
330			hexlength = offsetof(struct nvme_controller_data,
331			    reserved5);
332		print_controller_hex(&cdata, hexlength);
333		exit(EX_OK);
334	}
335
336	if (verboseflag == 1) {
337		printf("-v not currently supported without -x.\n");
338		usage();
339	}
340
341	print_controller(&cdata);
342	exit(EX_OK);
343}
344
345static void
346identify_ns(int argc, char *argv[])
347{
348	struct nvme_namespace_data	nsdata;
349	struct stat			devstat;
350	char				path[64];
351	int				ch, fd, hexflag = 0, hexlength;
352	int				verboseflag = 0;
353
354	while ((ch = getopt(argc, argv, "vx")) != -1) {
355		switch ((char)ch) {
356		case 'v':
357			verboseflag = 1;
358			break;
359		case 'x':
360			hexflag = 1;
361			break;
362		default:
363			usage();
364		}
365	}
366
367	sprintf(path, "/dev/%s", argv[optind]);
368
369	if (stat(path, &devstat) != 0) {
370		printf("Invalid device node '%s'.\n", path);
371		exit(EX_IOERR);
372	}
373
374	fd = open(path, O_RDWR);
375	if (fd < 0) {
376		printf("Could not open %s.\n", path);
377		exit(EX_NOPERM);
378	}
379
380	if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) == -1) {
381		printf("ioctl to %s failed.\n", path);
382		exit(EX_IOERR);
383	}
384
385	if (hexflag == 1) {
386		if (verboseflag == 1)
387			hexlength = sizeof(struct nvme_namespace_data);
388		else
389			hexlength = offsetof(struct nvme_namespace_data,
390			    reserved6);
391		print_namespace_hex(&nsdata, hexlength);
392		exit(EX_OK);
393	}
394
395	if (verboseflag == 1) {
396		printf("-v not currently supported without -x.\n");
397		usage();
398	}
399
400	print_namespace(&nsdata);
401	exit(EX_OK);
402}
403
404static void
405identify(int argc, char *argv[])
406{
407	char	*target;
408
409	if (argc < 2)
410		usage();
411
412	while (getopt(argc, argv, "vx") != -1) ;
413
414	target = argv[optind];
415
416	/* Specified device node must have "nvme" in it. */
417	if (strstr(argv[optind], "nvme") == NULL) {
418		printf("Invalid device node '%s'.\n", argv[optind]);
419		exit(EX_IOERR);
420	}
421
422	optreset = 1;
423	optind = 1;
424
425	/*
426	 * If devicde node contains "ns", we consider it a namespace,
427	 *  otherwise, consider it a controller.
428	 */
429	if (strstr(target, "ns") == NULL)
430		identify_ctrlr(argc, argv);
431	else
432		identify_ns(argc, argv);
433}
434
435static void
436print_perftest(struct nvme_io_test *io_test, bool perthread)
437{
438	uint32_t i, io_completed = 0, iops, mbps;
439
440	for (i = 0; i < io_test->num_threads; i++)
441		io_completed += io_test->io_completed[i];
442
443	iops = io_completed/io_test->time;
444	mbps = iops * io_test->size / (1024*1024);
445
446	printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7d MB/s: %4d\n",
447	    io_test->num_threads, io_test->size,
448	    io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
449	    io_test->time, iops, mbps);
450
451	if (perthread)
452		for (i = 0; i < io_test->num_threads; i++)
453			printf("\t%3d: %8d IO/s\n", i,
454			    io_test->io_completed[i]/io_test->time);
455
456	exit(1);
457}
458
459static void
460perftest_usage(void)
461{
462	fprintf(stderr, "usage:\n");
463	fprintf(stderr, PERFTEST_USAGE);
464	exit(EX_USAGE);
465}
466
467static void
468perftest(int argc, char *argv[])
469{
470	struct nvme_io_test		io_test;
471	int				fd;
472	char				ch;
473	char				*p;
474	const char			*name;
475	char				path[64];
476	u_long				ioctl_cmd = NVME_IO_TEST;
477	bool				nflag, oflag, sflag, tflag;
478	int				err, perthread = 0;
479
480	nflag = oflag = sflag = tflag = false;
481	name = NULL;
482
483	memset(&io_test, 0, sizeof(io_test));
484
485	while ((ch = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
486		switch (ch) {
487		case 'f':
488			if (!strcmp(optarg, "refthread"))
489				io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
490			break;
491		case 'i':
492			if (!strcmp(optarg, "bio") ||
493			    !strcmp(optarg, "wait"))
494				ioctl_cmd = NVME_BIO_TEST;
495			else if (!strcmp(optarg, "io") ||
496				 !strcmp(optarg, "intr"))
497				ioctl_cmd = NVME_IO_TEST;
498			break;
499		case 'n':
500			nflag = true;
501			io_test.num_threads = strtoul(optarg, &p, 0);
502			if (p != NULL && *p != '\0') {
503				fprintf(stderr,
504				    "\"%s\" not valid number of threads.\n",
505				    optarg);
506				perftest_usage();
507			} else if (io_test.num_threads == 0 ||
508				   io_test.num_threads > 128) {
509				fprintf(stderr,
510				    "\"%s\" not valid number of threads.\n",
511				    optarg);
512				perftest_usage();
513			}
514			break;
515		case 'o':
516			oflag = true;
517			if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
518				io_test.opc = NVME_OPC_READ;
519			else if (!strcmp(optarg, "write") ||
520				 !strcmp(optarg, "WRITE"))
521				io_test.opc = NVME_OPC_WRITE;
522			else {
523				fprintf(stderr, "\"%s\" not valid opcode.\n",
524				    optarg);
525				perftest_usage();
526			}
527			break;
528		case 'p':
529			perthread = 1;
530			break;
531		case 's':
532			sflag = true;
533			io_test.size = strtoul(optarg, &p, 0);
534			if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
535				// do nothing
536			} else if (toupper(*p) == 'K') {
537				io_test.size *= 1024;
538			} else if (toupper(*p) == 'M') {
539				io_test.size *= 1024 * 1024;
540			} else {
541				fprintf(stderr, "\"%s\" not valid size.\n",
542				    optarg);
543				perftest_usage();
544			}
545			break;
546		case 't':
547			tflag = true;
548			io_test.time = strtoul(optarg, &p, 0);
549			if (p != NULL && *p != '\0') {
550				fprintf(stderr,
551				    "\"%s\" not valid time duration.\n",
552				    optarg);
553				perftest_usage();
554			}
555			break;
556		}
557	}
558
559	name = argv[optind];
560
561	if (!nflag || !oflag || !sflag || !tflag || name == NULL)
562		perftest_usage();
563
564	sprintf(path, "/dev/%s", name);
565
566	fd = open(path, O_RDWR);
567	if (fd < 0) {
568		fprintf(stderr, "%s not valid device.\n", path);
569		perftest_usage();
570	}
571
572	err = ioctl(fd, ioctl_cmd, &io_test);
573
574	if (err) {
575		fprintf(stderr, "NVME_IO_TEST returned %d\n", errno);
576		exit(EX_IOERR);
577	}
578
579	print_perftest(&io_test, perthread);
580	exit(EX_OK);
581}
582
583int
584main(int argc, char *argv[])
585{
586
587	if (argc < 2)
588		usage();
589
590	if (strcmp(argv[1], "devlist") == 0)
591		devlist(argc-1, &argv[1]);
592	else if (strcmp(argv[1], "identify") == 0)
593		identify(argc-1, &argv[1]);
594	else if (strcmp(argv[1], "perftest") == 0)
595		perftest(argc-1, &argv[1]);
596
597	usage();
598
599	return (0);
600}
601