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