xref: /freebsd/usr.sbin/diskinfo/diskinfo.c (revision 55620f43deef5c0eb5b4b0f675de18b30c8d1c2d)
1 /*-
2  * Copyright (c) 2003 Poul-Henning Kamp
3  * Copyright (c) 2015 Spectra Logic Corporation
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The names of the authors may not be used to endorse or promote
15  *    products derived from this software without specific prior written
16  *    permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <libutil.h>
41 #include <paths.h>
42 #include <err.h>
43 #include <sys/disk.h>
44 #include <sys/param.h>
45 #include <sys/time.h>
46 
47 static void
48 usage(void)
49 {
50 	fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n");
51 	exit (1);
52 }
53 
54 static int opt_c, opt_t, opt_v;
55 
56 static void speeddisk(int fd, off_t mediasize, u_int sectorsize);
57 static void commandtime(int fd, off_t mediasize, u_int sectorsize);
58 static int zonecheck(int fd, uint32_t *zone_mode, char *zone_str,
59 		     size_t zone_str_len);
60 
61 int
62 main(int argc, char **argv)
63 {
64 	int i, ch, fd, error, exitval = 0;
65 	char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN];
66 	char zone_desc[64];
67 	off_t	mediasize, stripesize, stripeoffset;
68 	u_int	sectorsize, fwsectors, fwheads, zoned = 0;
69 	uint32_t zone_mode;
70 
71 	while ((ch = getopt(argc, argv, "ctv")) != -1) {
72 		switch (ch) {
73 		case 'c':
74 			opt_c = 1;
75 			opt_v = 1;
76 			break;
77 		case 't':
78 			opt_t = 1;
79 			opt_v = 1;
80 			break;
81 		case 'v':
82 			opt_v = 1;
83 			break;
84 		default:
85 			usage();
86 		}
87 	}
88 	argc -= optind;
89 	argv += optind;
90 
91 	if (argc < 1)
92 		usage();
93 
94 	for (i = 0; i < argc; i++) {
95 		fd = open(argv[i], O_RDONLY);
96 		if (fd < 0 && errno == ENOENT && *argv[i] != '/') {
97 			sprintf(buf, "%s%s", _PATH_DEV, argv[i]);
98 			fd = open(buf, O_RDONLY);
99 		}
100 		if (fd < 0) {
101 			warn("%s", argv[i]);
102 			exitval = 1;
103 			goto out;
104 		}
105 		error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
106 		if (error) {
107 			warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
108 			exitval = 1;
109 			goto out;
110 		}
111 		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
112 		if (error) {
113 			warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]);
114 			exitval = 1;
115 			goto out;
116 		}
117 		error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
118 		if (error)
119 			fwsectors = 0;
120 		error = ioctl(fd, DIOCGFWHEADS, &fwheads);
121 		if (error)
122 			fwheads = 0;
123 		error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize);
124 		if (error)
125 			stripesize = 0;
126 		error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset);
127 		if (error)
128 			stripeoffset = 0;
129 		error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc));
130 		if (error == 0)
131 			zoned = 1;
132 		if (!opt_v) {
133 			printf("%s", argv[i]);
134 			printf("\t%u", sectorsize);
135 			printf("\t%jd", (intmax_t)mediasize);
136 			printf("\t%jd", (intmax_t)mediasize/sectorsize);
137 			printf("\t%jd", (intmax_t)stripesize);
138 			printf("\t%jd", (intmax_t)stripeoffset);
139 			if (fwsectors != 0 && fwheads != 0) {
140 				printf("\t%jd", (intmax_t)mediasize /
141 				    (fwsectors * fwheads * sectorsize));
142 				printf("\t%u", fwheads);
143 				printf("\t%u", fwsectors);
144 			}
145 		} else {
146 			humanize_number(buf, 5, (int64_t)mediasize, "",
147 			    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
148 			printf("%s\n", argv[i]);
149 			printf("\t%-12u\t# sectorsize\n", sectorsize);
150 			printf("\t%-12jd\t# mediasize in bytes (%s)\n",
151 			    (intmax_t)mediasize, buf);
152 			printf("\t%-12jd\t# mediasize in sectors\n",
153 			    (intmax_t)mediasize/sectorsize);
154 			printf("\t%-12jd\t# stripesize\n", stripesize);
155 			printf("\t%-12jd\t# stripeoffset\n", stripeoffset);
156 			if (fwsectors != 0 && fwheads != 0) {
157 				printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
158 				    (fwsectors * fwheads * sectorsize));
159 				printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
160 				printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
161 			}
162 			if (ioctl(fd, DIOCGIDENT, ident) == 0)
163 				printf("\t%-12s\t# Disk ident.\n", ident);
164 			if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
165 				printf("\t%-12s\t# Physical path\n", physpath);
166 			if (zoned != 0)
167 				printf("\t%-12s\t# Zone Mode\n", zone_desc);
168 		}
169 		printf("\n");
170 		if (opt_c)
171 			commandtime(fd, mediasize, sectorsize);
172 		if (opt_t)
173 			speeddisk(fd, mediasize, sectorsize);
174 out:
175 		close(fd);
176 	}
177 	exit (exitval);
178 }
179 
180 
181 static char sector[65536];
182 static char mega[1024 * 1024];
183 
184 static void
185 rdsect(int fd, off_t blockno, u_int sectorsize)
186 {
187 	int error;
188 
189 	lseek(fd, (off_t)blockno * sectorsize, SEEK_SET);
190 	error = read(fd, sector, sectorsize);
191 	if (error == -1)
192 		err(1, "read");
193 	if (error != (int)sectorsize)
194 		errx(1, "disk too small for test.");
195 }
196 
197 static void
198 rdmega(int fd)
199 {
200 	int error;
201 
202 	error = read(fd, mega, sizeof(mega));
203 	if (error == -1)
204 		err(1, "read");
205 	if (error != sizeof(mega))
206 		errx(1, "disk too small for test.");
207 }
208 
209 static struct timeval tv1, tv2;
210 
211 static void
212 T0(void)
213 {
214 
215 	fflush(stdout);
216 	sync();
217 	sleep(1);
218 	sync();
219 	sync();
220 	gettimeofday(&tv1, NULL);
221 }
222 
223 static void
224 TN(int count)
225 {
226 	double dt;
227 
228 	gettimeofday(&tv2, NULL);
229 	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
230 	dt += (tv2.tv_sec - tv1.tv_sec);
231 	printf("%5d iter in %10.6f sec = %8.3f msec\n",
232 		count, dt, dt * 1000.0 / count);
233 }
234 
235 static void
236 TR(double count)
237 {
238 	double dt;
239 
240 	gettimeofday(&tv2, NULL);
241 	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
242 	dt += (tv2.tv_sec - tv1.tv_sec);
243 	printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
244 		count, dt, count / dt);
245 }
246 
247 static void
248 speeddisk(int fd, off_t mediasize, u_int sectorsize)
249 {
250 	int bulk, i;
251 	off_t b0, b1, sectorcount, step;
252 
253 	sectorcount = mediasize / sectorsize;
254 	step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
255 	if (step > 16384)
256 		step = 16384;
257 	bulk = mediasize / (1024 * 1024);
258 	if (bulk > 100)
259 		bulk = 100;
260 
261 	printf("Seek times:\n");
262 	printf("\tFull stroke:\t");
263 	b0 = 0;
264 	b1 = sectorcount - step;
265 	T0();
266 	for (i = 0; i < 125; i++) {
267 		rdsect(fd, b0, sectorsize);
268 		b0 += step;
269 		rdsect(fd, b1, sectorsize);
270 		b1 -= step;
271 	}
272 	TN(250);
273 
274 	printf("\tHalf stroke:\t");
275 	b0 = sectorcount / 4;
276 	b1 = b0 + sectorcount / 2;
277 	T0();
278 	for (i = 0; i < 125; i++) {
279 		rdsect(fd, b0, sectorsize);
280 		b0 += step;
281 		rdsect(fd, b1, sectorsize);
282 		b1 += step;
283 	}
284 	TN(250);
285 	printf("\tQuarter stroke:\t");
286 	b0 = sectorcount / 4;
287 	b1 = b0 + sectorcount / 4;
288 	T0();
289 	for (i = 0; i < 250; i++) {
290 		rdsect(fd, b0, sectorsize);
291 		b0 += step;
292 		rdsect(fd, b1, sectorsize);
293 		b1 += step;
294 	}
295 	TN(500);
296 
297 	printf("\tShort forward:\t");
298 	b0 = sectorcount / 2;
299 	T0();
300 	for (i = 0; i < 400; i++) {
301 		rdsect(fd, b0, sectorsize);
302 		b0 += step;
303 	}
304 	TN(400);
305 
306 	printf("\tShort backward:\t");
307 	b0 = sectorcount / 2;
308 	T0();
309 	for (i = 0; i < 400; i++) {
310 		rdsect(fd, b0, sectorsize);
311 		b0 -= step;
312 	}
313 	TN(400);
314 
315 	printf("\tSeq outer:\t");
316 	b0 = 0;
317 	T0();
318 	for (i = 0; i < 2048; i++) {
319 		rdsect(fd, b0, sectorsize);
320 		b0++;
321 	}
322 	TN(2048);
323 
324 	printf("\tSeq inner:\t");
325 	b0 = sectorcount - 2048;
326 	T0();
327 	for (i = 0; i < 2048; i++) {
328 		rdsect(fd, b0, sectorsize);
329 		b0++;
330 	}
331 	TN(2048);
332 
333 	printf("Transfer rates:\n");
334 	printf("\toutside:     ");
335 	rdsect(fd, 0, sectorsize);
336 	T0();
337 	for (i = 0; i < bulk; i++) {
338 		rdmega(fd);
339 	}
340 	TR(bulk * 1024);
341 
342 	printf("\tmiddle:      ");
343 	b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1;
344 	rdsect(fd, b0, sectorsize);
345 	T0();
346 	for (i = 0; i < bulk; i++) {
347 		rdmega(fd);
348 	}
349 	TR(bulk * 1024);
350 
351 	printf("\tinside:      ");
352 	b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
353 	rdsect(fd, b0, sectorsize);
354 	T0();
355 	for (i = 0; i < bulk; i++) {
356 		rdmega(fd);
357 	}
358 	TR(bulk * 1024);
359 
360 	printf("\n");
361 	return;
362 }
363 
364 static void
365 commandtime(int fd, off_t mediasize, u_int sectorsize)
366 {
367 	double dtmega, dtsector;
368 	int i;
369 
370 	printf("I/O command overhead:\n");
371 	i = mediasize;
372 	rdsect(fd, 0, sectorsize);
373 	T0();
374 	for (i = 0; i < 10; i++)
375 		rdmega(fd);
376 	gettimeofday(&tv2, NULL);
377 	dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6;
378 	dtmega += (tv2.tv_sec - tv1.tv_sec);
379 
380 	printf("\ttime to read 10MB block    %10.6f sec\t= %8.3f msec/sector\n",
381 		dtmega, dtmega*100/2048);
382 
383 	rdsect(fd, 0, sectorsize);
384 	T0();
385 	for (i = 0; i < 20480; i++)
386 		rdsect(fd, 0, sectorsize);
387 	gettimeofday(&tv2, NULL);
388 	dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6;
389 	dtsector += (tv2.tv_sec - tv1.tv_sec);
390 
391 	printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
392 		dtsector, dtsector*100/2048);
393 	printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
394 		(dtsector - dtmega)*100/2048);
395 
396 	printf("\n");
397 	return;
398 }
399 
400 static int
401 zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len)
402 {
403 	struct disk_zone_args zone_args;
404 	int error;
405 
406 	bzero(&zone_args, sizeof(zone_args));
407 
408 	zone_args.zone_cmd = DISK_ZONE_GET_PARAMS;
409 	error = ioctl(fd, DIOCZONECMD, &zone_args);
410 
411 	if (error == 0) {
412 		*zone_mode = zone_args.zone_params.disk_params.zone_mode;
413 
414 		switch (*zone_mode) {
415 		case DISK_ZONE_MODE_NONE:
416 			snprintf(zone_str, zone_str_len, "Not_Zoned");
417 			break;
418 		case DISK_ZONE_MODE_HOST_AWARE:
419 			snprintf(zone_str, zone_str_len, "Host_Aware");
420 			break;
421 		case DISK_ZONE_MODE_DRIVE_MANAGED:
422 			snprintf(zone_str, zone_str_len, "Drive_Managed");
423 			break;
424 		case DISK_ZONE_MODE_HOST_MANAGED:
425 			snprintf(zone_str, zone_str_len, "Host_Managed");
426 			break;
427 		default:
428 			snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u",
429 			    *zone_mode);
430 			break;
431 		}
432 	}
433 	return (error);
434 }
435