1/*-
2 * Copyright (C) 2012-2013 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$");
29
30#include <sys/param.h>
31#include <sys/ioccom.h>
32
33#include <ctype.h>
34#include <err.h>
35#include <fcntl.h>
36#include <inttypes.h>
37#include <stdbool.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "nvmecontrol.h"
45
46static void
47print_perftest(struct nvme_io_test *io_test, bool perthread)
48{
49	uint64_t	io_completed = 0, iops, mbps;
50	uint32_t	i;
51
52	for (i = 0; i < io_test->num_threads; i++)
53		io_completed += io_test->io_completed[i];
54
55	iops = io_completed/io_test->time;
56	mbps = iops * io_test->size / (1024*1024);
57
58	printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7ju MB/s: %4ju\n",
59	    io_test->num_threads, io_test->size,
60	    io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
61	    io_test->time, (uintmax_t)iops, (uintmax_t)mbps);
62
63	if (perthread)
64		for (i = 0; i < io_test->num_threads; i++)
65			printf("\t%3d: %8ju IO/s\n", i,
66			    (uintmax_t)io_test->io_completed[i]/io_test->time);
67}
68
69static void
70perftest_usage(void)
71{
72	fprintf(stderr, "usage:\n");
73	fprintf(stderr, PERFTEST_USAGE);
74	exit(1);
75}
76
77void
78perftest(int argc, char *argv[])
79{
80	struct nvme_io_test		io_test;
81	int				fd;
82	char				ch;
83	char				*p;
84	u_long				ioctl_cmd = NVME_IO_TEST;
85	bool				nflag, oflag, sflag, tflag;
86	int				perthread = 0;
87
88	nflag = oflag = sflag = tflag = false;
89
90	memset(&io_test, 0, sizeof(io_test));
91
92	while ((ch = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
93		switch (ch) {
94		case 'f':
95			if (!strcmp(optarg, "refthread"))
96				io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
97			break;
98		case 'i':
99			if (!strcmp(optarg, "bio") ||
100			    !strcmp(optarg, "wait"))
101				ioctl_cmd = NVME_BIO_TEST;
102			else if (!strcmp(optarg, "io") ||
103				 !strcmp(optarg, "intr"))
104				ioctl_cmd = NVME_IO_TEST;
105			break;
106		case 'n':
107			nflag = true;
108			io_test.num_threads = strtoul(optarg, &p, 0);
109			if (p != NULL && *p != '\0') {
110				fprintf(stderr,
111				    "\"%s\" not valid number of threads.\n",
112				    optarg);
113				perftest_usage();
114			} else if (io_test.num_threads == 0 ||
115				   io_test.num_threads > 128) {
116				fprintf(stderr,
117				    "\"%s\" not valid number of threads.\n",
118				    optarg);
119				perftest_usage();
120			}
121			break;
122		case 'o':
123			oflag = true;
124			if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
125				io_test.opc = NVME_OPC_READ;
126			else if (!strcmp(optarg, "write") ||
127				 !strcmp(optarg, "WRITE"))
128				io_test.opc = NVME_OPC_WRITE;
129			else {
130				fprintf(stderr, "\"%s\" not valid opcode.\n",
131				    optarg);
132				perftest_usage();
133			}
134			break;
135		case 'p':
136			perthread = 1;
137			break;
138		case 's':
139			sflag = true;
140			io_test.size = strtoul(optarg, &p, 0);
141			if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
142				// do nothing
143			} else if (toupper(*p) == 'K') {
144				io_test.size *= 1024;
145			} else if (toupper(*p) == 'M') {
146				io_test.size *= 1024 * 1024;
147			} else {
148				fprintf(stderr, "\"%s\" not valid size.\n",
149				    optarg);
150				perftest_usage();
151			}
152			break;
153		case 't':
154			tflag = true;
155			io_test.time = strtoul(optarg, &p, 0);
156			if (p != NULL && *p != '\0') {
157				fprintf(stderr,
158				    "\"%s\" not valid time duration.\n",
159				    optarg);
160				perftest_usage();
161			}
162			break;
163		}
164	}
165
166	if (!nflag || !oflag || !sflag || !tflag || optind >= argc)
167		perftest_usage();
168
169	open_dev(argv[optind], &fd, 1, 1);
170	if (ioctl(fd, ioctl_cmd, &io_test) < 0)
171		err(1, "ioctl NVME_IO_TEST failed");
172
173	close(fd);
174	print_perftest(&io_test, perthread);
175	exit(0);
176}
177