2 * Copyright (c) 2003 Poul-Henning Kamp
3 * Copyright (c) 2015 Spectra Logic Corporation
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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
45 #include <sys/param.h>
54 fprintf(stderr, "usage: diskinfo [-citv] disk ...\n");
58 static int opt_c, opt_i, opt_t, opt_v;
60 static void speeddisk(int fd, off_t mediasize, u_int sectorsize);
61 static void commandtime(int fd, off_t mediasize, u_int sectorsize);
62 static void iopsbench(int fd, off_t mediasize, u_int sectorsize);
63 static int zonecheck(int fd, uint32_t *zone_mode, char *zone_str,
67 main(int argc, char **argv)
70 int i, ch, fd, error, exitval = 0;
71 char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN];
73 off_t mediasize, stripesize, stripeoffset;
74 u_int sectorsize, fwsectors, fwheads, zoned = 0;
77 while ((ch = getopt(argc, argv, "citv")) != -1) {
104 for (i = 0; i < argc; i++) {
105 fd = open(argv[i], O_RDONLY | O_DIRECT);
106 if (fd < 0 && errno == ENOENT && *argv[i] != '/') {
107 snprintf(buf, BUFSIZ, "%s%s", _PATH_DEV, argv[i]);
108 fd = open(buf, O_RDONLY);
114 error = fstat(fd, &sb);
116 warn("cannot stat %s", argv[i]);
120 if (S_ISREG(sb.st_mode)) {
121 mediasize = sb.st_size;
122 sectorsize = S_BLKSIZE;
125 stripesize = sb.st_blksize;
128 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
130 warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
134 error = ioctl(fd, DIOCGSECTORSIZE, §orsize);
136 warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]);
140 error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
143 error = ioctl(fd, DIOCGFWHEADS, &fwheads);
146 error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize);
149 error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset);
152 error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc));
157 printf("%s", argv[i]);
158 printf("\t%u", sectorsize);
159 printf("\t%jd", (intmax_t)mediasize);
160 printf("\t%jd", (intmax_t)mediasize/sectorsize);
161 printf("\t%jd", (intmax_t)stripesize);
162 printf("\t%jd", (intmax_t)stripeoffset);
163 if (fwsectors != 0 && fwheads != 0) {
164 printf("\t%jd", (intmax_t)mediasize /
165 (fwsectors * fwheads * sectorsize));
166 printf("\t%u", fwheads);
167 printf("\t%u", fwsectors);
170 humanize_number(buf, 5, (int64_t)mediasize, "",
171 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
172 printf("%s\n", argv[i]);
173 printf("\t%-12u\t# sectorsize\n", sectorsize);
174 printf("\t%-12jd\t# mediasize in bytes (%s)\n",
175 (intmax_t)mediasize, buf);
176 printf("\t%-12jd\t# mediasize in sectors\n",
177 (intmax_t)mediasize/sectorsize);
178 printf("\t%-12jd\t# stripesize\n", stripesize);
179 printf("\t%-12jd\t# stripeoffset\n", stripeoffset);
180 if (fwsectors != 0 && fwheads != 0) {
181 printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
182 (fwsectors * fwheads * sectorsize));
183 printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
184 printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
186 if (ioctl(fd, DIOCGIDENT, ident) == 0)
187 printf("\t%-12s\t# Disk ident.\n", ident);
188 if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0)
189 printf("\t%-12s\t# Physical path\n", physpath);
191 printf("\t%-12s\t# Zone Mode\n", zone_desc);
195 commandtime(fd, mediasize, sectorsize);
197 speeddisk(fd, mediasize, sectorsize);
199 iopsbench(fd, mediasize, sectorsize);
207 static char sector[65536];
208 static char mega[1024 * 1024];
211 rdsect(int fd, off_t blockno, u_int sectorsize)
215 if (lseek(fd, (off_t)blockno * sectorsize, SEEK_SET) == -1)
217 error = read(fd, sector, sectorsize);
220 if (error != (int)sectorsize)
221 errx(1, "disk too small for test.");
229 error = read(fd, mega, sizeof(mega));
232 if (error != sizeof(mega))
233 errx(1, "disk too small for test.");
236 static struct timeval tv1, tv2;
247 gettimeofday(&tv1, NULL);
255 gettimeofday(&tv2, NULL);
256 dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
257 dt += (tv2.tv_sec - tv1.tv_sec);
268 printf("%5d iter in %10.6f sec = %8.3f msec\n",
269 count, dt, dt * 1000.0 / count);
278 printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
279 count, dt, count / dt);
288 printf("%8.0f ops in %10.6f sec = %8.0f IOPS\n",
289 count, dt, count / dt);
293 speeddisk(int fd, off_t mediasize, u_int sectorsize)
296 off_t b0, b1, sectorcount, step;
298 sectorcount = mediasize / sectorsize;
299 if (sectorcount <= 0)
300 return; /* Can't test devices with no sectors */
302 step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1);
305 bulk = mediasize / (1024 * 1024);
309 printf("Seek times:\n");
310 printf("\tFull stroke:\t");
312 b1 = sectorcount - step;
314 for (i = 0; i < 125; i++) {
315 rdsect(fd, b0, sectorsize);
317 rdsect(fd, b1, sectorsize);
322 printf("\tHalf stroke:\t");
323 b0 = sectorcount / 4;
324 b1 = b0 + sectorcount / 2;
326 for (i = 0; i < 125; i++) {
327 rdsect(fd, b0, sectorsize);
329 rdsect(fd, b1, sectorsize);
333 printf("\tQuarter stroke:\t");
334 b0 = sectorcount / 4;
335 b1 = b0 + sectorcount / 4;
337 for (i = 0; i < 250; i++) {
338 rdsect(fd, b0, sectorsize);
340 rdsect(fd, b1, sectorsize);
345 printf("\tShort forward:\t");
346 b0 = sectorcount / 2;
348 for (i = 0; i < 400; i++) {
349 rdsect(fd, b0, sectorsize);
354 printf("\tShort backward:\t");
355 b0 = sectorcount / 2;
357 for (i = 0; i < 400; i++) {
358 rdsect(fd, b0, sectorsize);
363 printf("\tSeq outer:\t");
366 for (i = 0; i < 2048; i++) {
367 rdsect(fd, b0, sectorsize);
372 printf("\tSeq inner:\t");
373 b0 = sectorcount - 2048;
375 for (i = 0; i < 2048; i++) {
376 rdsect(fd, b0, sectorsize);
381 printf("\nTransfer rates:\n");
382 printf("\toutside: ");
383 rdsect(fd, 0, sectorsize);
385 for (i = 0; i < bulk; i++) {
390 printf("\tmiddle: ");
391 b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1;
392 rdsect(fd, b0, sectorsize);
394 for (i = 0; i < bulk; i++) {
399 printf("\tinside: ");
400 b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1;
401 rdsect(fd, b0, sectorsize);
403 for (i = 0; i < bulk; i++) {
413 commandtime(int fd, off_t mediasize, u_int sectorsize)
415 double dtmega, dtsector;
418 printf("I/O command overhead:\n");
420 rdsect(fd, 0, sectorsize);
422 for (i = 0; i < 10; i++)
426 printf("\ttime to read 10MB block %10.6f sec\t= %8.3f msec/sector\n",
427 dtmega, dtmega*100/2048);
429 rdsect(fd, 0, sectorsize);
431 for (i = 0; i < 20480; i++)
432 rdsect(fd, 0, sectorsize);
433 dtsector = delta_t();
435 printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
436 dtsector, dtsector*100/2048);
437 printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
438 (dtsector - dtmega)*100/2048);
445 iops(int fd, off_t mediasize, u_int sectorsize)
447 struct aiocb aios[NAIO], *aiop;
450 int error, i, queued, completed;
452 sectorcount = mediasize / sectorsize;
454 for (i = 0; i < NAIO; i++) {
456 bzero(aiop, sizeof(*aiop));
457 aiop->aio_buf = malloc(sectorsize);
458 if (aiop->aio_buf == NULL)
463 for (i = 0; i < NAIO; i++) {
466 aiop->aio_fildes = fd;
467 aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
468 aiop->aio_nbytes = sectorsize;
470 error = aio_read(aiop);
479 ret = aio_waitcomplete(&aiop, NULL);
481 err(1, "aio_waitcomplete");
482 if (ret != (ssize_t)sectorsize)
483 errx(1, "short read");
487 if (delta_t() < 3.0) {
488 aiop->aio_fildes = fd;
489 aiop->aio_offset = (random() % (sectorcount)) * sectorsize;
490 aiop->aio_nbytes = sectorsize;
492 error = aio_read(aiop);
497 } else if (completed == queued) {
508 iopsbench(int fd, off_t mediasize, u_int sectorsize)
510 printf("Asynchronous random reads:\n");
512 printf("\tsectorsize: ");
513 iops(fd, mediasize, sectorsize);
515 if (sectorsize != 4096) {
516 printf("\t4 kbytes: ");
517 iops(fd, mediasize, 4096);
520 printf("\t32 kbytes: ");
521 iops(fd, mediasize, 32 * 1024);
523 printf("\t128 kbytes: ");
524 iops(fd, mediasize, 128 * 1024);
530 zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len)
532 struct disk_zone_args zone_args;
535 bzero(&zone_args, sizeof(zone_args));
537 zone_args.zone_cmd = DISK_ZONE_GET_PARAMS;
538 error = ioctl(fd, DIOCZONECMD, &zone_args);
541 *zone_mode = zone_args.zone_params.disk_params.zone_mode;
543 switch (*zone_mode) {
544 case DISK_ZONE_MODE_NONE:
545 snprintf(zone_str, zone_str_len, "Not_Zoned");
547 case DISK_ZONE_MODE_HOST_AWARE:
548 snprintf(zone_str, zone_str_len, "Host_Aware");
550 case DISK_ZONE_MODE_DRIVE_MANAGED:
551 snprintf(zone_str, zone_str_len, "Drive_Managed");
553 case DISK_ZONE_MODE_HOST_MANAGED:
554 snprintf(zone_str, zone_str_len, "Host_Managed");
557 snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u",