xref: /freebsd/sbin/nvmecontrol/perftest.c (revision 63cbe8d1d95f97e93929ec66f1138693d08dd9f6)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2012-2013 Intel Corporation
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/ioccom.h>
34 
35 #include <ctype.h>
36 #include <err.h>
37 #include <fcntl.h>
38 #include <inttypes.h>
39 #include <stdbool.h>
40 #include <stddef.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #include "nvmecontrol.h"
47 
48 static void
49 print_perftest(struct nvme_io_test *io_test, bool perthread)
50 {
51 	uint64_t	io_completed = 0, iops, mbps;
52 	uint32_t	i;
53 
54 	for (i = 0; i < io_test->num_threads; i++)
55 		io_completed += io_test->io_completed[i];
56 
57 	iops = io_completed/io_test->time;
58 	mbps = iops * io_test->size / (1024*1024);
59 
60 	printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7ju MB/s: %4ju\n",
61 	    io_test->num_threads, io_test->size,
62 	    io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
63 	    io_test->time, (uintmax_t)iops, (uintmax_t)mbps);
64 
65 	if (perthread)
66 		for (i = 0; i < io_test->num_threads; i++)
67 			printf("\t%3d: %8ju IO/s\n", i,
68 			    (uintmax_t)io_test->io_completed[i]/io_test->time);
69 }
70 
71 static void
72 perftest_usage(void)
73 {
74 	fprintf(stderr, "usage:\n");
75 	fprintf(stderr, PERFTEST_USAGE);
76 	exit(1);
77 }
78 
79 void
80 perftest(int argc, char *argv[])
81 {
82 	struct nvme_io_test		io_test;
83 	int				fd;
84 	int				opt;
85 	char				*p;
86 	u_long				ioctl_cmd = NVME_IO_TEST;
87 	bool				nflag, oflag, sflag, tflag;
88 	int				perthread = 0;
89 
90 	nflag = oflag = sflag = tflag = false;
91 
92 	memset(&io_test, 0, sizeof(io_test));
93 
94 	while ((opt = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
95 		switch (opt) {
96 		case 'f':
97 			if (!strcmp(optarg, "refthread"))
98 				io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
99 			break;
100 		case 'i':
101 			if (!strcmp(optarg, "bio") ||
102 			    !strcmp(optarg, "wait"))
103 				ioctl_cmd = NVME_BIO_TEST;
104 			else if (!strcmp(optarg, "io") ||
105 				 !strcmp(optarg, "intr"))
106 				ioctl_cmd = NVME_IO_TEST;
107 			break;
108 		case 'n':
109 			nflag = true;
110 			io_test.num_threads = strtoul(optarg, &p, 0);
111 			if (p != NULL && *p != '\0') {
112 				fprintf(stderr,
113 				    "\"%s\" not valid number of threads.\n",
114 				    optarg);
115 				perftest_usage();
116 			} else if (io_test.num_threads == 0 ||
117 				   io_test.num_threads > 128) {
118 				fprintf(stderr,
119 				    "\"%s\" not valid number of threads.\n",
120 				    optarg);
121 				perftest_usage();
122 			}
123 			break;
124 		case 'o':
125 			oflag = true;
126 			if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
127 				io_test.opc = NVME_OPC_READ;
128 			else if (!strcmp(optarg, "write") ||
129 				 !strcmp(optarg, "WRITE"))
130 				io_test.opc = NVME_OPC_WRITE;
131 			else {
132 				fprintf(stderr, "\"%s\" not valid opcode.\n",
133 				    optarg);
134 				perftest_usage();
135 			}
136 			break;
137 		case 'p':
138 			perthread = 1;
139 			break;
140 		case 's':
141 			sflag = true;
142 			io_test.size = strtoul(optarg, &p, 0);
143 			if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
144 				// do nothing
145 			} else if (toupper(*p) == 'K') {
146 				io_test.size *= 1024;
147 			} else if (toupper(*p) == 'M') {
148 				io_test.size *= 1024 * 1024;
149 			} else {
150 				fprintf(stderr, "\"%s\" not valid size.\n",
151 				    optarg);
152 				perftest_usage();
153 			}
154 			break;
155 		case 't':
156 			tflag = true;
157 			io_test.time = strtoul(optarg, &p, 0);
158 			if (p != NULL && *p != '\0') {
159 				fprintf(stderr,
160 				    "\"%s\" not valid time duration.\n",
161 				    optarg);
162 				perftest_usage();
163 			}
164 			break;
165 		}
166 	}
167 
168 	if (!nflag || !oflag || !sflag || !tflag || optind >= argc)
169 		perftest_usage();
170 
171 	open_dev(argv[optind], &fd, 1, 1);
172 	if (ioctl(fd, ioctl_cmd, &io_test) < 0)
173 		err(1, "ioctl NVME_IO_TEST failed");
174 
175 	close(fd);
176 	print_perftest(&io_test, perthread);
177 	exit(0);
178 }
179