1220422Sgabor/*- 2210389Sgabor * Copyright (c) 2003 Poul-Henning Kamp 3210389Sgabor * All rights reserved. 4210389Sgabor * 5265420Simp * Redistribution and use in source and binary forms, with or without 6222273Sobrien * modification, are permitted provided that the following conditions 7222273Sobrien * are met: 8210389Sgabor * 1. Redistributions of source code must retain the above copyright 9222273Sobrien * notice, this list of conditions and the following disclaimer. 10222273Sobrien * 2. Redistributions in binary form must reproduce the above copyright 11226035Sgabor * notice, this list of conditions and the following disclaimer in the 12226035Sgabor * documentation and/or other materials provided with the distribution. 13226035Sgabor * 3. The names of the authors may not be used to endorse or promote 14277273Swill * products derived from this software without specific prior written 15222273Sobrien * permission. 16226035Sgabor * 17220422Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18322582Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19322582Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20226035Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21322557Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22226035Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23322582Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24322582Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25322582Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26226035Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27285884Spfg * SUCH DAMAGE. 28285884Spfg * 29222273Sobrien * $FreeBSD$ 30210389Sgabor */ 31210389Sgabor 32322525Skevans#include <stdio.h> 33210389Sgabor#include <stdint.h> 34210389Sgabor#include <stdlib.h> 35228099Sgabor#include <strings.h> 36210389Sgabor#include <unistd.h> 37210389Sgabor#include <errno.h> 38210389Sgabor#include <fcntl.h> 39322525Skevans#include <libutil.h> 40210389Sgabor#include <paths.h> 41210389Sgabor#include <err.h> 42277939Sngie#include <sys/disk.h> 43222273Sobrien#include <sys/param.h> 44210389Sgabor#include <sys/time.h> 45275042Sbapt 46245171Sobrienstatic void 47263997Simpusage(void) 48275042Sbapt{ 49245171Sobrien fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n"); 50284345Ssjg exit (1); 51228099Sgabor} 52228099Sgabor 53228099Sgaborstatic int opt_c, opt_t, opt_v; 54228099Sgabor 55228099Sgaborstatic void speeddisk(int fd, off_t mediasize, u_int sectorsize); 56228099Sgaborstatic void commandtime(int fd, off_t mediasize, u_int sectorsize); 57277939Sngie 58277939Sngieint 59277939Sngiemain(int argc, char **argv) 60277939Sngie{ 61277939Sngie int i, ch, fd, error, exitval = 0; 62277939Sngie char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN]; 63277939Sngie off_t mediasize, stripesize, stripeoffset; 64284345Ssjg u_int sectorsize, fwsectors, fwheads; 65245171Sobrien 66245171Sobrien while ((ch = getopt(argc, argv, "ctv")) != -1) { 67245171Sobrien switch (ch) { 68228099Sgabor case 'c': 69263997Simp opt_c = 1; 70275042Sbapt opt_v = 1; 71226271Sgabor break; 72226664Sgabor case 't': 73226271Sgabor opt_t = 1; 74226271Sgabor opt_v = 1; 75226271Sgabor break; 76226271Sgabor case 'v': 77226271Sgabor opt_v = 1; 78226271Sgabor break; 79226664Sgabor default: 80226271Sgabor usage(); 81226271Sgabor } 82226271Sgabor } 83226271Sgabor argc -= optind; 84263997Simp argv += optind; 85322584Skevans 86275042Sbapt if (argc < 1) 87210389Sgabor usage(); 88210389Sgabor 89263997Simp for (i = 0; i < argc; i++) { 90210389Sgabor fd = open(argv[i], O_RDONLY); 91210389Sgabor if (fd < 0 && errno == ENOENT && *argv[i] != '/') { 92210389Sgabor sprintf(buf, "%s%s", _PATH_DEV, argv[i]); 93210389Sgabor fd = open(buf, O_RDONLY); 94210389Sgabor } 95272784Sngie if (fd < 0) { 96272784Sngie warn("%s", argv[i]); 97272784Sngie exitval = 1; 98272784Sngie goto out; 99210389Sgabor } 100 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 101 if (error) { 102 warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]); 103 exitval = 1; 104 goto out; 105 } 106 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 107 if (error) { 108 warnx("%s: ioctl(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 == -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 step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1); 245 if (step > 16384) 246 step = 16384; 247 bulk = mediasize / (1024 * 1024); 248 if (bulk > 100) 249 bulk = 100; 250 251 printf("Seek times:\n"); 252 printf("\tFull stroke:\t"); 253 b0 = 0; 254 b1 = sectorcount - step; 255 T0(); 256 for (i = 0; i < 125; i++) { 257 rdsect(fd, b0, sectorsize); 258 b0 += step; 259 rdsect(fd, b1, sectorsize); 260 b1 -= step; 261 } 262 TN(250); 263 264 printf("\tHalf stroke:\t"); 265 b0 = sectorcount / 4; 266 b1 = b0 + sectorcount / 2; 267 T0(); 268 for (i = 0; i < 125; i++) { 269 rdsect(fd, b0, sectorsize); 270 b0 += step; 271 rdsect(fd, b1, sectorsize); 272 b1 += step; 273 } 274 TN(250); 275 printf("\tQuarter stroke:\t"); 276 b0 = sectorcount / 4; 277 b1 = b0 + sectorcount / 4; 278 T0(); 279 for (i = 0; i < 250; i++) { 280 rdsect(fd, b0, sectorsize); 281 b0 += step; 282 rdsect(fd, b1, sectorsize); 283 b1 += step; 284 } 285 TN(500); 286 287 printf("\tShort forward:\t"); 288 b0 = sectorcount / 2; 289 T0(); 290 for (i = 0; i < 400; i++) { 291 rdsect(fd, b0, sectorsize); 292 b0 += step; 293 } 294 TN(400); 295 296 printf("\tShort backward:\t"); 297 b0 = sectorcount / 2; 298 T0(); 299 for (i = 0; i < 400; i++) { 300 rdsect(fd, b0, sectorsize); 301 b0 -= step; 302 } 303 TN(400); 304 305 printf("\tSeq outer:\t"); 306 b0 = 0; 307 T0(); 308 for (i = 0; i < 2048; i++) { 309 rdsect(fd, b0, sectorsize); 310 b0++; 311 } 312 TN(2048); 313 314 printf("\tSeq inner:\t"); 315 b0 = sectorcount - 2048; 316 T0(); 317 for (i = 0; i < 2048; i++) { 318 rdsect(fd, b0, sectorsize); 319 b0++; 320 } 321 TN(2048); 322 323 printf("Transfer rates:\n"); 324 printf("\toutside: "); 325 rdsect(fd, 0, sectorsize); 326 T0(); 327 for (i = 0; i < bulk; i++) { 328 rdmega(fd); 329 } 330 TR(bulk * 1024); 331 332 printf("\tmiddle: "); 333 b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1; 334 rdsect(fd, b0, sectorsize); 335 T0(); 336 for (i = 0; i < bulk; i++) { 337 rdmega(fd); 338 } 339 TR(bulk * 1024); 340 341 printf("\tinside: "); 342 b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1; 343 rdsect(fd, b0, sectorsize); 344 T0(); 345 for (i = 0; i < bulk; i++) { 346 rdmega(fd); 347 } 348 TR(bulk * 1024); 349 350 printf("\n"); 351 return; 352} 353 354static void 355commandtime(int fd, off_t mediasize, u_int sectorsize) 356{ 357 double dtmega, dtsector; 358 int i; 359 360 printf("I/O command overhead:\n"); 361 i = mediasize; 362 rdsect(fd, 0, sectorsize); 363 T0(); 364 for (i = 0; i < 10; i++) 365 rdmega(fd); 366 gettimeofday(&tv2, NULL); 367 dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6; 368 dtmega += (tv2.tv_sec - tv1.tv_sec); 369 370 printf("\ttime to read 10MB block %10.6f sec\t= %8.3f msec/sector\n", 371 dtmega, dtmega*100/2048); 372 373 rdsect(fd, 0, sectorsize); 374 T0(); 375 for (i = 0; i < 20480; i++) 376 rdsect(fd, 0, sectorsize); 377 gettimeofday(&tv2, NULL); 378 dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6; 379 dtsector += (tv2.tv_sec - tv1.tv_sec); 380 381 printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n", 382 dtsector, dtsector*100/2048); 383 printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n", 384 (dtsector - dtmega)*100/2048); 385 386 printf("\n"); 387 return; 388} 389