diskinfo.c revision 313103
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: stable/10/usr.sbin/diskinfo/diskinfo.c 313103 2017-02-02 19:50:28Z asomers $
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			snprintf(buf, BUFSIZ, "%s%s", _PATH_DEV, argv[i]);
93			fd = open(buf, O_RDONLY);
94		}
95		if (fd < 0) {
96			warn("%s", argv[i]);
97			exit(1);
98		}
99		error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
100		if (error) {
101			warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
102			exitval = 1;
103			goto out;
104		}
105		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
106		if (error) {
107			warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]);
108			exitval = 1;
109			goto out;
110		}
111		error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
112		if (error)
113			fwsectors = 0;
114		error = ioctl(fd, DIOCGFWHEADS, &fwheads);
115		if (error)
116			fwheads = 0;
117		error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize);
118		if (error)
119			stripesize = 0;
120		error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset);
121		if (error)
122			stripeoffset = 0;
123		if (!opt_v) {
124			printf("%s", argv[i]);
125			printf("\t%u", sectorsize);
126			printf("\t%jd", (intmax_t)mediasize);
127			printf("\t%jd", (intmax_t)mediasize/sectorsize);
128			printf("\t%jd", (intmax_t)stripesize);
129			printf("\t%jd", (intmax_t)stripeoffset);
130			if (fwsectors != 0 && fwheads != 0) {
131				printf("\t%jd", (intmax_t)mediasize /
132				    (fwsectors * fwheads * sectorsize));
133				printf("\t%u", fwheads);
134				printf("\t%u", fwsectors);
135			}
136		} else {
137			humanize_number(buf, 5, (int64_t)mediasize, "",
138			    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
139			printf("%s\n", argv[i]);
140			printf("\t%-12u\t# sectorsize\n", sectorsize);
141			printf("\t%-12jd\t# mediasize in bytes (%s)\n",
142			    (intmax_t)mediasize, buf);
143			printf("\t%-12jd\t# mediasize in sectors\n",
144			    (intmax_t)mediasize/sectorsize);
145			printf("\t%-12jd\t# stripesize\n", stripesize);
146			printf("\t%-12jd\t# stripeoffset\n", stripeoffset);
147			if (fwsectors != 0 && fwheads != 0) {
148				printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
149				    (fwsectors * fwheads * sectorsize));
150				printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
151				printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
152			}
153			if (ioctl(fd, DIOCGIDENT, ident) == 0)
154				printf("\t%-12s\t# Disk ident.\n", ident);
155			if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
156				printf("\t%-12s\t# Physical path\n", physpath);
157		}
158		printf("\n");
159		if (opt_c)
160			commandtime(fd, mediasize, sectorsize);
161		if (opt_t)
162			speeddisk(fd, mediasize, sectorsize);
163out:
164		close(fd);
165	}
166	exit (exitval);
167}
168
169
170static char sector[65536];
171static char mega[1024 * 1024];
172
173static void
174rdsect(int fd, off_t blockno, u_int sectorsize)
175{
176	int error;
177
178	if (lseek(fd, (off_t)blockno * sectorsize, SEEK_SET) == -1)
179		err(1, "lseek");
180	error = read(fd, sector, sectorsize);
181	if (error == -1)
182		err(1, "read");
183	if (error != (int)sectorsize)
184		errx(1, "disk too small for test.");
185}
186
187static void
188rdmega(int fd)
189{
190	int error;
191
192	error = read(fd, mega, sizeof(mega));
193	if (error == -1)
194		err(1, "read");
195	if (error != sizeof(mega))
196		errx(1, "disk too small for test.");
197}
198
199static struct timeval tv1, tv2;
200
201static void
202T0(void)
203{
204
205	fflush(stdout);
206	sync();
207	sleep(1);
208	sync();
209	sync();
210	gettimeofday(&tv1, NULL);
211}
212
213static void
214TN(int count)
215{
216	double dt;
217
218	gettimeofday(&tv2, NULL);
219	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
220	dt += (tv2.tv_sec - tv1.tv_sec);
221	printf("%5d iter in %10.6f sec = %8.3f msec\n",
222		count, dt, dt * 1000.0 / count);
223}
224
225static void
226TR(double count)
227{
228	double dt;
229
230	gettimeofday(&tv2, NULL);
231	dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
232	dt += (tv2.tv_sec - tv1.tv_sec);
233	printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
234		count, dt, count / dt);
235}
236
237static void
238speeddisk(int fd, off_t mediasize, u_int sectorsize)
239{
240	int bulk, i;
241	off_t b0, b1, sectorcount, step;
242
243	sectorcount = mediasize / sectorsize;
244	if (sectorcount <= 0)
245		return;		/* Can't test devices with no sectors */
246
247	step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
248	if (step > 16384)
249		step = 16384;
250	bulk = mediasize / (1024 * 1024);
251	if (bulk > 100)
252		bulk = 100;
253
254	printf("Seek times:\n");
255	printf("\tFull stroke:\t");
256	b0 = 0;
257	b1 = sectorcount - step;
258	T0();
259	for (i = 0; i < 125; i++) {
260		rdsect(fd, b0, sectorsize);
261		b0 += step;
262		rdsect(fd, b1, sectorsize);
263		b1 -= step;
264	}
265	TN(250);
266
267	printf("\tHalf stroke:\t");
268	b0 = sectorcount / 4;
269	b1 = b0 + sectorcount / 2;
270	T0();
271	for (i = 0; i < 125; i++) {
272		rdsect(fd, b0, sectorsize);
273		b0 += step;
274		rdsect(fd, b1, sectorsize);
275		b1 += step;
276	}
277	TN(250);
278	printf("\tQuarter stroke:\t");
279	b0 = sectorcount / 4;
280	b1 = b0 + sectorcount / 4;
281	T0();
282	for (i = 0; i < 250; i++) {
283		rdsect(fd, b0, sectorsize);
284		b0 += step;
285		rdsect(fd, b1, sectorsize);
286		b1 += step;
287	}
288	TN(500);
289
290	printf("\tShort forward:\t");
291	b0 = sectorcount / 2;
292	T0();
293	for (i = 0; i < 400; i++) {
294		rdsect(fd, b0, sectorsize);
295		b0 += step;
296	}
297	TN(400);
298
299	printf("\tShort backward:\t");
300	b0 = sectorcount / 2;
301	T0();
302	for (i = 0; i < 400; i++) {
303		rdsect(fd, b0, sectorsize);
304		b0 -= step;
305	}
306	TN(400);
307
308	printf("\tSeq outer:\t");
309	b0 = 0;
310	T0();
311	for (i = 0; i < 2048; i++) {
312		rdsect(fd, b0, sectorsize);
313		b0++;
314	}
315	TN(2048);
316
317	printf("\tSeq inner:\t");
318	b0 = sectorcount - 2048;
319	T0();
320	for (i = 0; i < 2048; i++) {
321		rdsect(fd, b0, sectorsize);
322		b0++;
323	}
324	TN(2048);
325
326	printf("Transfer rates:\n");
327	printf("\toutside:     ");
328	rdsect(fd, 0, sectorsize);
329	T0();
330	for (i = 0; i < bulk; i++) {
331		rdmega(fd);
332	}
333	TR(bulk * 1024);
334
335	printf("\tmiddle:      ");
336	b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1;
337	rdsect(fd, b0, sectorsize);
338	T0();
339	for (i = 0; i < bulk; i++) {
340		rdmega(fd);
341	}
342	TR(bulk * 1024);
343
344	printf("\tinside:      ");
345	b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
346	rdsect(fd, b0, sectorsize);
347	T0();
348	for (i = 0; i < bulk; i++) {
349		rdmega(fd);
350	}
351	TR(bulk * 1024);
352
353	printf("\n");
354	return;
355}
356
357static void
358commandtime(int fd, off_t mediasize, u_int sectorsize)
359{
360	double dtmega, dtsector;
361	int i;
362
363	printf("I/O command overhead:\n");
364	i = mediasize;
365	rdsect(fd, 0, sectorsize);
366	T0();
367	for (i = 0; i < 10; i++)
368		rdmega(fd);
369	gettimeofday(&tv2, NULL);
370	dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6;
371	dtmega += (tv2.tv_sec - tv1.tv_sec);
372
373	printf("\ttime to read 10MB block    %10.6f sec\t= %8.3f msec/sector\n",
374		dtmega, dtmega*100/2048);
375
376	rdsect(fd, 0, sectorsize);
377	T0();
378	for (i = 0; i < 20480; i++)
379		rdsect(fd, 0, sectorsize);
380	gettimeofday(&tv2, NULL);
381	dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6;
382	dtsector += (tv2.tv_sec - tv1.tv_sec);
383
384	printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
385		dtsector, dtsector*100/2048);
386	printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
387		(dtsector - dtmega)*100/2048);
388
389	printf("\n");
390	return;
391}
392