1 /*- 2 * Copyright (c) 2003 Poul-Henning Kamp 3 * Copyright (c) 2015 Spectra Logic Corporation 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 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 16 * permission. 17 * 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 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 #include <stdio.h> 34 #include <stdint.h> 35 #include <stdlib.h> 36 #include <strings.h> 37 #include <unistd.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <libutil.h> 41 #include <paths.h> 42 #include <err.h> 43 #include <sys/aio.h> 44 #include <sys/disk.h> 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <sys/time.h> 48 49 #define NAIO 128 50 51 static void 52 usage(void) 53 { 54 fprintf(stderr, "usage: diskinfo [-citv] disk ...\n"); 55 exit (1); 56 } 57 58 static int opt_c, opt_i, opt_t, opt_v; 59 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, 64 size_t zone_str_len); 65 66 int 67 main(int argc, char **argv) 68 { 69 struct stat sb; 70 int i, ch, fd, error, exitval = 0; 71 char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN]; 72 char zone_desc[64]; 73 off_t mediasize, stripesize, stripeoffset; 74 u_int sectorsize, fwsectors, fwheads, zoned = 0; 75 uint32_t zone_mode; 76 77 while ((ch = getopt(argc, argv, "citv")) != -1) { 78 switch (ch) { 79 case 'c': 80 opt_c = 1; 81 opt_v = 1; 82 break; 83 case 'i': 84 opt_i = 1; 85 opt_v = 1; 86 break; 87 case 't': 88 opt_t = 1; 89 opt_v = 1; 90 break; 91 case 'v': 92 opt_v = 1; 93 break; 94 default: 95 usage(); 96 } 97 } 98 argc -= optind; 99 argv += optind; 100 101 if (argc < 1) 102 usage(); 103 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 sprintf(buf, "%s%s", _PATH_DEV, argv[i]); 108 fd = open(buf, O_RDONLY); 109 } 110 if (fd < 0) { 111 warn("%s", argv[i]); 112 exitval = 1; 113 goto out; 114 } 115 error = fstat(fd, &sb); 116 if (error != 0) { 117 warn("cannot stat %s", argv[i]); 118 exitval = 1; 119 goto out; 120 } 121 if (S_ISREG(sb.st_mode)) { 122 mediasize = sb.st_size; 123 sectorsize = S_BLKSIZE; 124 fwsectors = 0; 125 fwheads = 0; 126 stripesize = sb.st_blksize; 127 stripeoffset = 0; 128 } else { 129 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 130 if (error) { 131 warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]); 132 exitval = 1; 133 goto out; 134 } 135 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 136 if (error) { 137 warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]); 138 exitval = 1; 139 goto out; 140 } 141 error = ioctl(fd, DIOCGFWSECTORS, &fwsectors); 142 if (error) 143 fwsectors = 0; 144 error = ioctl(fd, DIOCGFWHEADS, &fwheads); 145 if (error) 146 fwheads = 0; 147 error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize); 148 if (error) 149 stripesize = 0; 150 error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset); 151 if (error) 152 stripeoffset = 0; 153 error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc)); 154 if (error == 0) 155 zoned = 1; 156 } 157 if (!opt_v) { 158 printf("%s", argv[i]); 159 printf("\t%u", sectorsize); 160 printf("\t%jd", (intmax_t)mediasize); 161 printf("\t%jd", (intmax_t)mediasize/sectorsize); 162 printf("\t%jd", (intmax_t)stripesize); 163 printf("\t%jd", (intmax_t)stripeoffset); 164 if (fwsectors != 0 && fwheads != 0) { 165 printf("\t%jd", (intmax_t)mediasize / 166 (fwsectors * fwheads * sectorsize)); 167 printf("\t%u", fwheads); 168 printf("\t%u", fwsectors); 169 } 170 } else { 171 humanize_number(buf, 5, (int64_t)mediasize, "", 172 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 173 printf("%s\n", argv[i]); 174 printf("\t%-12u\t# sectorsize\n", sectorsize); 175 printf("\t%-12jd\t# mediasize in bytes (%s)\n", 176 (intmax_t)mediasize, buf); 177 printf("\t%-12jd\t# mediasize in sectors\n", 178 (intmax_t)mediasize/sectorsize); 179 printf("\t%-12jd\t# stripesize\n", stripesize); 180 printf("\t%-12jd\t# stripeoffset\n", stripeoffset); 181 if (fwsectors != 0 && fwheads != 0) { 182 printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize / 183 (fwsectors * fwheads * sectorsize)); 184 printf("\t%-12u\t# Heads according to firmware.\n", fwheads); 185 printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors); 186 } 187 if (ioctl(fd, DIOCGIDENT, ident) == 0) 188 printf("\t%-12s\t# Disk ident.\n", ident); 189 if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0) 190 printf("\t%-12s\t# Physical path\n", physpath); 191 if (zoned != 0) 192 printf("\t%-12s\t# Zone Mode\n", zone_desc); 193 } 194 printf("\n"); 195 if (opt_c) 196 commandtime(fd, mediasize, sectorsize); 197 if (opt_t) 198 speeddisk(fd, mediasize, sectorsize); 199 if (opt_i) 200 iopsbench(fd, mediasize, sectorsize); 201 out: 202 close(fd); 203 } 204 exit (exitval); 205 } 206 207 208 static char sector[65536]; 209 static char mega[1024 * 1024]; 210 211 static void 212 rdsect(int fd, off_t blockno, u_int sectorsize) 213 { 214 int error; 215 216 lseek(fd, (off_t)blockno * sectorsize, SEEK_SET); 217 error = read(fd, sector, sectorsize); 218 if (error == -1) 219 err(1, "read"); 220 if (error != (int)sectorsize) 221 errx(1, "disk too small for test."); 222 } 223 224 static void 225 rdmega(int fd) 226 { 227 int error; 228 229 error = read(fd, mega, sizeof(mega)); 230 if (error == -1) 231 err(1, "read"); 232 if (error != sizeof(mega)) 233 errx(1, "disk too small for test."); 234 } 235 236 static struct timeval tv1, tv2; 237 238 static void 239 T0(void) 240 { 241 242 fflush(stdout); 243 sync(); 244 sleep(1); 245 sync(); 246 sync(); 247 gettimeofday(&tv1, NULL); 248 } 249 250 static double 251 delta_t(void) 252 { 253 double dt; 254 255 gettimeofday(&tv2, NULL); 256 dt = (tv2.tv_usec - tv1.tv_usec) / 1e6; 257 dt += (tv2.tv_sec - tv1.tv_sec); 258 259 return (dt); 260 } 261 262 static void 263 TN(int count) 264 { 265 double dt; 266 267 dt = delta_t(); 268 printf("%5d iter in %10.6f sec = %8.3f msec\n", 269 count, dt, dt * 1000.0 / count); 270 } 271 272 static void 273 TR(double count) 274 { 275 double dt; 276 277 dt = delta_t(); 278 printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n", 279 count, dt, count / dt); 280 } 281 282 static void 283 TI(double count) 284 { 285 double dt; 286 287 dt = delta_t(); 288 printf("%8.0f ops in %10.6f sec = %8.0f IOPS\n", 289 count, dt, count / dt); 290 } 291 292 static void 293 speeddisk(int fd, off_t mediasize, u_int sectorsize) 294 { 295 int bulk, i; 296 off_t b0, b1, sectorcount, step; 297 298 sectorcount = mediasize / sectorsize; 299 step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1); 300 if (step > 16384) 301 step = 16384; 302 bulk = mediasize / (1024 * 1024); 303 if (bulk > 100) 304 bulk = 100; 305 306 printf("Seek times:\n"); 307 printf("\tFull stroke:\t"); 308 b0 = 0; 309 b1 = sectorcount - step; 310 T0(); 311 for (i = 0; i < 125; i++) { 312 rdsect(fd, b0, sectorsize); 313 b0 += step; 314 rdsect(fd, b1, sectorsize); 315 b1 -= step; 316 } 317 TN(250); 318 319 printf("\tHalf stroke:\t"); 320 b0 = sectorcount / 4; 321 b1 = b0 + sectorcount / 2; 322 T0(); 323 for (i = 0; i < 125; i++) { 324 rdsect(fd, b0, sectorsize); 325 b0 += step; 326 rdsect(fd, b1, sectorsize); 327 b1 += step; 328 } 329 TN(250); 330 printf("\tQuarter stroke:\t"); 331 b0 = sectorcount / 4; 332 b1 = b0 + sectorcount / 4; 333 T0(); 334 for (i = 0; i < 250; i++) { 335 rdsect(fd, b0, sectorsize); 336 b0 += step; 337 rdsect(fd, b1, sectorsize); 338 b1 += step; 339 } 340 TN(500); 341 342 printf("\tShort forward:\t"); 343 b0 = sectorcount / 2; 344 T0(); 345 for (i = 0; i < 400; i++) { 346 rdsect(fd, b0, sectorsize); 347 b0 += step; 348 } 349 TN(400); 350 351 printf("\tShort backward:\t"); 352 b0 = sectorcount / 2; 353 T0(); 354 for (i = 0; i < 400; i++) { 355 rdsect(fd, b0, sectorsize); 356 b0 -= step; 357 } 358 TN(400); 359 360 printf("\tSeq outer:\t"); 361 b0 = 0; 362 T0(); 363 for (i = 0; i < 2048; i++) { 364 rdsect(fd, b0, sectorsize); 365 b0++; 366 } 367 TN(2048); 368 369 printf("\tSeq inner:\t"); 370 b0 = sectorcount - 2048; 371 T0(); 372 for (i = 0; i < 2048; i++) { 373 rdsect(fd, b0, sectorsize); 374 b0++; 375 } 376 TN(2048); 377 378 printf("\nTransfer rates:\n"); 379 printf("\toutside: "); 380 rdsect(fd, 0, sectorsize); 381 T0(); 382 for (i = 0; i < bulk; i++) { 383 rdmega(fd); 384 } 385 TR(bulk * 1024); 386 387 printf("\tmiddle: "); 388 b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1; 389 rdsect(fd, b0, sectorsize); 390 T0(); 391 for (i = 0; i < bulk; i++) { 392 rdmega(fd); 393 } 394 TR(bulk * 1024); 395 396 printf("\tinside: "); 397 b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1; 398 rdsect(fd, b0, sectorsize); 399 T0(); 400 for (i = 0; i < bulk; i++) { 401 rdmega(fd); 402 } 403 TR(bulk * 1024); 404 405 printf("\n"); 406 return; 407 } 408 409 static void 410 commandtime(int fd, off_t mediasize, u_int sectorsize) 411 { 412 double dtmega, dtsector; 413 int i; 414 415 printf("I/O command overhead:\n"); 416 i = mediasize; 417 rdsect(fd, 0, sectorsize); 418 T0(); 419 for (i = 0; i < 10; i++) 420 rdmega(fd); 421 dtmega = delta_t(); 422 423 printf("\ttime to read 10MB block %10.6f sec\t= %8.3f msec/sector\n", 424 dtmega, dtmega*100/2048); 425 426 rdsect(fd, 0, sectorsize); 427 T0(); 428 for (i = 0; i < 20480; i++) 429 rdsect(fd, 0, sectorsize); 430 dtsector = delta_t(); 431 432 printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n", 433 dtsector, dtsector*100/2048); 434 printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n", 435 (dtsector - dtmega)*100/2048); 436 437 printf("\n"); 438 return; 439 } 440 441 static void 442 iops(int fd, off_t mediasize, u_int sectorsize) 443 { 444 struct aiocb aios[NAIO], *aiop; 445 ssize_t ret; 446 off_t sectorcount; 447 int error, i, queued, completed; 448 449 sectorcount = mediasize / sectorsize; 450 451 for (i = 0; i < NAIO; i++) { 452 aiop = &(aios[i]); 453 bzero(aiop, sizeof(*aiop)); 454 aiop->aio_buf = malloc(sectorsize); 455 if (aiop->aio_buf == NULL) 456 err(1, "malloc"); 457 } 458 459 T0(); 460 for (i = 0; i < NAIO; i++) { 461 aiop = &(aios[i]); 462 463 aiop->aio_fildes = fd; 464 aiop->aio_offset = (random() % (sectorcount)) * sectorsize; 465 aiop->aio_nbytes = sectorsize; 466 467 error = aio_read(aiop); 468 if (error != 0) 469 err(1, "aio_read"); 470 } 471 472 queued = i; 473 completed = 0; 474 475 for (;;) { 476 ret = aio_waitcomplete(&aiop, NULL); 477 if (ret < 0) 478 err(1, "aio_waitcomplete"); 479 if (ret != (ssize_t)sectorsize) 480 errx(1, "short read"); 481 482 completed++; 483 484 if (delta_t() < 3.0) { 485 aiop->aio_fildes = fd; 486 aiop->aio_offset = (random() % (sectorcount)) * sectorsize; 487 aiop->aio_nbytes = sectorsize; 488 489 error = aio_read(aiop); 490 if (error != 0) 491 err(1, "aio_read"); 492 493 queued++; 494 } else if (completed == queued) { 495 break; 496 } 497 } 498 499 TI(completed); 500 501 return; 502 } 503 504 static void 505 iopsbench(int fd, off_t mediasize, u_int sectorsize) 506 { 507 printf("Asynchronous random reads:\n"); 508 509 printf("\tsectorsize: "); 510 iops(fd, mediasize, sectorsize); 511 512 if (sectorsize != 4096) { 513 printf("\t4 kbytes: "); 514 iops(fd, mediasize, 4096); 515 } 516 517 printf("\t32 kbytes: "); 518 iops(fd, mediasize, 32 * 1024); 519 520 printf("\t128 kbytes: "); 521 iops(fd, mediasize, 128 * 1024); 522 523 printf("\n"); 524 } 525 526 static int 527 zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len) 528 { 529 struct disk_zone_args zone_args; 530 int error; 531 532 bzero(&zone_args, sizeof(zone_args)); 533 534 zone_args.zone_cmd = DISK_ZONE_GET_PARAMS; 535 error = ioctl(fd, DIOCZONECMD, &zone_args); 536 537 if (error == 0) { 538 *zone_mode = zone_args.zone_params.disk_params.zone_mode; 539 540 switch (*zone_mode) { 541 case DISK_ZONE_MODE_NONE: 542 snprintf(zone_str, zone_str_len, "Not_Zoned"); 543 break; 544 case DISK_ZONE_MODE_HOST_AWARE: 545 snprintf(zone_str, zone_str_len, "Host_Aware"); 546 break; 547 case DISK_ZONE_MODE_DRIVE_MANAGED: 548 snprintf(zone_str, zone_str_len, "Drive_Managed"); 549 break; 550 case DISK_ZONE_MODE_HOST_MANAGED: 551 snprintf(zone_str, zone_str_len, "Host_Managed"); 552 break; 553 default: 554 snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u", 555 *zone_mode); 556 break; 557 } 558 } 559 return (error); 560 } 561