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 <unistd.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <libutil.h> 39 #include <paths.h> 40 #include <err.h> 41 #include <sys/disk.h> 42 #include <sys/time.h> 43 44 static void 45 usage(void) 46 { 47 fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n"); 48 exit (1); 49 } 50 51 static int opt_c, opt_t, opt_v; 52 53 static void speeddisk(int fd, off_t mediasize, u_int sectorsize); 54 static void commandtime(int fd, off_t mediasize, u_int sectorsize); 55 56 int 57 main(int argc, char **argv) 58 { 59 int i, ch, fd, error; 60 char buf[BUFSIZ]; 61 off_t mediasize; 62 u_int sectorsize, fwsectors, fwheads; 63 64 while ((ch = getopt(argc, argv, "ctv")) != -1) { 65 switch (ch) { 66 case 'c': 67 opt_c = 1; 68 opt_v = 1; 69 break; 70 case 't': 71 opt_t = 1; 72 opt_v = 1; 73 break; 74 case 'v': 75 opt_v = 1; 76 break; 77 default: 78 usage(); 79 } 80 } 81 argc -= optind; 82 argv += optind; 83 84 if (argc < 1) 85 usage(); 86 87 for (i = 0; i < argc; i++) { 88 fd = open(argv[i], O_RDONLY); 89 if (fd < 0 && errno == ENOENT && *argv[i] != '/') { 90 sprintf(buf, "%s%s", _PATH_DEV, argv[i]); 91 fd = open(buf, O_RDONLY); 92 } 93 if (fd < 0) 94 err(1, argv[i]); 95 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 96 if (error) 97 err(1, "%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]); 98 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 99 if (error) 100 err(1, "%s: DIOCGSECTORSIZE failed, probably not a disk.", argv[i]); 101 error = ioctl(fd, DIOCGFWSECTORS, &fwsectors); 102 if (error) 103 fwsectors = 0; 104 error = ioctl(fd, DIOCGFWHEADS, &fwheads); 105 if (error) 106 fwheads = 0; 107 if (!opt_v) { 108 printf("%s", argv[i]); 109 printf("\t%u", sectorsize); 110 printf("\t%jd", (intmax_t)mediasize); 111 printf("\t%jd", (intmax_t)mediasize/sectorsize); 112 if (fwsectors != 0 && fwheads != 0) { 113 printf("\t%jd", (intmax_t)mediasize / 114 (fwsectors * fwheads * sectorsize)); 115 printf("\t%u", fwheads); 116 printf("\t%u", fwsectors); 117 } 118 } else { 119 humanize_number(buf, 5, (int64_t)mediasize, "", 120 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 121 printf("%s\n", argv[i]); 122 printf("\t%-12u\t# sectorsize\n", sectorsize); 123 printf("\t%-12jd\t# mediasize in bytes (%s)\n", 124 (intmax_t)mediasize, buf); 125 printf("\t%-12jd\t# mediasize in sectors\n", 126 (intmax_t)mediasize/sectorsize); 127 if (fwsectors != 0 && fwheads != 0) { 128 printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize / 129 (fwsectors * fwheads * sectorsize)); 130 printf("\t%-12u\t# Heads according to firmware.\n", fwheads); 131 printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors); 132 } 133 } 134 printf("\n"); 135 if (opt_c) 136 commandtime(fd, mediasize, sectorsize); 137 if (opt_t) 138 speeddisk(fd, mediasize, sectorsize); 139 close(fd); 140 } 141 exit (0); 142 } 143 144 145 static char sector[65536]; 146 static char mega[1024 * 1024]; 147 148 static void 149 rdsect(int fd, u_int blockno, u_int sectorsize) 150 { 151 int error; 152 153 lseek(fd, (off_t)blockno * sectorsize, SEEK_SET); 154 error = read(fd, sector, sectorsize); 155 if (error != (int)sectorsize) 156 err(1, "read error or disk too small for test."); 157 } 158 159 static void 160 rdmega(int fd) 161 { 162 int error; 163 164 error = read(fd, mega, sizeof(mega)); 165 if (error != sizeof(mega)) 166 err(1, "read error or disk too small for test."); 167 } 168 169 static struct timeval tv1, tv2; 170 171 static void 172 T0(void) 173 { 174 175 fflush(stdout); 176 sync(); 177 sleep(1); 178 sync(); 179 sync(); 180 gettimeofday(&tv1, NULL); 181 } 182 183 static void 184 TN(int count) 185 { 186 double dt; 187 188 gettimeofday(&tv2, NULL); 189 dt = (tv2.tv_usec - tv1.tv_usec) / 1e6; 190 dt += (tv2.tv_sec - tv1.tv_sec); 191 printf("%5d iter in %10.6f sec = %8.3f msec\n", 192 count, dt, dt * 1000.0 / count); 193 } 194 195 static void 196 TR(double count) 197 { 198 double dt; 199 200 gettimeofday(&tv2, NULL); 201 dt = (tv2.tv_usec - tv1.tv_usec) / 1e6; 202 dt += (tv2.tv_sec - tv1.tv_sec); 203 printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n", 204 count, dt, count / dt); 205 } 206 207 static void 208 speeddisk(int fd, off_t mediasize, u_int sectorsize) 209 { 210 int i; 211 uint b0, b1, sectorcount; 212 213 sectorcount = mediasize / sectorsize; 214 215 printf("Seek times:\n"); 216 printf("\tFull stroke:\t"); 217 b0 = 0; 218 b1 = sectorcount - 1 - 16384; 219 T0(); 220 for (i = 0; i < 125; i++) { 221 rdsect(fd, b0, sectorsize); 222 b0 += 16384; 223 rdsect(fd, b1, sectorsize); 224 b1 -= 16384; 225 } 226 TN(250); 227 228 printf("\tHalf stroke:\t"); 229 b0 = sectorcount / 4; 230 b1 = b0 + sectorcount / 2; 231 T0(); 232 for (i = 0; i < 125; i++) { 233 rdsect(fd, b0, sectorsize); 234 b0 += 16384; 235 rdsect(fd, b1, sectorsize); 236 b1 += 16384; 237 } 238 TN(250); 239 printf("\tQuarter stroke:\t"); 240 b0 = sectorcount / 4; 241 b1 = b0 + sectorcount / 4; 242 T0(); 243 for (i = 0; i < 250; i++) { 244 rdsect(fd, b0, sectorsize); 245 b0 += 16384; 246 rdsect(fd, b1, sectorsize); 247 b1 += 16384; 248 } 249 TN(500); 250 251 printf("\tShort forward:\t"); 252 b0 = sectorcount / 2; 253 T0(); 254 for (i = 0; i < 400; i++) { 255 rdsect(fd, b0, sectorsize); 256 b0 += 16384; 257 } 258 TN(400); 259 260 printf("\tShort backward:\t"); 261 b0 = sectorcount / 2; 262 T0(); 263 for (i = 0; i < 400; i++) { 264 rdsect(fd, b0, sectorsize); 265 b0 -= 16384; 266 } 267 TN(400); 268 269 printf("\tSeq outer:\t"); 270 b0 = 0; 271 T0(); 272 for (i = 0; i < 2048; i++) { 273 rdsect(fd, b0, sectorsize); 274 b0++; 275 } 276 TN(2048); 277 278 printf("\tSeq inner:\t"); 279 b0 = sectorcount - 2048 - 1; 280 T0(); 281 for (i = 0; i < 2048; i++) { 282 rdsect(fd, b0, sectorsize); 283 b0++; 284 } 285 TN(2048); 286 287 printf("Transfer rates:\n"); 288 printf("\toutside: "); 289 rdsect(fd, 0, sectorsize); 290 T0(); 291 for (i = 0; i < 100; i++) { 292 rdmega(fd); 293 } 294 TR(100 * 1024); 295 296 printf("\tmiddle: "); 297 b0 = sectorcount / 2; 298 rdsect(fd, b0, sectorsize); 299 T0(); 300 for (i = 0; i < 100; i++) { 301 rdmega(fd); 302 } 303 TR(100 * 1024); 304 305 printf("\tinside: "); 306 b0 = sectorcount - 100 * (1024*1024 / sectorsize) - 1;; 307 rdsect(fd, b0, sectorsize); 308 T0(); 309 for (i = 0; i < 100; i++) { 310 rdmega(fd); 311 } 312 TR(100 * 1024); 313 314 printf("\n"); 315 return; 316 } 317 318 static void 319 commandtime(int fd, off_t mediasize, u_int sectorsize) 320 { 321 double dtmega, dtsector; 322 int i; 323 324 printf("I/O command overhead:\n"); 325 i = mediasize; 326 rdsect(fd, 0, sectorsize); 327 T0(); 328 for (i = 0; i < 10; i++) 329 rdmega(fd); 330 gettimeofday(&tv2, NULL); 331 dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6; 332 dtmega += (tv2.tv_sec - tv1.tv_sec); 333 334 printf("\ttime to read 10MB block %10.6f sec\t= %8.3f msec/sector\n", 335 dtmega, dtmega*100/2048); 336 337 rdsect(fd, 0, sectorsize); 338 T0(); 339 for (i = 0; i < 20480; i++) 340 rdsect(fd, 0, sectorsize); 341 gettimeofday(&tv2, NULL); 342 dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6; 343 dtsector += (tv2.tv_sec - tv1.tv_sec); 344 345 printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n", 346 dtsector, dtsector*100/2048); 347 printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n", 348 (dtsector - dtmega)*100/2048); 349 350 printf("\n"); 351 return; 352 } 353