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 46 static void 47 usage(void) 48 { 49 fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n"); 50 exit (1); 51 } 52 53 static int opt_c, opt_t, opt_v; 54 55 static void speeddisk(int fd, off_t mediasize, u_int sectorsize); 56 static void commandtime(int fd, off_t mediasize, u_int sectorsize); 57 58 int 59 main(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 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); 164 out: 165 close(fd); 166 } 167 exit (exitval); 168 } 169 170 171 static char sector[65536]; 172 static char mega[1024 * 1024]; 173 174 static void 175 rdsect(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 187 static void 188 rdmega(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 199 static struct timeval tv1, tv2; 200 201 static void 202 T0(void) 203 { 204 205 fflush(stdout); 206 sync(); 207 sleep(1); 208 sync(); 209 sync(); 210 gettimeofday(&tv1, NULL); 211 } 212 213 static void 214 TN(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 225 static void 226 TR(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 237 static void 238 speeddisk(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 354 static void 355 commandtime(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