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 warn("%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 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); 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 != (int)sectorsize) 182 err(1, "read error or disk too small for test."); 183 } 184 185 static void 186 rdmega(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 195 static struct timeval tv1, tv2; 196 197 static void 198 T0(void) 199 { 200 201 fflush(stdout); 202 sync(); 203 sleep(1); 204 sync(); 205 sync(); 206 gettimeofday(&tv1, NULL); 207 } 208 209 static void 210 TN(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 221 static void 222 TR(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 233 static void 234 speeddisk(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 350 static void 351 commandtime(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