nvmecontrol.c revision 252655
186227Stmm/*-
286227Stmm * Copyright (C) 2012-2013 Intel Corporation
386227Stmm * All rights reserved.
486227Stmm *
586227Stmm * Redistribution and use in source and binary forms, with or without
686227Stmm * modification, are permitted provided that the following conditions
786227Stmm * are met:
886227Stmm * 1. Redistributions of source code must retain the above copyright
986227Stmm *    notice, this list of conditions and the following disclaimer.
1086227Stmm * 2. Redistributions in binary form must reproduce the above copyright
1186227Stmm *    notice, this list of conditions and the following disclaimer in the
1286227Stmm *    documentation and/or other materials provided with the distribution.
1386227Stmm *
1486227Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1586227Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1686227Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1786227Stmm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1886227Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1986227Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2086227Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2186227Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2286227Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2386227Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2486227Stmm * SUCH DAMAGE.
2586227Stmm */
2686227Stmm
2786227Stmm#include <sys/cdefs.h>
2886227Stmm__FBSDID("$FreeBSD: stable/9/sbin/nvmecontrol/nvmecontrol.c 252655 2013-07-03 23:42:12Z jimharris $");
2986227Stmm
3086227Stmm#include <sys/param.h>
3186227Stmm#include <sys/ioccom.h>
3286227Stmm#include <sys/stat.h>
3386227Stmm
3486227Stmm#include <dev/nvme/nvme.h>
3586227Stmm
3686227Stmm#include <ctype.h>
3786227Stmm#include <errno.h>
3886227Stmm#include <fcntl.h>
3986227Stmm#include <stdbool.h>
4086227Stmm#include <stddef.h>
4186227Stmm#include <stdio.h>
4286227Stmm#include <stdlib.h>
4386227Stmm#include <string.h>
4486227Stmm#include <sysexits.h>
4586227Stmm#include <unistd.h>
4686227Stmm
4786227Stmm#define DEVLIST_USAGE							       \
4886227Stmm"       nvmecontrol devlist\n"
4986227Stmm
5086227Stmm#define IDENTIFY_USAGE							       \
5186227Stmm"       nvmecontrol identify <controller id|namespace id>\n"
5286227Stmm
5386227Stmm#define PERFTEST_USAGE							       \
5486227Stmm"       nvmecontrol perftest <-n num_threads> <-o read|write>\n"	       \
5586227Stmm"                            <-s size_in_bytes> <-t time_in_seconds>\n"	       \
5686227Stmm"                            <-i intr|wait> [-f refthread] [-p]\n"	       \
5786227Stmm"                            <namespace id>\n"
5886227Stmm
5986227Stmm#define RESET_USAGE							       \
6086227Stmm"       nvmecontrol reset <controller id>\n"
6186227Stmm
6286227Stmmstatic void perftest_usage(void);
6386227Stmm
6486227Stmmstatic void
6586227Stmmusage(void)
6686227Stmm{
6786227Stmm	fprintf(stderr, "usage:\n");
6886227Stmm	fprintf(stderr, DEVLIST_USAGE);
6986227Stmm	fprintf(stderr, IDENTIFY_USAGE);
7086227Stmm	fprintf(stderr, RESET_USAGE);
7188823Stmm	fprintf(stderr, PERFTEST_USAGE);
7286227Stmm	exit(EX_USAGE);
7388823Stmm}
7486227Stmm
7586227Stmmstatic void
7686227Stmmprint_controller_hex(struct nvme_controller_data *cdata, uint32_t length)
7786227Stmm{
7886227Stmm	uint32_t	*p;
7986227Stmm	uint32_t	i, j;
8086227Stmm
8186227Stmm	p = (uint32_t *)cdata;
8286227Stmm	length /= sizeof(uint32_t);
8386227Stmm
8486227Stmm	for (i = 0; i < length; i+=8) {
8586227Stmm		printf("%03x: ", i*4);
8686227Stmm		for (j = 0; j < 8; j++)
8786227Stmm			printf("%08x ", p[i+j]);
8886227Stmm		printf("\n");
8986227Stmm	}
9086227Stmm
9186227Stmm	printf("\n");
9286227Stmm}
9386227Stmm
9486227Stmmstatic void
9586227Stmmprint_controller(struct nvme_controller_data *cdata)
9686227Stmm{
9786227Stmm	printf("Controller Capabilities/Features\n");
9886227Stmm	printf("================================\n");
9986227Stmm	printf("Vendor ID:                  %04x\n", cdata->vid);
10086227Stmm	printf("Subsystem Vendor ID:        %04x\n", cdata->ssvid);
10188823Stmm	printf("Serial Number:              %s\n", cdata->sn);
10286227Stmm	printf("Model Number:               %s\n", cdata->mn);
10386227Stmm	printf("Firmware Version:           %s\n", cdata->fr);
10486227Stmm	printf("Recommended Arb Burst:      %d\n", cdata->rab);
10586227Stmm	printf("IEEE OUI Identifier:        %02x %02x %02x\n",
10686227Stmm		cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
10786227Stmm	printf("Multi-Interface Cap:        %02x\n", cdata->mic);
10886227Stmm	/* TODO: Use CAP.MPSMIN to determine true memory page size. */
10988823Stmm	printf("Max Data Transfer Size:     ");
11088823Stmm	if (cdata->mdts == 0)
11188823Stmm		printf("Unlimited\n");
11288823Stmm	else
11388823Stmm		printf("%d\n", PAGE_SIZE * (1 << cdata->mdts));
11486227Stmm	printf("\n");
11586227Stmm
11686227Stmm	printf("Admin Command Set Attributes\n");
11786227Stmm	printf("============================\n");
11886227Stmm	printf("Security Send/Receive:       %s\n",
11986227Stmm		cdata->oacs.security ? "Supported" : "Not Supported");
12086227Stmm	printf("Format NVM:                  %s\n",
12186227Stmm		cdata->oacs.format ? "Supported" : "Not Supported");
12286227Stmm	printf("Firmware Activate/Download:  %s\n",
12386227Stmm		cdata->oacs.firmware ? "Supported" : "Not Supported");
12486227Stmm	printf("Abort Command Limit:         %d\n", cdata->acl+1);
12586227Stmm	printf("Async Event Request Limit:   %d\n", cdata->aerl+1);
12686227Stmm	printf("Number of Firmware Slots:    ");
12786227Stmm	if (cdata->oacs.firmware != 0)
12886227Stmm		printf("%d\n", cdata->frmw.num_slots);
12986227Stmm	else
13086227Stmm		printf("N/A\n");
13186227Stmm	printf("Firmware Slot 1 Read-Only:   ");
13286227Stmm	if (cdata->oacs.firmware != 0)
13386227Stmm		printf("%s\n", cdata->frmw.slot1_ro ? "Yes" : "No");
13486227Stmm	else
13586227Stmm		printf("N/A\n");
13686227Stmm	printf("Per-Namespace SMART Log:     %s\n",
13786227Stmm		cdata->lpa.ns_smart ? "Yes" : "No");
13886227Stmm	printf("Error Log Page Entries:      %d\n", cdata->elpe+1);
13986227Stmm	printf("Number of Power States:      %d\n", cdata->npss+1);
14086227Stmm	printf("\n");
14186227Stmm
14286227Stmm	printf("NVM Command Set Attributes\n");
14386227Stmm	printf("==========================\n");
14486227Stmm	printf("Submission Queue Entry Size\n");
14586227Stmm	printf("  Max:                       %d\n", 1 << cdata->sqes.max);
14686227Stmm	printf("  Min:                       %d\n", 1 << cdata->sqes.min);
14786227Stmm	printf("Completion Queue Entry Size\n");
14886227Stmm	printf("  Max:                       %d\n", 1 << cdata->cqes.max);
14986227Stmm	printf("  Min:                       %d\n", 1 << cdata->cqes.min);
15086227Stmm	printf("Number of Namespaces:        %d\n", cdata->nn);
15186227Stmm	printf("Compare Command:             %s\n",
15286227Stmm		cdata->oncs.compare ? "Supported" : "Not Supported");
15386227Stmm	printf("Write Uncorrectable Command: %s\n",
15486227Stmm		cdata->oncs.write_unc ? "Supported" : "Not Supported");
15586227Stmm	printf("Dataset Management Command:  %s\n",
15686227Stmm		cdata->oncs.dsm ? "Supported" : "Not Supported");
15786227Stmm	printf("Volatile Write Cache:        %s\n",
15888823Stmm		cdata->vwc.present ? "Present" : "Not Present");
15986227Stmm}
16086227Stmm
16186227Stmmstatic void
16286227Stmmprint_namespace_hex(struct nvme_namespace_data *nsdata, uint32_t length)
16386227Stmm{
16486227Stmm	uint32_t	*p;
16586227Stmm	uint32_t	i, j;
16690622Stmm
16790622Stmm	p = (uint32_t *)nsdata;
16890622Stmm	length /= sizeof(uint32_t);
16986227Stmm
17090622Stmm	for (i = 0; i < length; i+=8) {
17186227Stmm		printf("%03x: ", i*4);
17286227Stmm		for (j = 0; j < 8; j++)
17390622Stmm			printf("%08x ", p[i+j]);
17486227Stmm		printf("\n");
17586227Stmm	}
17686227Stmm
17786227Stmm	printf("\n");
17886227Stmm}
17986227Stmm
18086227Stmmstatic void
18186227Stmmprint_namespace(struct nvme_namespace_data *nsdata)
18286227Stmm{
18386227Stmm	uint32_t	i;
18486227Stmm
18586227Stmm	printf("Size (in LBAs):              %lld (%lldM)\n",
18686227Stmm		(long long)nsdata->nsze,
18786227Stmm		(long long)nsdata->nsze / 1024 / 1024);
18886227Stmm	printf("Capacity (in LBAs):          %lld (%lldM)\n",
18986227Stmm		(long long)nsdata->ncap,
19086227Stmm		(long long)nsdata->ncap / 1024 / 1024);
19186227Stmm	printf("Utilization (in LBAs):       %lld (%lldM)\n",
19286227Stmm		(long long)nsdata->nuse,
19386227Stmm		(long long)nsdata->nuse / 1024 / 1024);
19486227Stmm	printf("Thin Provisioning:           %s\n",
19586227Stmm		nsdata->nsfeat.thin_prov ? "Supported" : "Not Supported");
19686227Stmm	printf("Number of LBA Formats:       %d\n", nsdata->nlbaf+1);
19786227Stmm	printf("Current LBA Format:          LBA Format #%d\n",
19886227Stmm		nsdata->flbas.format);
19986227Stmm	for (i = 0; i <= nsdata->nlbaf; i++) {
20086227Stmm		printf("LBA Format #%d:\n", i);
20186227Stmm		printf("  LBA Data Size:             %d\n",
20286227Stmm			1 << nsdata->lbaf[i].lbads);
20386227Stmm	}
20486227Stmm}
20586227Stmm
20686227Stmmstatic uint32_t
20788823Stmmns_get_sector_size(struct nvme_namespace_data *nsdata)
20886227Stmm{
20986227Stmm
21086227Stmm	return (1 << nsdata->lbaf[0].lbads);
21186227Stmm}
21286227Stmm
21388823Stmmstatic void
21488823Stmmread_controller_data(int fd, struct nvme_controller_data *cdata)
21588823Stmm{
21688823Stmm	struct nvme_pt_command	pt;
21788823Stmm
21888823Stmm	memset(&pt, 0, sizeof(pt));
21988823Stmm	pt.cmd.opc = NVME_OPC_IDENTIFY;
22088823Stmm	pt.cmd.cdw10 = 1;
22188823Stmm	pt.buf = cdata;
22288823Stmm	pt.len = sizeof(*cdata);
22386227Stmm	pt.is_read = 1;
22486227Stmm
22586227Stmm	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) {
22686227Stmm		printf("Identify request failed. errno=%d (%s)\n",
22786227Stmm		    errno, strerror(errno));
22886227Stmm		exit(EX_IOERR);
22986227Stmm	}
23086227Stmm
23186227Stmm	if (nvme_completion_is_error(&pt.cpl)) {
23286227Stmm		printf("Passthrough command returned error.\n");
23386227Stmm		exit(EX_IOERR);
23488823Stmm	}
23586227Stmm}
23686227Stmm
23786227Stmmstatic void
23886227Stmmread_namespace_data(int fd, int nsid, struct nvme_namespace_data *nsdata)
23986227Stmm{
24086227Stmm	struct nvme_pt_command	pt;
24186227Stmm
24286227Stmm	memset(&pt, 0, sizeof(pt));
24386227Stmm	pt.cmd.opc = NVME_OPC_IDENTIFY;
24486227Stmm	pt.cmd.nsid = nsid;
24586227Stmm	pt.buf = nsdata;
24686227Stmm	pt.len = sizeof(*nsdata);
24786227Stmm	pt.is_read = 1;
24886227Stmm
24986227Stmm	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) {
25086227Stmm		printf("Identify request failed. errno=%d (%s)\n",
25186227Stmm		    errno, strerror(errno));
25286227Stmm		exit(EX_IOERR);
25386227Stmm	}
25486227Stmm
25586227Stmm	if (nvme_completion_is_error(&pt.cpl)) {
25686227Stmm		printf("Passthrough command returned error.\n");
25786227Stmm		exit(EX_IOERR);
25886227Stmm	}
25986227Stmm}
26086227Stmm
26186227Stmmstatic int
26286227Stmmopen_dev(const char *str, int *fd, int show_error, int exit_on_error)
26386227Stmm{
26486227Stmm	struct stat	devstat;
26586227Stmm	char		full_path[64];
26686227Stmm
26786227Stmm	snprintf(full_path, sizeof(full_path), "/dev/%s", str);
26886227Stmm	if (stat(full_path, &devstat) != 0) {
26986227Stmm		if (show_error)
27088823Stmm			fprintf(stderr, "error\n");
27186227Stmm		if (exit_on_error)
27286227Stmm			exit(EX_NOINPUT);
27386227Stmm		else
27486227Stmm			return (EX_NOINPUT);
27586227Stmm	}
27686227Stmm
27786227Stmm	*fd = open(full_path, O_RDWR);
27886227Stmm	if (*fd < 0) {
27986227Stmm		if (show_error)
28086227Stmm			printf("Could not open %s. errno=%d (%s)\n", full_path,
28186227Stmm			    errno, strerror(errno));
28286227Stmm		if (exit_on_error)
28386227Stmm			exit(EX_NOPERM);
28486227Stmm		else
28586227Stmm			return (EX_NOPERM);
28686227Stmm	}
28786227Stmm
28886227Stmm	return (EX_OK);
28986227Stmm}
29086227Stmm
29186227Stmmstatic void
29286227Stmmdevlist_usage(void)
29386227Stmm{
29486227Stmm	fprintf(stderr, "usage:\n");
29586227Stmm	fprintf(stderr, DEVLIST_USAGE);
29686227Stmm	exit(EX_USAGE);
29786227Stmm}
29886227Stmm
29986227Stmmstatic void
30086227Stmmdevlist(int argc, char *argv[])
30186227Stmm{
30286227Stmm	struct nvme_controller_data	cdata;
30386227Stmm	struct nvme_namespace_data	nsdata;
30486227Stmm	char				name[64];
30586227Stmm	uint32_t			i;
30686227Stmm	int				ch, ctrlr, exit_code, fd, found;
30786227Stmm
30886227Stmm	exit_code = EX_OK;
30986227Stmm
31086227Stmm	while ((ch = getopt(argc, argv, "")) != -1) {
31186227Stmm		switch ((char)ch) {
31286227Stmm		default:
31386227Stmm			devlist_usage();
31486227Stmm		}
31586227Stmm	}
31686227Stmm
31786227Stmm	ctrlr = -1;
31886227Stmm	found = 0;
31986227Stmm
32086227Stmm	while (1) {
32186227Stmm		ctrlr++;
32286227Stmm		sprintf(name, "nvme%d", ctrlr);
32386227Stmm
32486227Stmm		exit_code = open_dev(name, &fd, 0, 0);
32586227Stmm
32686227Stmm		if (exit_code == EX_NOINPUT)
32786227Stmm			break;
32886227Stmm		else if (exit_code == EX_NOPERM) {
32986227Stmm			printf("Could not open /dev/%s, errno = %d (%s)\n",
33086227Stmm			    name, errno, strerror(errno));
33186227Stmm			continue;
33286227Stmm		}
33386227Stmm
33486227Stmm		found++;
33586227Stmm		read_controller_data(fd, &cdata);
33686227Stmm		printf("%6s: %s\n", name, cdata.mn);
33786227Stmm
33886227Stmm		for (i = 0; i < cdata.nn; i++) {
33986227Stmm			sprintf(name, "nvme%dns%d", ctrlr, i+1);
34086227Stmm			read_namespace_data(fd, i+1, &nsdata);
34186227Stmm			printf("  %10s (%lldGB)\n",
34286227Stmm				name,
34386227Stmm				nsdata.nsze *
34486227Stmm				(long long)ns_get_sector_size(&nsdata) /
34586227Stmm				1024 / 1024 / 1024);
34686227Stmm		}
34786227Stmm
34888823Stmm		close(fd);
34986227Stmm	}
35086227Stmm
35186227Stmm	if (found == 0)
35286227Stmm		printf("No NVMe controllers found.\n");
35386227Stmm
35486227Stmm	exit(EX_OK);
35586227Stmm}
35686227Stmm
35786227Stmmstatic void
35886227Stmmidentify_usage(void)
35986227Stmm{
36086227Stmm	fprintf(stderr, "usage:\n");
36186227Stmm	fprintf(stderr, IDENTIFY_USAGE);
36286227Stmm	exit(EX_USAGE);
36386227Stmm}
36486227Stmm
36586227Stmmstatic void
36686227Stmmidentify_ctrlr(int argc, char *argv[])
36786227Stmm{
36886227Stmm	struct nvme_controller_data	cdata;
36986227Stmm	int				ch, fd, hexflag = 0, hexlength;
37086227Stmm	int				verboseflag = 0;
37186227Stmm
37286227Stmm	while ((ch = getopt(argc, argv, "vx")) != -1) {
37386227Stmm		switch ((char)ch) {
37486227Stmm		case 'v':
37586227Stmm			verboseflag = 1;
37686227Stmm			break;
37786227Stmm		case 'x':
37886227Stmm			hexflag = 1;
37986227Stmm			break;
38086227Stmm		default:
38186227Stmm			identify_usage();
38286227Stmm		}
38386227Stmm	}
38488823Stmm
38586227Stmm	open_dev(argv[optind], &fd, 1, 1);
38686227Stmm	read_controller_data(fd, &cdata);
38786227Stmm	close(fd);
38886227Stmm
38986227Stmm	if (hexflag == 1) {
39086227Stmm		if (verboseflag == 1)
39186227Stmm			hexlength = sizeof(struct nvme_controller_data);
39286227Stmm		else
39388823Stmm			hexlength = offsetof(struct nvme_controller_data,
39486227Stmm			    reserved5);
39588823Stmm		print_controller_hex(&cdata, hexlength);
39688823Stmm		exit(EX_OK);
39788823Stmm	}
39886227Stmm
39986227Stmm	if (verboseflag == 1) {
40086227Stmm		printf("-v not currently supported without -x.\n");
40186227Stmm		identify_usage();
40286227Stmm	}
40386227Stmm
40486227Stmm	print_controller(&cdata);
40588823Stmm	exit(EX_OK);
40688823Stmm}
40788823Stmm
40888823Stmmstatic void
40986227Stmmidentify_ns(int argc, char *argv[])
41086227Stmm{
41186227Stmm	struct nvme_namespace_data	nsdata;
41286227Stmm	char				path[64];
41386227Stmm	char				*nsloc;
41486227Stmm	int				ch, fd, hexflag = 0, hexlength, nsid;
41586227Stmm	int				verboseflag = 0;
41688823Stmm
41786227Stmm	while ((ch = getopt(argc, argv, "vx")) != -1) {
41886227Stmm		switch ((char)ch) {
41986227Stmm		case 'v':
42086227Stmm			verboseflag = 1;
42186227Stmm			break;
42286227Stmm		case 'x':
42386227Stmm			hexflag = 1;
42486227Stmm			break;
42586227Stmm		default:
42686227Stmm			identify_usage();
42786227Stmm		}
42886227Stmm	}
42986227Stmm
43086227Stmm	/*
43186227Stmm	 * Check if the specified device node exists before continuing.
43286227Stmm	 *  This is a cleaner check for cases where the correct controller
43388823Stmm	 *  is specified, but an invalid namespace on that controller.
43486227Stmm	 */
43586227Stmm	open_dev(argv[optind], &fd, 1, 1);
43686227Stmm	close(fd);
43786227Stmm
43886227Stmm	/*
43986227Stmm	 * Pull the namespace id from the string. +2 skips past the "ns" part
44086227Stmm	 *  of the string.  Don't search past 10 characters into the string,
44186227Stmm	 *  otherwise we know it is malformed.
44286227Stmm	 */
44386227Stmm	nsloc = strnstr(argv[optind], "ns", 10);
44486227Stmm	if (nsloc != NULL)
44586227Stmm		nsid = strtol(nsloc + 2, NULL, 10);
44686227Stmm	if (nsloc == NULL || (nsid == 0 && errno != 0)) {
44786227Stmm		printf("Invalid namespace ID %s.\n", argv[optind]);
44886227Stmm		exit(EX_IOERR);
44986227Stmm	}
45086227Stmm
451	/*
452	 * We send IDENTIFY commands to the controller, not the namespace,
453	 *  since it is an admin cmd.  So the path should only include the
454	 *  nvmeX part of the nvmeXnsY string.
455	 */
456	snprintf(path, nsloc - argv[optind] + 1, "%s", argv[optind]);
457	open_dev(path, &fd, 1, 1);
458	read_namespace_data(fd, nsid, &nsdata);
459	close(fd);
460
461	if (hexflag == 1) {
462		if (verboseflag == 1)
463			hexlength = sizeof(struct nvme_namespace_data);
464		else
465			hexlength = offsetof(struct nvme_namespace_data,
466			    reserved6);
467		print_namespace_hex(&nsdata, hexlength);
468		exit(EX_OK);
469	}
470
471	if (verboseflag == 1) {
472		printf("-v not currently supported without -x.\n");
473		identify_usage();
474	}
475
476	print_namespace(&nsdata);
477	exit(EX_OK);
478}
479
480static void
481identify(int argc, char *argv[])
482{
483	char	*target;
484
485	if (argc < 2)
486		identify_usage();
487
488	while (getopt(argc, argv, "vx") != -1) ;
489
490	target = argv[optind];
491
492	optreset = 1;
493	optind = 1;
494
495	/*
496	 * If device node contains "ns", we consider it a namespace,
497	 *  otherwise, consider it a controller.
498	 */
499	if (strstr(target, "ns") == NULL)
500		identify_ctrlr(argc, argv);
501	else
502		identify_ns(argc, argv);
503}
504
505static void
506print_perftest(struct nvme_io_test *io_test, bool perthread)
507{
508	uint32_t i, io_completed = 0, iops, mbps;
509
510	for (i = 0; i < io_test->num_threads; i++)
511		io_completed += io_test->io_completed[i];
512
513	iops = io_completed/io_test->time;
514	mbps = iops * io_test->size / (1024*1024);
515
516	printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7d MB/s: %4d\n",
517	    io_test->num_threads, io_test->size,
518	    io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
519	    io_test->time, iops, mbps);
520
521	if (perthread)
522		for (i = 0; i < io_test->num_threads; i++)
523			printf("\t%3d: %8d IO/s\n", i,
524			    io_test->io_completed[i]/io_test->time);
525
526	exit(1);
527}
528
529static void
530perftest_usage(void)
531{
532	fprintf(stderr, "usage:\n");
533	fprintf(stderr, PERFTEST_USAGE);
534	exit(EX_USAGE);
535}
536
537static void
538perftest(int argc, char *argv[])
539{
540	struct nvme_io_test		io_test;
541	int				fd;
542	char				ch;
543	char				*p;
544	u_long				ioctl_cmd = NVME_IO_TEST;
545	bool				nflag, oflag, sflag, tflag;
546	int				perthread = 0;
547
548	nflag = oflag = sflag = tflag = false;
549
550	memset(&io_test, 0, sizeof(io_test));
551
552	while ((ch = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
553		switch (ch) {
554		case 'f':
555			if (!strcmp(optarg, "refthread"))
556				io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
557			break;
558		case 'i':
559			if (!strcmp(optarg, "bio") ||
560			    !strcmp(optarg, "wait"))
561				ioctl_cmd = NVME_BIO_TEST;
562			else if (!strcmp(optarg, "io") ||
563				 !strcmp(optarg, "intr"))
564				ioctl_cmd = NVME_IO_TEST;
565			break;
566		case 'n':
567			nflag = true;
568			io_test.num_threads = strtoul(optarg, &p, 0);
569			if (p != NULL && *p != '\0') {
570				fprintf(stderr,
571				    "\"%s\" not valid number of threads.\n",
572				    optarg);
573				perftest_usage();
574			} else if (io_test.num_threads == 0 ||
575				   io_test.num_threads > 128) {
576				fprintf(stderr,
577				    "\"%s\" not valid number of threads.\n",
578				    optarg);
579				perftest_usage();
580			}
581			break;
582		case 'o':
583			oflag = true;
584			if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
585				io_test.opc = NVME_OPC_READ;
586			else if (!strcmp(optarg, "write") ||
587				 !strcmp(optarg, "WRITE"))
588				io_test.opc = NVME_OPC_WRITE;
589			else {
590				fprintf(stderr, "\"%s\" not valid opcode.\n",
591				    optarg);
592				perftest_usage();
593			}
594			break;
595		case 'p':
596			perthread = 1;
597			break;
598		case 's':
599			sflag = true;
600			io_test.size = strtoul(optarg, &p, 0);
601			if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
602				// do nothing
603			} else if (toupper(*p) == 'K') {
604				io_test.size *= 1024;
605			} else if (toupper(*p) == 'M') {
606				io_test.size *= 1024 * 1024;
607			} else {
608				fprintf(stderr, "\"%s\" not valid size.\n",
609				    optarg);
610				perftest_usage();
611			}
612			break;
613		case 't':
614			tflag = true;
615			io_test.time = strtoul(optarg, &p, 0);
616			if (p != NULL && *p != '\0') {
617				fprintf(stderr,
618				    "\"%s\" not valid time duration.\n",
619				    optarg);
620				perftest_usage();
621			}
622			break;
623		}
624	}
625
626	if (!nflag || !oflag || !sflag || !tflag || optind >= argc)
627		perftest_usage();
628
629	open_dev(argv[optind], &fd, 1, 1);
630	if (ioctl(fd, ioctl_cmd, &io_test) < 0) {
631		fprintf(stderr, "NVME_IO_TEST failed. errno=%d (%s)\n", errno,
632		    strerror(errno));
633		close(fd);
634		exit(EX_IOERR);
635	}
636
637	close(fd);
638	print_perftest(&io_test, perthread);
639	exit(EX_OK);
640}
641
642static void
643reset_usage(void)
644{
645	fprintf(stderr, "usage:\n");
646	fprintf(stderr, RESET_USAGE);
647	exit(EX_USAGE);
648}
649
650static void
651reset_ctrlr(int argc, char *argv[])
652{
653	int	ch, fd;
654
655	while ((ch = getopt(argc, argv, "")) != -1) {
656		switch ((char)ch) {
657		default:
658			reset_usage();
659		}
660	}
661
662	open_dev(argv[optind], &fd, 1, 1);
663	if (ioctl(fd, NVME_RESET_CONTROLLER) < 0) {
664		printf("Reset request to %s failed. errno=%d (%s)\n",
665		    argv[optind], errno, strerror(errno));
666		exit(EX_IOERR);
667	}
668
669	exit(EX_OK);
670}
671
672int
673main(int argc, char *argv[])
674{
675
676	if (argc < 2)
677		usage();
678
679	if (strcmp(argv[1], "devlist") == 0)
680		devlist(argc-1, &argv[1]);
681	else if (strcmp(argv[1], "identify") == 0)
682		identify(argc-1, &argv[1]);
683	else if (strcmp(argv[1], "perftest") == 0)
684		perftest(argc-1, &argv[1]);
685	else if (strcmp(argv[1], "reset") == 0)
686		reset_ctrlr(argc-1, &argv[1]);
687
688	usage();
689
690	return (0);
691}
692
693