nvmecontrol.c revision 240620
1240620Sjimharris/*-
2240620Sjimharris * Copyright (C) 2012 Intel Corporation
3240620Sjimharris * All rights reserved.
4240620Sjimharris *
5240620Sjimharris * Redistribution and use in source and binary forms, with or without
6240620Sjimharris * modification, are permitted provided that the following conditions
7240620Sjimharris * are met:
8240620Sjimharris * 1. Redistributions of source code must retain the above copyright
9240620Sjimharris *    notice, this list of conditions and the following disclaimer.
10240620Sjimharris * 2. Redistributions in binary form must reproduce the above copyright
11240620Sjimharris *    notice, this list of conditions and the following disclaimer in the
12240620Sjimharris *    documentation and/or other materials provided with the distribution.
13240620Sjimharris *
14240620Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15240620Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16240620Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17240620Sjimharris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18240620Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19240620Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20240620Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21240620Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22240620Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23240620Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24240620Sjimharris * SUCH DAMAGE.
25240620Sjimharris */
26240620Sjimharris
27240620Sjimharris#include <sys/cdefs.h>
28240620Sjimharris__FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 240620 2012-09-17 21:26:55Z jimharris $");
29240620Sjimharris
30240620Sjimharris#include <sys/param.h>
31240620Sjimharris#include <sys/ioccom.h>
32240620Sjimharris#include <sys/stat.h>
33240620Sjimharris
34240620Sjimharris#include <dev/nvme/nvme.h>
35240620Sjimharris
36240620Sjimharris#include <ctype.h>
37240620Sjimharris#include <errno.h>
38240620Sjimharris#include <fcntl.h>
39240620Sjimharris#include <stdbool.h>
40240620Sjimharris#include <stddef.h>
41240620Sjimharris#include <stdio.h>
42240620Sjimharris#include <stdlib.h>
43240620Sjimharris#include <string.h>
44240620Sjimharris#include <sysexits.h>
45240620Sjimharris#include <unistd.h>
46240620Sjimharris
47240620Sjimharris#define DEVLIST_USAGE							       \
48240620Sjimharris"       nvmecontrol devlist\n"
49240620Sjimharris
50240620Sjimharris#define IDENTIFY_USAGE							       \
51240620Sjimharris"       nvmecontrol identify <controller id|namespace id>\n"
52240620Sjimharris
53240620Sjimharris#define PERFTEST_USAGE							       \
54240620Sjimharris"       nvmecontrol perftest <-n num_threads> <-o read|write>\n"	       \
55240620Sjimharris"                            <-s size_in_bytes> <-t time_in_seconds>\n"	       \
56240620Sjimharris"                            <-i intr|wait> [-f refthread] [-p]\n"	       \
57240620Sjimharris"                            <namespace id>\n"
58240620Sjimharris
59240620Sjimharrisstatic void perftest_usage(void);
60240620Sjimharris
61240620Sjimharrisstatic void
62240620Sjimharrisusage(void)
63240620Sjimharris{
64240620Sjimharris	fprintf(stderr, "usage:\n");
65240620Sjimharris	fprintf(stderr, DEVLIST_USAGE);
66240620Sjimharris	fprintf(stderr, IDENTIFY_USAGE);
67240620Sjimharris	fprintf(stderr, PERFTEST_USAGE);
68240620Sjimharris	exit(EX_USAGE);
69240620Sjimharris}
70240620Sjimharris
71240620Sjimharrisstatic void
72240620Sjimharrisprint_controller_hex(struct nvme_controller_data *cdata, uint32_t length)
73240620Sjimharris{
74240620Sjimharris	uint32_t	*p;
75240620Sjimharris	uint32_t	i, j;
76240620Sjimharris
77240620Sjimharris	p = (uint32_t *)cdata;
78240620Sjimharris	length /= sizeof(uint32_t);
79240620Sjimharris
80240620Sjimharris	for (i = 0; i < length; i+=8) {
81240620Sjimharris		printf("%03x: ", i*4);
82240620Sjimharris		for (j = 0; j < 8; j++)
83240620Sjimharris			printf("%08x ", p[i+j]);
84240620Sjimharris		printf("\n");
85240620Sjimharris	}
86240620Sjimharris
87240620Sjimharris	printf("\n");
88240620Sjimharris}
89240620Sjimharris
90240620Sjimharrisstatic void
91240620Sjimharrisprint_controller(struct nvme_controller_data *cdata)
92240620Sjimharris{
93240620Sjimharris	printf("Controller Capabilities/Features\n");
94240620Sjimharris	printf("================================\n");
95240620Sjimharris	printf("Vendor ID:                  %04x\n", cdata->vid);
96240620Sjimharris	printf("Subsystem Vendor ID:        %04x\n", cdata->ssvid);
97240620Sjimharris	printf("Serial Number:              %s\n", cdata->sn);
98240620Sjimharris	printf("Model Number:               %s\n", cdata->mn);
99240620Sjimharris	printf("Firmware Version:           %s\n", cdata->fr);
100240620Sjimharris	printf("Recommended Arb Burst:      %d\n", cdata->rab);
101240620Sjimharris	printf("IEEE OUI Identifier:        %02x %02x %02x\n",
102240620Sjimharris		cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
103240620Sjimharris	printf("Multi-Interface Cap:        %02x\n", cdata->mic);
104240620Sjimharris	/* TODO: Use CAP.MPSMIN to determine true memory page size. */
105240620Sjimharris	printf("Max Data Transfer Size:     ");
106240620Sjimharris	if (cdata->mdts == 0)
107240620Sjimharris		printf("Unlimited\n");
108240620Sjimharris	else
109240620Sjimharris		printf("%d\n", PAGE_SIZE * (1 << cdata->mdts));
110240620Sjimharris	printf("\n");
111240620Sjimharris
112240620Sjimharris	printf("Admin Command Set Attributes\n");
113240620Sjimharris	printf("============================\n");
114240620Sjimharris	printf("Security Send/Receive:       %s\n",
115240620Sjimharris		cdata->oacs.security ? "Supported" : "Not Supported");
116240620Sjimharris	printf("Format NVM:                  %s\n",
117240620Sjimharris		cdata->oacs.format ? "Supported" : "Not Supported");
118240620Sjimharris	printf("Firmware Activate/Download:  %s\n",
119240620Sjimharris		cdata->oacs.firmware ? "Supported" : "Not Supported");
120240620Sjimharris	printf("Abort Command Limit:         %d\n", cdata->acl+1);
121240620Sjimharris	printf("Async Event Request Limit:   %d\n", cdata->aerl+1);
122240620Sjimharris	printf("Number of Firmware Slots:    ");
123240620Sjimharris	if (cdata->oacs.firmware != 0)
124240620Sjimharris		printf("%d\n", cdata->frmw.num_slots);
125240620Sjimharris	else
126240620Sjimharris		printf("N/A\n");
127240620Sjimharris	printf("Firmware Slot 1 Read-Only:   ");
128240620Sjimharris	if (cdata->oacs.firmware != 0)
129240620Sjimharris		printf("%s\n", cdata->frmw.slot1_ro ? "Yes" : "No");
130240620Sjimharris	else
131240620Sjimharris		printf("N/A\n");
132240620Sjimharris	printf("Per-Namespace SMART Log:     %s\n",
133240620Sjimharris		cdata->lpa.ns_smart ? "Yes" : "No");
134240620Sjimharris	printf("Error Log Page Entries:      %d\n", cdata->elpe+1);
135240620Sjimharris	printf("Number of Power States:      %d\n", cdata->npss+1);
136240620Sjimharris	printf("\n");
137240620Sjimharris
138240620Sjimharris	printf("NVM Command Set Attributes\n");
139240620Sjimharris	printf("==========================\n");
140240620Sjimharris	printf("Submission Queue Entry Size\n");
141240620Sjimharris	printf("  Max:                       %d\n", 1 << cdata->sqes.max);
142240620Sjimharris	printf("  Min:                       %d\n", 1 << cdata->sqes.min);
143240620Sjimharris	printf("Completion Queue Entry Size\n");
144240620Sjimharris	printf("  Max:                       %d\n", 1 << cdata->cqes.max);
145240620Sjimharris	printf("  Min:                       %d\n", 1 << cdata->cqes.min);
146240620Sjimharris	printf("Number of Namespaces:        %d\n", cdata->nn);
147240620Sjimharris	printf("Compare Command:             %s\n",
148240620Sjimharris		cdata->oncs.compare ? "Supported" : "Not Supported");
149240620Sjimharris	printf("Write Uncorrectable Command: %s\n",
150240620Sjimharris		cdata->oncs.write_unc ? "Supported" : "Not Supported");
151240620Sjimharris	printf("Dataset Management Command:  %s\n",
152240620Sjimharris		cdata->oncs.dsm ? "Supported" : "Not Supported");
153240620Sjimharris	printf("Volatile Write Cache:        %s\n",
154240620Sjimharris		cdata->vwc.present ? "Present" : "Not Present");
155240620Sjimharris}
156240620Sjimharris
157240620Sjimharrisstatic void
158240620Sjimharrisprint_namespace_hex(struct nvme_namespace_data *nsdata, uint32_t length)
159240620Sjimharris{
160240620Sjimharris	uint32_t	*p;
161240620Sjimharris	uint32_t	i, j;
162240620Sjimharris
163240620Sjimharris	p = (uint32_t *)nsdata;
164240620Sjimharris	length /= sizeof(uint32_t);
165240620Sjimharris
166240620Sjimharris	for (i = 0; i < length; i+=8) {
167240620Sjimharris		printf("%03x: ", i*4);
168240620Sjimharris		for (j = 0; j < 8; j++)
169240620Sjimharris			printf("%08x ", p[i+j]);
170240620Sjimharris		printf("\n");
171240620Sjimharris	}
172240620Sjimharris
173240620Sjimharris	printf("\n");
174240620Sjimharris}
175240620Sjimharris
176240620Sjimharrisstatic void
177240620Sjimharrisprint_namespace(struct nvme_namespace_data *nsdata)
178240620Sjimharris{
179240620Sjimharris	uint32_t	i;
180240620Sjimharris
181240620Sjimharris	printf("Size (in LBAs):              %lld (%lldM)\n",
182240620Sjimharris		(long long)nsdata->nsze,
183240620Sjimharris		(long long)nsdata->nsze / 1024 / 1024);
184240620Sjimharris	printf("Capacity (in LBAs):          %lld (%lldM)\n",
185240620Sjimharris		(long long)nsdata->ncap,
186240620Sjimharris		(long long)nsdata->ncap / 1024 / 1024);
187240620Sjimharris	printf("Utilization (in LBAs):       %lld (%lldM)\n",
188240620Sjimharris		(long long)nsdata->nuse,
189240620Sjimharris		(long long)nsdata->nuse / 1024 / 1024);
190240620Sjimharris	printf("Thin Provisioning:           %s\n",
191240620Sjimharris		nsdata->nsfeat.thin_prov ? "Supported" : "Not Supported");
192240620Sjimharris	printf("Number of LBA Formats:       %d\n", nsdata->nlbaf+1);
193240620Sjimharris	printf("Current LBA Format:          LBA Format #%d\n",
194240620Sjimharris		nsdata->flbas.format);
195240620Sjimharris	for (i = 0; i <= nsdata->nlbaf; i++) {
196240620Sjimharris		printf("LBA Format #%d:\n", i);
197240620Sjimharris		printf("  LBA Data Size:             %d\n",
198240620Sjimharris			1 << nsdata->lbaf[i].lbads);
199240620Sjimharris	}
200240620Sjimharris}
201240620Sjimharris
202240620Sjimharrisstatic uint32_t
203240620Sjimharrisns_get_sector_size(struct nvme_namespace_data *nsdata)
204240620Sjimharris{
205240620Sjimharris
206240620Sjimharris	return (1 << nsdata->lbaf[0].lbads);
207240620Sjimharris}
208240620Sjimharris
209240620Sjimharris
210240620Sjimharrisstatic void
211240620Sjimharrisdevlist(int argc, char *argv[])
212240620Sjimharris{
213240620Sjimharris	struct nvme_controller_data	cdata;
214240620Sjimharris	struct nvme_namespace_data	nsdata;
215240620Sjimharris	struct stat			devstat;
216240620Sjimharris	char				name[64], path[64];
217240620Sjimharris	uint32_t			i;
218240620Sjimharris	int				ch, ctrlr, exit_code, fd, found;
219240620Sjimharris
220240620Sjimharris	exit_code = EX_OK;
221240620Sjimharris
222240620Sjimharris	while ((ch = getopt(argc, argv, "")) != -1) {
223240620Sjimharris		switch ((char)ch) {
224240620Sjimharris		default:
225240620Sjimharris			usage();
226240620Sjimharris		}
227240620Sjimharris	}
228240620Sjimharris
229240620Sjimharris	ctrlr = -1;
230240620Sjimharris	found = 0;
231240620Sjimharris
232240620Sjimharris	while (1) {
233240620Sjimharris		ctrlr++;
234240620Sjimharris		sprintf(name, "nvme%d", ctrlr);
235240620Sjimharris		sprintf(path, "/dev/%s", name);
236240620Sjimharris
237240620Sjimharris		if (stat(path, &devstat) != 0)
238240620Sjimharris			break;
239240620Sjimharris
240240620Sjimharris		found++;
241240620Sjimharris
242240620Sjimharris		fd = open(path, O_RDWR);
243240620Sjimharris		if (fd < 0) {
244240620Sjimharris			printf("Could not open %s.\n", path);
245240620Sjimharris			exit_code = EX_NOPERM;
246240620Sjimharris			continue;
247240620Sjimharris		}
248240620Sjimharris
249240620Sjimharris		if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) == -1) {
250240620Sjimharris			printf("ioctl to %s failed.\n", path);
251240620Sjimharris			exit_code = EX_IOERR;
252240620Sjimharris			continue;
253240620Sjimharris		}
254240620Sjimharris
255240620Sjimharris		printf("%6s: %s\n", name, cdata.mn);
256240620Sjimharris
257240620Sjimharris		for (i = 0; i < cdata.nn; i++) {
258240620Sjimharris			sprintf(name, "nvme%dns%d", ctrlr, i+1);
259240620Sjimharris			sprintf(path, "/dev/%s", name);
260240620Sjimharris
261240620Sjimharris			fd = open(path, O_RDWR);
262240620Sjimharris			if (fd < 0) {
263240620Sjimharris				printf("Could not open %s.\n", path);
264240620Sjimharris				exit_code = EX_NOPERM;
265240620Sjimharris				continue;
266240620Sjimharris			}
267240620Sjimharris			if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) == -1) {
268240620Sjimharris				printf("ioctl to %s failed.\n", path);
269240620Sjimharris				exit_code = EX_IOERR;
270240620Sjimharris				continue;
271240620Sjimharris			}
272240620Sjimharris			printf("  %10s (%lldGB)\n",
273240620Sjimharris				name,
274240620Sjimharris				nsdata.nsze *
275240620Sjimharris				(long long)ns_get_sector_size(&nsdata) /
276240620Sjimharris				1024 / 1024 / 1024);
277240620Sjimharris		}
278240620Sjimharris	}
279240620Sjimharris
280240620Sjimharris	if (found == 0)
281240620Sjimharris		printf("No NVMe controllers found.\n");
282240620Sjimharris
283240620Sjimharris	exit(exit_code);
284240620Sjimharris}
285240620Sjimharris
286240620Sjimharrisstatic void
287240620Sjimharrisidentify_ctrlr(int argc, char *argv[])
288240620Sjimharris{
289240620Sjimharris	struct nvme_controller_data	cdata;
290240620Sjimharris	struct stat			devstat;
291240620Sjimharris	char				path[64];
292240620Sjimharris	int				ch, fd, hexflag = 0, hexlength;
293240620Sjimharris	int				verboseflag = 0;
294240620Sjimharris
295240620Sjimharris	while ((ch = getopt(argc, argv, "vx")) != -1) {
296240620Sjimharris		switch ((char)ch) {
297240620Sjimharris		case 'v':
298240620Sjimharris			verboseflag = 1;
299240620Sjimharris			break;
300240620Sjimharris		case 'x':
301240620Sjimharris			hexflag = 1;
302240620Sjimharris			break;
303240620Sjimharris		default:
304240620Sjimharris			usage();
305240620Sjimharris		}
306240620Sjimharris	}
307240620Sjimharris
308240620Sjimharris	sprintf(path, "/dev/%s", argv[optind]);
309240620Sjimharris
310240620Sjimharris	if (stat(path, &devstat) != 0) {
311240620Sjimharris		printf("Invalid device node '%s'.\n", path);
312240620Sjimharris		exit(EX_IOERR);
313240620Sjimharris	}
314240620Sjimharris
315240620Sjimharris	fd = open(path, O_RDWR);
316240620Sjimharris	if (fd < 0) {
317240620Sjimharris		printf("Could not open %s.\n", path);
318240620Sjimharris		exit(EX_NOPERM);
319240620Sjimharris	}
320240620Sjimharris
321240620Sjimharris	if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) == -1) {
322240620Sjimharris		printf("ioctl to %s failed.\n", path);
323240620Sjimharris		exit(EX_IOERR);
324240620Sjimharris	}
325240620Sjimharris
326240620Sjimharris	if (hexflag == 1) {
327240620Sjimharris		if (verboseflag == 1)
328240620Sjimharris			hexlength = sizeof(struct nvme_controller_data);
329240620Sjimharris		else
330240620Sjimharris			hexlength = offsetof(struct nvme_controller_data,
331240620Sjimharris			    reserved5);
332240620Sjimharris		print_controller_hex(&cdata, hexlength);
333240620Sjimharris		exit(EX_OK);
334240620Sjimharris	}
335240620Sjimharris
336240620Sjimharris	if (verboseflag == 1) {
337240620Sjimharris		printf("-v not currently supported without -x.\n");
338240620Sjimharris		usage();
339240620Sjimharris	}
340240620Sjimharris
341240620Sjimharris	print_controller(&cdata);
342240620Sjimharris	exit(EX_OK);
343240620Sjimharris}
344240620Sjimharris
345240620Sjimharrisstatic void
346240620Sjimharrisidentify_ns(int argc, char *argv[])
347240620Sjimharris{
348240620Sjimharris	struct nvme_namespace_data	nsdata;
349240620Sjimharris	struct stat			devstat;
350240620Sjimharris	char				path[64];
351240620Sjimharris	int				ch, fd, hexflag = 0, hexlength;
352240620Sjimharris	int				verboseflag = 0;
353240620Sjimharris
354240620Sjimharris	while ((ch = getopt(argc, argv, "vx")) != -1) {
355240620Sjimharris		switch ((char)ch) {
356240620Sjimharris		case 'v':
357240620Sjimharris			verboseflag = 1;
358240620Sjimharris			break;
359240620Sjimharris		case 'x':
360240620Sjimharris			hexflag = 1;
361240620Sjimharris			break;
362240620Sjimharris		default:
363240620Sjimharris			usage();
364240620Sjimharris		}
365240620Sjimharris	}
366240620Sjimharris
367240620Sjimharris	sprintf(path, "/dev/%s", argv[optind]);
368240620Sjimharris
369240620Sjimharris	if (stat(path, &devstat) != 0) {
370240620Sjimharris		printf("Invalid device node '%s'.\n", path);
371240620Sjimharris		exit(EX_IOERR);
372240620Sjimharris	}
373240620Sjimharris
374240620Sjimharris	fd = open(path, O_RDWR);
375240620Sjimharris	if (fd < 0) {
376240620Sjimharris		printf("Could not open %s.\n", path);
377240620Sjimharris		exit(EX_NOPERM);
378240620Sjimharris	}
379240620Sjimharris
380240620Sjimharris	if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) == -1) {
381240620Sjimharris		printf("ioctl to %s failed.\n", path);
382240620Sjimharris		exit(EX_IOERR);
383240620Sjimharris	}
384240620Sjimharris
385240620Sjimharris	if (hexflag == 1) {
386240620Sjimharris		if (verboseflag == 1)
387240620Sjimharris			hexlength = sizeof(struct nvme_namespace_data);
388240620Sjimharris		else
389240620Sjimharris			hexlength = offsetof(struct nvme_namespace_data,
390240620Sjimharris			    reserved6);
391240620Sjimharris		print_namespace_hex(&nsdata, hexlength);
392240620Sjimharris		exit(EX_OK);
393240620Sjimharris	}
394240620Sjimharris
395240620Sjimharris	if (verboseflag == 1) {
396240620Sjimharris		printf("-v not currently supported without -x.\n");
397240620Sjimharris		usage();
398240620Sjimharris	}
399240620Sjimharris
400240620Sjimharris	print_namespace(&nsdata);
401240620Sjimharris	exit(EX_OK);
402240620Sjimharris}
403240620Sjimharris
404240620Sjimharrisstatic void
405240620Sjimharrisidentify(int argc, char *argv[])
406240620Sjimharris{
407240620Sjimharris	char	*target;
408240620Sjimharris
409240620Sjimharris	if (argc < 2)
410240620Sjimharris		usage();
411240620Sjimharris
412240620Sjimharris	while (getopt(argc, argv, "vx") != -1) ;
413240620Sjimharris
414240620Sjimharris	target = argv[optind];
415240620Sjimharris
416240620Sjimharris	/* Specified device node must have "nvme" in it. */
417240620Sjimharris	if (strstr(argv[optind], "nvme") == NULL) {
418240620Sjimharris		printf("Invalid device node '%s'.\n", argv[optind]);
419240620Sjimharris		exit(EX_IOERR);
420240620Sjimharris	}
421240620Sjimharris
422240620Sjimharris	optreset = 1;
423240620Sjimharris	optind = 1;
424240620Sjimharris
425240620Sjimharris	/*
426240620Sjimharris	 * If devicde node contains "ns", we consider it a namespace,
427240620Sjimharris	 *  otherwise, consider it a controller.
428240620Sjimharris	 */
429240620Sjimharris	if (strstr(target, "ns") == NULL)
430240620Sjimharris		identify_ctrlr(argc, argv);
431240620Sjimharris	else
432240620Sjimharris		identify_ns(argc, argv);
433240620Sjimharris}
434240620Sjimharris
435240620Sjimharrisstatic void
436240620Sjimharrisprint_perftest(struct nvme_io_test *io_test, bool perthread)
437240620Sjimharris{
438240620Sjimharris	uint32_t i, io_completed = 0, iops, mbps;
439240620Sjimharris
440240620Sjimharris	for (i = 0; i < io_test->num_threads; i++)
441240620Sjimharris		io_completed += io_test->io_completed[i];
442240620Sjimharris
443240620Sjimharris	iops = io_completed/io_test->time;
444240620Sjimharris	mbps = iops * io_test->size / (1024*1024);
445240620Sjimharris
446240620Sjimharris	printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7d MB/s: %4d\n",
447240620Sjimharris	    io_test->num_threads, io_test->size,
448240620Sjimharris	    io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
449240620Sjimharris	    io_test->time, iops, mbps);
450240620Sjimharris
451240620Sjimharris	if (perthread)
452240620Sjimharris		for (i = 0; i < io_test->num_threads; i++)
453240620Sjimharris			printf("\t%3d: %8d IO/s\n", i,
454240620Sjimharris			    io_test->io_completed[i]/io_test->time);
455240620Sjimharris
456240620Sjimharris	exit(1);
457240620Sjimharris}
458240620Sjimharris
459240620Sjimharrisstatic void
460240620Sjimharrisperftest_usage(void)
461240620Sjimharris{
462240620Sjimharris	fprintf(stderr, "usage:\n");
463240620Sjimharris	fprintf(stderr, PERFTEST_USAGE);
464240620Sjimharris	exit(EX_USAGE);
465240620Sjimharris}
466240620Sjimharris
467240620Sjimharrisstatic void
468240620Sjimharrisperftest(int argc, char *argv[])
469240620Sjimharris{
470240620Sjimharris	struct nvme_io_test		io_test;
471240620Sjimharris	int				fd;
472240620Sjimharris	char				ch;
473240620Sjimharris	char				*p;
474240620Sjimharris	const char			*name;
475240620Sjimharris	char				path[64];
476240620Sjimharris	u_long				ioctl_cmd = NVME_IO_TEST;
477240620Sjimharris	bool				nflag, oflag, sflag, tflag;
478240620Sjimharris	int				err, perthread = 0;
479240620Sjimharris
480240620Sjimharris	nflag = oflag = sflag = tflag = false;
481240620Sjimharris	name = NULL;
482240620Sjimharris
483240620Sjimharris	memset(&io_test, 0, sizeof(io_test));
484240620Sjimharris
485240620Sjimharris	while ((ch = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
486240620Sjimharris		switch (ch) {
487240620Sjimharris		case 'f':
488240620Sjimharris			if (!strcmp(optarg, "refthread"))
489240620Sjimharris				io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
490240620Sjimharris			break;
491240620Sjimharris		case 'i':
492240620Sjimharris			if (!strcmp(optarg, "bio") ||
493240620Sjimharris			    !strcmp(optarg, "wait"))
494240620Sjimharris				ioctl_cmd = NVME_BIO_TEST;
495240620Sjimharris			else if (!strcmp(optarg, "io") ||
496240620Sjimharris				 !strcmp(optarg, "intr"))
497240620Sjimharris				ioctl_cmd = NVME_IO_TEST;
498240620Sjimharris			break;
499240620Sjimharris		case 'n':
500240620Sjimharris			nflag = true;
501240620Sjimharris			io_test.num_threads = strtoul(optarg, &p, 0);
502240620Sjimharris			if (p != NULL && *p != '\0') {
503240620Sjimharris				fprintf(stderr,
504240620Sjimharris				    "\"%s\" not valid number of threads.\n",
505240620Sjimharris				    optarg);
506240620Sjimharris				perftest_usage();
507240620Sjimharris			} else if (io_test.num_threads == 0 ||
508240620Sjimharris				   io_test.num_threads > 128) {
509240620Sjimharris				fprintf(stderr,
510240620Sjimharris				    "\"%s\" not valid number of threads.\n",
511240620Sjimharris				    optarg);
512240620Sjimharris				perftest_usage();
513240620Sjimharris			}
514240620Sjimharris			break;
515240620Sjimharris		case 'o':
516240620Sjimharris			oflag = true;
517240620Sjimharris			if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
518240620Sjimharris				io_test.opc = NVME_OPC_READ;
519240620Sjimharris			else if (!strcmp(optarg, "write") ||
520240620Sjimharris				 !strcmp(optarg, "WRITE"))
521240620Sjimharris				io_test.opc = NVME_OPC_WRITE;
522240620Sjimharris			else {
523240620Sjimharris				fprintf(stderr, "\"%s\" not valid opcode.\n",
524240620Sjimharris				    optarg);
525240620Sjimharris				perftest_usage();
526240620Sjimharris			}
527240620Sjimharris			break;
528240620Sjimharris		case 'p':
529240620Sjimharris			perthread = 1;
530240620Sjimharris			break;
531240620Sjimharris		case 's':
532240620Sjimharris			sflag = true;
533240620Sjimharris			io_test.size = strtoul(optarg, &p, 0);
534240620Sjimharris			if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
535240620Sjimharris				// do nothing
536240620Sjimharris			} else if (toupper(*p) == 'K') {
537240620Sjimharris				io_test.size *= 1024;
538240620Sjimharris			} else if (toupper(*p) == 'M') {
539240620Sjimharris				io_test.size *= 1024 * 1024;
540240620Sjimharris			} else {
541240620Sjimharris				fprintf(stderr, "\"%s\" not valid size.\n",
542240620Sjimharris				    optarg);
543240620Sjimharris				perftest_usage();
544240620Sjimharris			}
545240620Sjimharris			break;
546240620Sjimharris		case 't':
547240620Sjimharris			tflag = true;
548240620Sjimharris			io_test.time = strtoul(optarg, &p, 0);
549240620Sjimharris			if (p != NULL && *p != '\0') {
550240620Sjimharris				fprintf(stderr,
551240620Sjimharris				    "\"%s\" not valid time duration.\n",
552240620Sjimharris				    optarg);
553240620Sjimharris				perftest_usage();
554240620Sjimharris			}
555240620Sjimharris			break;
556240620Sjimharris		}
557240620Sjimharris	}
558240620Sjimharris
559240620Sjimharris	name = argv[optind];
560240620Sjimharris
561240620Sjimharris	if (!nflag || !oflag || !sflag || !tflag || name == NULL)
562240620Sjimharris		perftest_usage();
563240620Sjimharris
564240620Sjimharris	sprintf(path, "/dev/%s", name);
565240620Sjimharris
566240620Sjimharris	fd = open(path, O_RDWR);
567240620Sjimharris	if (fd < 0) {
568240620Sjimharris		fprintf(stderr, "%s not valid device.\n", path);
569240620Sjimharris		perftest_usage();
570240620Sjimharris	}
571240620Sjimharris
572240620Sjimharris	err = ioctl(fd, ioctl_cmd, &io_test);
573240620Sjimharris
574240620Sjimharris	if (err) {
575240620Sjimharris		fprintf(stderr, "NVME_IO_TEST returned %d\n", errno);
576240620Sjimharris		exit(EX_IOERR);
577240620Sjimharris	}
578240620Sjimharris
579240620Sjimharris	print_perftest(&io_test, perthread);
580240620Sjimharris	exit(EX_OK);
581240620Sjimharris}
582240620Sjimharris
583240620Sjimharrisint
584240620Sjimharrismain(int argc, char *argv[])
585240620Sjimharris{
586240620Sjimharris
587240620Sjimharris	if (argc < 2)
588240620Sjimharris		usage();
589240620Sjimharris
590240620Sjimharris	if (strcmp(argv[1], "devlist") == 0)
591240620Sjimharris		devlist(argc-1, &argv[1]);
592240620Sjimharris	else if (strcmp(argv[1], "identify") == 0)
593240620Sjimharris		identify(argc-1, &argv[1]);
594240620Sjimharris	else if (strcmp(argv[1], "perftest") == 0)
595240620Sjimharris		perftest(argc-1, &argv[1]);
596240620Sjimharris
597240620Sjimharris	usage();
598240620Sjimharris
599240620Sjimharris	return (0);
600240620Sjimharris}
601