1113287Sphk/*-
2113287Sphk * Copyright (c) 2003 Poul-Henning Kamp
3113287Sphk * All rights reserved.
4113287Sphk *
5113287Sphk * Redistribution and use in source and binary forms, with or without
6113287Sphk * modification, are permitted provided that the following conditions
7113287Sphk * are met:
8113287Sphk * 1. Redistributions of source code must retain the above copyright
9113287Sphk *    notice, this list of conditions and the following disclaimer.
10113287Sphk * 2. Redistributions in binary form must reproduce the above copyright
11113287Sphk *    notice, this list of conditions and the following disclaimer in the
12113287Sphk *    documentation and/or other materials provided with the distribution.
13113287Sphk * 3. The names of the authors may not be used to endorse or promote
14113287Sphk *    products derived from this software without specific prior written
15113287Sphk *    permission.
16113287Sphk *
17113287Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18113287Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19113287Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20113287Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21113287Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22113287Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23113287Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24113287Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25113287Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26113287Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27113287Sphk * SUCH DAMAGE.
28113287Sphk *
29113287Sphk * $FreeBSD$
30113287Sphk */
31113287Sphk
32113287Sphk#include <stdio.h>
33113287Sphk#include <stdint.h>
34127884Sphk#include <stdlib.h>
35224250Smav#include <strings.h>
36113287Sphk#include <unistd.h>
37113287Sphk#include <errno.h>
38113287Sphk#include <fcntl.h>
39129679Spjd#include <libutil.h>
40113287Sphk#include <paths.h>
41113287Sphk#include <err.h>
42113287Sphk#include <sys/disk.h>
43223089Sgibbs#include <sys/param.h>
44127884Sphk#include <sys/time.h>
45113287Sphk
46113287Sphkstatic void
47113287Sphkusage(void)
48113287Sphk{
49140058Sru	fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n");
50113287Sphk	exit (1);
51113287Sphk}
52113287Sphk
53137456Ssosstatic int opt_c, opt_t, opt_v;
54113287Sphk
55127884Sphkstatic void speeddisk(int fd, off_t mediasize, u_int sectorsize);
56137456Ssosstatic void commandtime(int fd, off_t mediasize, u_int sectorsize);
57113287Sphk
58113287Sphkint
59113287Sphkmain(int argc, char **argv)
60113287Sphk{
61218432Sdelphij	int i, ch, fd, error, exitval = 0;
62223089Sgibbs	char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN];
63200968Smav	off_t	mediasize, stripesize, stripeoffset;
64113287Sphk	u_int	sectorsize, fwsectors, fwheads;
65113287Sphk
66137456Ssos	while ((ch = getopt(argc, argv, "ctv")) != -1) {
67113287Sphk		switch (ch) {
68137456Ssos		case 'c':
69137456Ssos			opt_c = 1;
70137456Ssos			opt_v = 1;
71137456Ssos			break;
72113287Sphk		case 't':
73113287Sphk			opt_t = 1;
74113287Sphk			opt_v = 1;
75113287Sphk			break;
76113287Sphk		case 'v':
77113287Sphk			opt_v = 1;
78113287Sphk			break;
79113287Sphk		default:
80113287Sphk			usage();
81113287Sphk		}
82113287Sphk	}
83113287Sphk	argc -= optind;
84113287Sphk	argv += optind;
85113287Sphk
86127610Sru	if (argc < 1)
87127610Sru		usage();
88127610Sru
89113287Sphk	for (i = 0; i < argc; i++) {
90113287Sphk		fd = open(argv[i], O_RDONLY);
91113287Sphk		if (fd < 0 && errno == ENOENT && *argv[i] != '/') {
92113287Sphk			sprintf(buf, "%s%s", _PATH_DEV, argv[i]);
93113287Sphk			fd = open(buf, O_RDONLY);
94113287Sphk		}
95218432Sdelphij		if (fd < 0) {
96218432Sdelphij			warn("%s", argv[i]);
97218432Sdelphij			exitval = 1;
98218432Sdelphij			goto out;
99218432Sdelphij		}
100113287Sphk		error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
101218432Sdelphij		if (error) {
102232738Strasz			warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
103218432Sdelphij			exitval = 1;
104218432Sdelphij			goto out;
105218432Sdelphij		}
106113287Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
107218432Sdelphij		if (error) {
108232738Strasz			warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]);
109218432Sdelphij			exitval = 1;
110218432Sdelphij			goto out;
111218432Sdelphij		}
112113287Sphk		error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
113113287Sphk		if (error)
114113287Sphk			fwsectors = 0;
115113287Sphk		error = ioctl(fd, DIOCGFWHEADS, &fwheads);
116113287Sphk		if (error)
117113287Sphk			fwheads = 0;
118200968Smav		error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize);
119200968Smav		if (error)
120200968Smav			stripesize = 0;
121200968Smav		error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset);
122200968Smav		if (error)
123200968Smav			stripeoffset = 0;
124113287Sphk		if (!opt_v) {
125113287Sphk			printf("%s", argv[i]);
126113287Sphk			printf("\t%u", sectorsize);
127113287Sphk			printf("\t%jd", (intmax_t)mediasize);
128113287Sphk			printf("\t%jd", (intmax_t)mediasize/sectorsize);
129200968Smav			printf("\t%jd", (intmax_t)stripesize);
130200968Smav			printf("\t%jd", (intmax_t)stripeoffset);
131113287Sphk			if (fwsectors != 0 && fwheads != 0) {
132113287Sphk				printf("\t%jd", (intmax_t)mediasize /
133113287Sphk				    (fwsectors * fwheads * sectorsize));
134113287Sphk				printf("\t%u", fwheads);
135113287Sphk				printf("\t%u", fwsectors);
136113287Sphk			}
137113287Sphk		} else {
138129712Spjd			humanize_number(buf, 5, (int64_t)mediasize, "",
139129679Spjd			    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
140113287Sphk			printf("%s\n", argv[i]);
141113287Sphk			printf("\t%-12u\t# sectorsize\n", sectorsize);
142129679Spjd			printf("\t%-12jd\t# mediasize in bytes (%s)\n",
143129679Spjd			    (intmax_t)mediasize, buf);
144113287Sphk			printf("\t%-12jd\t# mediasize in sectors\n",
145113287Sphk			    (intmax_t)mediasize/sectorsize);
146200968Smav			printf("\t%-12jd\t# stripesize\n", stripesize);
147200968Smav			printf("\t%-12jd\t# stripeoffset\n", stripeoffset);
148113287Sphk			if (fwsectors != 0 && fwheads != 0) {
149113287Sphk				printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
150113287Sphk				    (fwsectors * fwheads * sectorsize));
151113287Sphk				printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
152113287Sphk				printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
153113287Sphk			}
154196799Spjd			if (ioctl(fd, DIOCGIDENT, ident) == 0)
155169296Spjd				printf("\t%-12s\t# Disk ident.\n", ident);
156223089Sgibbs			if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
157223089Sgibbs				printf("\t%-12s\t# Physical path\n", physpath);
158113287Sphk		}
159113287Sphk		printf("\n");
160137456Ssos		if (opt_c)
161137456Ssos			commandtime(fd, mediasize, sectorsize);
162113287Sphk		if (opt_t)
163127884Sphk			speeddisk(fd, mediasize, sectorsize);
164218432Sdelphijout:
165113287Sphk		close(fd);
166113287Sphk	}
167218432Sdelphij	exit (exitval);
168113287Sphk}
169113287Sphk
170113287Sphk
171113287Sphkstatic char sector[65536];
172113287Sphkstatic char mega[1024 * 1024];
173113287Sphk
174113287Sphkstatic void
175224250Smavrdsect(int fd, off_t blockno, u_int sectorsize)
176113287Sphk{
177113287Sphk	int error;
178113287Sphk
179113295Sphk	lseek(fd, (off_t)blockno * sectorsize, SEEK_SET);
180113287Sphk	error = read(fd, sector, sectorsize);
181232738Strasz	if (error == -1)
182232738Strasz		err(1, "read");
183127884Sphk	if (error != (int)sectorsize)
184232738Strasz		errx(1, "disk too small for test.");
185113287Sphk}
186113287Sphk
187113287Sphkstatic void
188113287Sphkrdmega(int fd)
189113287Sphk{
190113287Sphk	int error;
191113287Sphk
192113287Sphk	error = read(fd, mega, sizeof(mega));
193232738Strasz	if (error == -1)
194232738Strasz		err(1, "read");
195113287Sphk	if (error != sizeof(mega))
196232738Strasz		errx(1, "disk too small for test.");
197113287Sphk}
198113287Sphk
199113287Sphkstatic struct timeval tv1, tv2;
200113287Sphk
201113287Sphkstatic void
202113287SphkT0(void)
203113287Sphk{
204113287Sphk
205113287Sphk	fflush(stdout);
206113287Sphk	sync();
207113287Sphk	sleep(1);
208113287Sphk	sync();
209113287Sphk	sync();
210113287Sphk	gettimeofday(&tv1, NULL);
211113287Sphk}
212113287Sphk
213113287Sphkstatic void
214113287SphkTN(int count)
215113287Sphk{
216113287Sphk	double dt;
217113287Sphk
218113287Sphk	gettimeofday(&tv2, NULL);
219113287Sphk	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
220113287Sphk	dt += (tv2.tv_sec - tv1.tv_sec);
221113287Sphk	printf("%5d iter in %10.6f sec = %8.3f msec\n",
222113287Sphk		count, dt, dt * 1000.0 / count);
223113287Sphk}
224113287Sphk
225113287Sphkstatic void
226113287SphkTR(double count)
227113287Sphk{
228113287Sphk	double dt;
229113287Sphk
230113287Sphk	gettimeofday(&tv2, NULL);
231113287Sphk	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
232113287Sphk	dt += (tv2.tv_sec - tv1.tv_sec);
233113287Sphk	printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
234113287Sphk		count, dt, count / dt);
235113287Sphk}
236113287Sphk
237113287Sphkstatic void
238127884Sphkspeeddisk(int fd, off_t mediasize, u_int sectorsize)
239113287Sphk{
240224250Smav	int bulk, i;
241224250Smav	off_t b0, b1, sectorcount, step;
242113287Sphk
243113287Sphk	sectorcount = mediasize / sectorsize;
244224250Smav	step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
245224250Smav	if (step > 16384)
246224250Smav		step = 16384;
247224250Smav	bulk = mediasize / (1024 * 1024);
248224250Smav	if (bulk > 100)
249224250Smav		bulk = 100;
250113287Sphk
251113287Sphk	printf("Seek times:\n");
252113287Sphk	printf("\tFull stroke:\t");
253113287Sphk	b0 = 0;
254224250Smav	b1 = sectorcount - step;
255113287Sphk	T0();
256113287Sphk	for (i = 0; i < 125; i++) {
257113287Sphk		rdsect(fd, b0, sectorsize);
258224250Smav		b0 += step;
259113287Sphk		rdsect(fd, b1, sectorsize);
260224250Smav		b1 -= step;
261113287Sphk	}
262113287Sphk	TN(250);
263113287Sphk
264113287Sphk	printf("\tHalf stroke:\t");
265113287Sphk	b0 = sectorcount / 4;
266113287Sphk	b1 = b0 + sectorcount / 2;
267113287Sphk	T0();
268113287Sphk	for (i = 0; i < 125; i++) {
269113287Sphk		rdsect(fd, b0, sectorsize);
270224250Smav		b0 += step;
271113287Sphk		rdsect(fd, b1, sectorsize);
272224250Smav		b1 += step;
273113287Sphk	}
274113287Sphk	TN(250);
275113287Sphk	printf("\tQuarter stroke:\t");
276113287Sphk	b0 = sectorcount / 4;
277113287Sphk	b1 = b0 + sectorcount / 4;
278113287Sphk	T0();
279113287Sphk	for (i = 0; i < 250; i++) {
280113287Sphk		rdsect(fd, b0, sectorsize);
281224250Smav		b0 += step;
282113287Sphk		rdsect(fd, b1, sectorsize);
283224250Smav		b1 += step;
284113287Sphk	}
285113287Sphk	TN(500);
286113287Sphk
287113287Sphk	printf("\tShort forward:\t");
288113287Sphk	b0 = sectorcount / 2;
289113287Sphk	T0();
290113295Sphk	for (i = 0; i < 400; i++) {
291113287Sphk		rdsect(fd, b0, sectorsize);
292224250Smav		b0 += step;
293113287Sphk	}
294113295Sphk	TN(400);
295113287Sphk
296113287Sphk	printf("\tShort backward:\t");
297113287Sphk	b0 = sectorcount / 2;
298113287Sphk	T0();
299113295Sphk	for (i = 0; i < 400; i++) {
300113287Sphk		rdsect(fd, b0, sectorsize);
301224250Smav		b0 -= step;
302113287Sphk	}
303113295Sphk	TN(400);
304113287Sphk
305113287Sphk	printf("\tSeq outer:\t");
306113287Sphk	b0 = 0;
307113287Sphk	T0();
308113295Sphk	for (i = 0; i < 2048; i++) {
309113287Sphk		rdsect(fd, b0, sectorsize);
310113287Sphk		b0++;
311113287Sphk	}
312113295Sphk	TN(2048);
313113287Sphk
314113287Sphk	printf("\tSeq inner:\t");
315224250Smav	b0 = sectorcount - 2048;
316113287Sphk	T0();
317113295Sphk	for (i = 0; i < 2048; i++) {
318113287Sphk		rdsect(fd, b0, sectorsize);
319113287Sphk		b0++;
320113287Sphk	}
321113295Sphk	TN(2048);
322113287Sphk
323113287Sphk	printf("Transfer rates:\n");
324113287Sphk	printf("\toutside:     ");
325113287Sphk	rdsect(fd, 0, sectorsize);
326113287Sphk	T0();
327224250Smav	for (i = 0; i < bulk; i++) {
328113287Sphk		rdmega(fd);
329113287Sphk	}
330224250Smav	TR(bulk * 1024);
331113287Sphk
332113287Sphk	printf("\tmiddle:      ");
333224250Smav	b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1;
334113287Sphk	rdsect(fd, b0, sectorsize);
335113287Sphk	T0();
336224250Smav	for (i = 0; i < bulk; i++) {
337113287Sphk		rdmega(fd);
338113287Sphk	}
339224250Smav	TR(bulk * 1024);
340113287Sphk
341113287Sphk	printf("\tinside:      ");
342241844Seadler	b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
343113287Sphk	rdsect(fd, b0, sectorsize);
344113287Sphk	T0();
345224250Smav	for (i = 0; i < bulk; i++) {
346113287Sphk		rdmega(fd);
347113287Sphk	}
348224250Smav	TR(bulk * 1024);
349113287Sphk
350113287Sphk	printf("\n");
351137456Ssos	return;
352137456Ssos}
353113287Sphk
354137456Ssosstatic void
355137456Ssoscommandtime(int fd, off_t mediasize, u_int sectorsize)
356137456Ssos{
357137456Ssos	double dtmega, dtsector;
358137456Ssos	int i;
359137456Ssos
360137456Ssos	printf("I/O command overhead:\n");
361137456Ssos	i = mediasize;
362137456Ssos	rdsect(fd, 0, sectorsize);
363137456Ssos	T0();
364137456Ssos	for (i = 0; i < 10; i++)
365137456Ssos		rdmega(fd);
366137456Ssos	gettimeofday(&tv2, NULL);
367137456Ssos	dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6;
368137456Ssos	dtmega += (tv2.tv_sec - tv1.tv_sec);
369137456Ssos
370137456Ssos	printf("\ttime to read 10MB block    %10.6f sec\t= %8.3f msec/sector\n",
371137456Ssos		dtmega, dtmega*100/2048);
372137456Ssos
373137456Ssos	rdsect(fd, 0, sectorsize);
374137456Ssos	T0();
375137456Ssos	for (i = 0; i < 20480; i++)
376137456Ssos		rdsect(fd, 0, sectorsize);
377137456Ssos	gettimeofday(&tv2, NULL);
378137456Ssos	dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6;
379137456Ssos	dtsector += (tv2.tv_sec - tv1.tv_sec);
380137456Ssos
381137456Ssos	printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
382137456Ssos		dtsector, dtsector*100/2048);
383137456Ssos	printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
384137456Ssos		(dtsector - dtmega)*100/2048);
385137456Ssos
386137456Ssos	printf("\n");
387113287Sphk	return;
388113287Sphk}
389