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