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 snprintf(buf, BUFSIZ, "%s%s", _PATH_DEV, argv[i]); 108 fd = open(buf, O_RDONLY); 109 } 110 if (fd < 0) { 111 warn("%s", argv[i]); 112 exit(1); 113 } 114 error = fstat(fd, &sb); 115 if (error != 0) { 116 warn("cannot stat %s", argv[i]); 117 exitval = 1; 118 goto out; 119 } 120 if (S_ISREG(sb.st_mode)) { 121 mediasize = sb.st_size; 122 sectorsize = S_BLKSIZE; 123 fwsectors = 0; 124 fwheads = 0; 125 stripesize = sb.st_blksize; 126 stripeoffset = 0; 127 } else { 128 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 129 if (error) { 130 warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]); 131 exitval = 1; 132 goto out; 133 } 134 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 135 if (error) { 136 warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]); 137 exitval = 1; 138 goto out; 139 } 140 error = ioctl(fd, DIOCGFWSECTORS, &fwsectors); 141 if (error) 142 fwsectors = 0; 143 error = ioctl(fd, DIOCGFWHEADS, &fwheads); 144 if (error) 145 fwheads = 0; 146 error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize); 147 if (error) 148 stripesize = 0; 149 error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset); 150 if (error) 151 stripeoffset = 0; 152 error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc)); 153 if (error == 0) 154 zoned = 1; 155 } 156 if (!opt_v) { 157 printf("%s", argv[i]); 158 printf("\t%u", sectorsize); 159 printf("\t%jd", (intmax_t)mediasize); 160 printf("\t%jd", (intmax_t)mediasize/sectorsize); 161 printf("\t%jd", (intmax_t)stripesize); 162 printf("\t%jd", (intmax_t)stripeoffset); 163 if (fwsectors != 0 && fwheads != 0) { 164 printf("\t%jd", (intmax_t)mediasize / 165 (fwsectors * fwheads * sectorsize)); 166 printf("\t%u", fwheads); 167 printf("\t%u", fwsectors); 168 } 169 } else { 170 humanize_number(buf, 5, (int64_t)mediasize, "", 171 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 172 printf("%s\n", argv[i]); 173 printf("\t%-12u\t# sectorsize\n", sectorsize); 174 printf("\t%-12jd\t# mediasize in bytes (%s)\n", 175 (intmax_t)mediasize, buf); 176 printf("\t%-12jd\t# mediasize in sectors\n", 177 (intmax_t)mediasize/sectorsize); 178 printf("\t%-12jd\t# stripesize\n", stripesize); 179 printf("\t%-12jd\t# stripeoffset\n", stripeoffset); 180 if (fwsectors != 0 && fwheads != 0) { 181 printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize / 182 (fwsectors * fwheads * sectorsize)); 183 printf("\t%-12u\t# Heads according to firmware.\n", fwheads); 184 printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors); 185 } 186 if (ioctl(fd, DIOCGIDENT, ident) == 0) 187 printf("\t%-12s\t# Disk ident.\n", ident); 188 if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0) 189 printf("\t%-12s\t# Physical path\n", physpath); 190 if (zoned != 0) 191 printf("\t%-12s\t# Zone Mode\n", zone_desc); 192 } 193 printf("\n"); 194 if (opt_c) 195 commandtime(fd, mediasize, sectorsize); 196 if (opt_t) 197 speeddisk(fd, mediasize, sectorsize); 198 if (opt_i) 199 iopsbench(fd, mediasize, sectorsize); 200 out: 201 close(fd); 202 } 203 exit (exitval); 204 } 205 206 207 static char sector[65536]; 208 static char mega[1024 * 1024]; 209 210 static void 211 rdsect(int fd, off_t blockno, u_int sectorsize) 212 { 213 int error; 214 215 if (lseek(fd, (off_t)blockno * sectorsize, SEEK_SET) == -1) 216 err(1, "lseek"); 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 if (sectorcount <= 0) 300 return; /* Can't test devices with no sectors */ 301 302 step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1); 303 if (step > 16384) 304 step = 16384; 305 bulk = mediasize / (1024 * 1024); 306 if (bulk > 100) 307 bulk = 100; 308 309 printf("Seek times:\n"); 310 printf("\tFull stroke:\t"); 311 b0 = 0; 312 b1 = sectorcount - step; 313 T0(); 314 for (i = 0; i < 125; i++) { 315 rdsect(fd, b0, sectorsize); 316 b0 += step; 317 rdsect(fd, b1, sectorsize); 318 b1 -= step; 319 } 320 TN(250); 321 322 printf("\tHalf stroke:\t"); 323 b0 = sectorcount / 4; 324 b1 = b0 + sectorcount / 2; 325 T0(); 326 for (i = 0; i < 125; i++) { 327 rdsect(fd, b0, sectorsize); 328 b0 += step; 329 rdsect(fd, b1, sectorsize); 330 b1 += step; 331 } 332 TN(250); 333 printf("\tQuarter stroke:\t"); 334 b0 = sectorcount / 4; 335 b1 = b0 + sectorcount / 4; 336 T0(); 337 for (i = 0; i < 250; i++) { 338 rdsect(fd, b0, sectorsize); 339 b0 += step; 340 rdsect(fd, b1, sectorsize); 341 b1 += step; 342 } 343 TN(500); 344 345 printf("\tShort forward:\t"); 346 b0 = sectorcount / 2; 347 T0(); 348 for (i = 0; i < 400; i++) { 349 rdsect(fd, b0, sectorsize); 350 b0 += step; 351 } 352 TN(400); 353 354 printf("\tShort backward:\t"); 355 b0 = sectorcount / 2; 356 T0(); 357 for (i = 0; i < 400; i++) { 358 rdsect(fd, b0, sectorsize); 359 b0 -= step; 360 } 361 TN(400); 362 363 printf("\tSeq outer:\t"); 364 b0 = 0; 365 T0(); 366 for (i = 0; i < 2048; i++) { 367 rdsect(fd, b0, sectorsize); 368 b0++; 369 } 370 TN(2048); 371 372 printf("\tSeq inner:\t"); 373 b0 = sectorcount - 2048; 374 T0(); 375 for (i = 0; i < 2048; i++) { 376 rdsect(fd, b0, sectorsize); 377 b0++; 378 } 379 TN(2048); 380 381 printf("\nTransfer rates:\n"); 382 printf("\toutside: "); 383 rdsect(fd, 0, sectorsize); 384 T0(); 385 for (i = 0; i < bulk; i++) { 386 rdmega(fd); 387 } 388 TR(bulk * 1024); 389 390 printf("\tmiddle: "); 391 b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1; 392 rdsect(fd, b0, sectorsize); 393 T0(); 394 for (i = 0; i < bulk; i++) { 395 rdmega(fd); 396 } 397 TR(bulk * 1024); 398 399 printf("\tinside: "); 400 b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1; 401 rdsect(fd, b0, sectorsize); 402 T0(); 403 for (i = 0; i < bulk; i++) { 404 rdmega(fd); 405 } 406 TR(bulk * 1024); 407 408 printf("\n"); 409 return; 410 } 411 412 static void 413 commandtime(int fd, off_t mediasize, u_int sectorsize) 414 { 415 double dtmega, dtsector; 416 int i; 417 418 printf("I/O command overhead:\n"); 419 i = mediasize; 420 rdsect(fd, 0, sectorsize); 421 T0(); 422 for (i = 0; i < 10; i++) 423 rdmega(fd); 424 dtmega = delta_t(); 425 426 printf("\ttime to read 10MB block %10.6f sec\t= %8.3f msec/sector\n", 427 dtmega, dtmega*100/2048); 428 429 rdsect(fd, 0, sectorsize); 430 T0(); 431 for (i = 0; i < 20480; i++) 432 rdsect(fd, 0, sectorsize); 433 dtsector = delta_t(); 434 435 printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n", 436 dtsector, dtsector*100/2048); 437 printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n", 438 (dtsector - dtmega)*100/2048); 439 440 printf("\n"); 441 return; 442 } 443 444 static void 445 iops(int fd, off_t mediasize, u_int sectorsize) 446 { 447 struct aiocb aios[NAIO], *aiop; 448 ssize_t ret; 449 off_t sectorcount; 450 int error, i, queued, completed; 451 452 sectorcount = mediasize / sectorsize; 453 454 for (i = 0; i < NAIO; i++) { 455 aiop = &(aios[i]); 456 bzero(aiop, sizeof(*aiop)); 457 aiop->aio_buf = malloc(sectorsize); 458 if (aiop->aio_buf == NULL) 459 err(1, "malloc"); 460 } 461 462 T0(); 463 for (i = 0; i < NAIO; i++) { 464 aiop = &(aios[i]); 465 466 aiop->aio_fildes = fd; 467 aiop->aio_offset = (random() % (sectorcount)) * sectorsize; 468 aiop->aio_nbytes = sectorsize; 469 470 error = aio_read(aiop); 471 if (error != 0) 472 err(1, "aio_read"); 473 } 474 475 queued = i; 476 completed = 0; 477 478 for (;;) { 479 ret = aio_waitcomplete(&aiop, NULL); 480 if (ret < 0) 481 err(1, "aio_waitcomplete"); 482 if (ret != (ssize_t)sectorsize) 483 errx(1, "short read"); 484 485 completed++; 486 487 if (delta_t() < 3.0) { 488 aiop->aio_fildes = fd; 489 aiop->aio_offset = (random() % (sectorcount)) * sectorsize; 490 aiop->aio_nbytes = sectorsize; 491 492 error = aio_read(aiop); 493 if (error != 0) 494 err(1, "aio_read"); 495 496 queued++; 497 } else if (completed == queued) { 498 break; 499 } 500 } 501 502 TI(completed); 503 504 return; 505 } 506 507 static void 508 iopsbench(int fd, off_t mediasize, u_int sectorsize) 509 { 510 printf("Asynchronous random reads:\n"); 511 512 printf("\tsectorsize: "); 513 iops(fd, mediasize, sectorsize); 514 515 if (sectorsize != 4096) { 516 printf("\t4 kbytes: "); 517 iops(fd, mediasize, 4096); 518 } 519 520 printf("\t32 kbytes: "); 521 iops(fd, mediasize, 32 * 1024); 522 523 printf("\t128 kbytes: "); 524 iops(fd, mediasize, 128 * 1024); 525 526 printf("\n"); 527 } 528 529 static int 530 zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len) 531 { 532 struct disk_zone_args zone_args; 533 int error; 534 535 bzero(&zone_args, sizeof(zone_args)); 536 537 zone_args.zone_cmd = DISK_ZONE_GET_PARAMS; 538 error = ioctl(fd, DIOCZONECMD, &zone_args); 539 540 if (error == 0) { 541 *zone_mode = zone_args.zone_params.disk_params.zone_mode; 542 543 switch (*zone_mode) { 544 case DISK_ZONE_MODE_NONE: 545 snprintf(zone_str, zone_str_len, "Not_Zoned"); 546 break; 547 case DISK_ZONE_MODE_HOST_AWARE: 548 snprintf(zone_str, zone_str_len, "Host_Aware"); 549 break; 550 case DISK_ZONE_MODE_DRIVE_MANAGED: 551 snprintf(zone_str, zone_str_len, "Drive_Managed"); 552 break; 553 case DISK_ZONE_MODE_HOST_MANAGED: 554 snprintf(zone_str, zone_str_len, "Host_Managed"); 555 break; 556 default: 557 snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u", 558 *zone_mode); 559 break; 560 } 561 } 562 return (error); 563 } 564