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/disk.h> 44 #include <sys/param.h> 45 #include <sys/time.h> 46 47 static void 48 usage(void) 49 { 50 fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n"); 51 exit (1); 52 } 53 54 static int opt_c, opt_t, opt_v; 55 56 static void speeddisk(int fd, off_t mediasize, u_int sectorsize); 57 static void commandtime(int fd, off_t mediasize, u_int sectorsize); 58 static int zonecheck(int fd, uint32_t *zone_mode, char *zone_str, 59 size_t zone_str_len); 60 61 int 62 main(int argc, char **argv) 63 { 64 int i, ch, fd, error, exitval = 0; 65 char buf[BUFSIZ], ident[DISK_IDENT_SIZE], physpath[MAXPATHLEN]; 66 char zone_desc[64]; 67 off_t mediasize, stripesize, stripeoffset; 68 u_int sectorsize, fwsectors, fwheads, zoned = 0; 69 uint32_t zone_mode; 70 71 while ((ch = getopt(argc, argv, "ctv")) != -1) { 72 switch (ch) { 73 case 'c': 74 opt_c = 1; 75 opt_v = 1; 76 break; 77 case 't': 78 opt_t = 1; 79 opt_v = 1; 80 break; 81 case 'v': 82 opt_v = 1; 83 break; 84 default: 85 usage(); 86 } 87 } 88 argc -= optind; 89 argv += optind; 90 91 if (argc < 1) 92 usage(); 93 94 for (i = 0; i < argc; i++) { 95 fd = open(argv[i], O_RDONLY); 96 if (fd < 0 && errno == ENOENT && *argv[i] != '/') { 97 sprintf(buf, "%s%s", _PATH_DEV, argv[i]); 98 fd = open(buf, O_RDONLY); 99 } 100 if (fd < 0) { 101 warn("%s", argv[i]); 102 exitval = 1; 103 goto out; 104 } 105 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 106 if (error) { 107 warnx("%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]); 108 exitval = 1; 109 goto out; 110 } 111 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 112 if (error) { 113 warnx("%s: ioctl(DIOCGSECTORSIZE) failed, probably not a disk.", argv[i]); 114 exitval = 1; 115 goto out; 116 } 117 error = ioctl(fd, DIOCGFWSECTORS, &fwsectors); 118 if (error) 119 fwsectors = 0; 120 error = ioctl(fd, DIOCGFWHEADS, &fwheads); 121 if (error) 122 fwheads = 0; 123 error = ioctl(fd, DIOCGSTRIPESIZE, &stripesize); 124 if (error) 125 stripesize = 0; 126 error = ioctl(fd, DIOCGSTRIPEOFFSET, &stripeoffset); 127 if (error) 128 stripeoffset = 0; 129 error = zonecheck(fd, &zone_mode, zone_desc, sizeof(zone_desc)); 130 if (error == 0) 131 zoned = 1; 132 if (!opt_v) { 133 printf("%s", argv[i]); 134 printf("\t%u", sectorsize); 135 printf("\t%jd", (intmax_t)mediasize); 136 printf("\t%jd", (intmax_t)mediasize/sectorsize); 137 printf("\t%jd", (intmax_t)stripesize); 138 printf("\t%jd", (intmax_t)stripeoffset); 139 if (fwsectors != 0 && fwheads != 0) { 140 printf("\t%jd", (intmax_t)mediasize / 141 (fwsectors * fwheads * sectorsize)); 142 printf("\t%u", fwheads); 143 printf("\t%u", fwsectors); 144 } 145 } else { 146 humanize_number(buf, 5, (int64_t)mediasize, "", 147 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 148 printf("%s\n", argv[i]); 149 printf("\t%-12u\t# sectorsize\n", sectorsize); 150 printf("\t%-12jd\t# mediasize in bytes (%s)\n", 151 (intmax_t)mediasize, buf); 152 printf("\t%-12jd\t# mediasize in sectors\n", 153 (intmax_t)mediasize/sectorsize); 154 printf("\t%-12jd\t# stripesize\n", stripesize); 155 printf("\t%-12jd\t# stripeoffset\n", stripeoffset); 156 if (fwsectors != 0 && fwheads != 0) { 157 printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize / 158 (fwsectors * fwheads * sectorsize)); 159 printf("\t%-12u\t# Heads according to firmware.\n", fwheads); 160 printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors); 161 } 162 if (ioctl(fd, DIOCGIDENT, ident) == 0) 163 printf("\t%-12s\t# Disk ident.\n", ident); 164 if (ioctl(fd, DIOCGPHYSPATH, physpath) == 0) 165 printf("\t%-12s\t# Physical path\n", physpath); 166 if (zoned != 0) 167 printf("\t%-12s\t# Zone Mode\n", zone_desc); 168 } 169 printf("\n"); 170 if (opt_c) 171 commandtime(fd, mediasize, sectorsize); 172 if (opt_t) 173 speeddisk(fd, mediasize, sectorsize); 174 out: 175 close(fd); 176 } 177 exit (exitval); 178 } 179 180 181 static char sector[65536]; 182 static char mega[1024 * 1024]; 183 184 static void 185 rdsect(int fd, off_t blockno, u_int sectorsize) 186 { 187 int error; 188 189 lseek(fd, (off_t)blockno * sectorsize, SEEK_SET); 190 error = read(fd, sector, sectorsize); 191 if (error == -1) 192 err(1, "read"); 193 if (error != (int)sectorsize) 194 errx(1, "disk too small for test."); 195 } 196 197 static void 198 rdmega(int fd) 199 { 200 int error; 201 202 error = read(fd, mega, sizeof(mega)); 203 if (error == -1) 204 err(1, "read"); 205 if (error != sizeof(mega)) 206 errx(1, "disk too small for test."); 207 } 208 209 static struct timeval tv1, tv2; 210 211 static void 212 T0(void) 213 { 214 215 fflush(stdout); 216 sync(); 217 sleep(1); 218 sync(); 219 sync(); 220 gettimeofday(&tv1, NULL); 221 } 222 223 static void 224 TN(int count) 225 { 226 double dt; 227 228 gettimeofday(&tv2, NULL); 229 dt = (tv2.tv_usec - tv1.tv_usec) / 1e6; 230 dt += (tv2.tv_sec - tv1.tv_sec); 231 printf("%5d iter in %10.6f sec = %8.3f msec\n", 232 count, dt, dt * 1000.0 / count); 233 } 234 235 static void 236 TR(double count) 237 { 238 double dt; 239 240 gettimeofday(&tv2, NULL); 241 dt = (tv2.tv_usec - tv1.tv_usec) / 1e6; 242 dt += (tv2.tv_sec - tv1.tv_sec); 243 printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n", 244 count, dt, count / dt); 245 } 246 247 static void 248 speeddisk(int fd, off_t mediasize, u_int sectorsize) 249 { 250 int bulk, i; 251 off_t b0, b1, sectorcount, step; 252 253 sectorcount = mediasize / sectorsize; 254 step = 1ULL << (flsll(sectorcount / (4 * 200)) - 1); 255 if (step > 16384) 256 step = 16384; 257 bulk = mediasize / (1024 * 1024); 258 if (bulk > 100) 259 bulk = 100; 260 261 printf("Seek times:\n"); 262 printf("\tFull stroke:\t"); 263 b0 = 0; 264 b1 = sectorcount - step; 265 T0(); 266 for (i = 0; i < 125; i++) { 267 rdsect(fd, b0, sectorsize); 268 b0 += step; 269 rdsect(fd, b1, sectorsize); 270 b1 -= step; 271 } 272 TN(250); 273 274 printf("\tHalf stroke:\t"); 275 b0 = sectorcount / 4; 276 b1 = b0 + sectorcount / 2; 277 T0(); 278 for (i = 0; i < 125; i++) { 279 rdsect(fd, b0, sectorsize); 280 b0 += step; 281 rdsect(fd, b1, sectorsize); 282 b1 += step; 283 } 284 TN(250); 285 printf("\tQuarter stroke:\t"); 286 b0 = sectorcount / 4; 287 b1 = b0 + sectorcount / 4; 288 T0(); 289 for (i = 0; i < 250; i++) { 290 rdsect(fd, b0, sectorsize); 291 b0 += step; 292 rdsect(fd, b1, sectorsize); 293 b1 += step; 294 } 295 TN(500); 296 297 printf("\tShort forward:\t"); 298 b0 = sectorcount / 2; 299 T0(); 300 for (i = 0; i < 400; i++) { 301 rdsect(fd, b0, sectorsize); 302 b0 += step; 303 } 304 TN(400); 305 306 printf("\tShort backward:\t"); 307 b0 = sectorcount / 2; 308 T0(); 309 for (i = 0; i < 400; i++) { 310 rdsect(fd, b0, sectorsize); 311 b0 -= step; 312 } 313 TN(400); 314 315 printf("\tSeq outer:\t"); 316 b0 = 0; 317 T0(); 318 for (i = 0; i < 2048; i++) { 319 rdsect(fd, b0, sectorsize); 320 b0++; 321 } 322 TN(2048); 323 324 printf("\tSeq inner:\t"); 325 b0 = sectorcount - 2048; 326 T0(); 327 for (i = 0; i < 2048; i++) { 328 rdsect(fd, b0, sectorsize); 329 b0++; 330 } 331 TN(2048); 332 333 printf("Transfer rates:\n"); 334 printf("\toutside: "); 335 rdsect(fd, 0, sectorsize); 336 T0(); 337 for (i = 0; i < bulk; i++) { 338 rdmega(fd); 339 } 340 TR(bulk * 1024); 341 342 printf("\tmiddle: "); 343 b0 = sectorcount / 2 - bulk * (1024*1024 / sectorsize) / 2 - 1; 344 rdsect(fd, b0, sectorsize); 345 T0(); 346 for (i = 0; i < bulk; i++) { 347 rdmega(fd); 348 } 349 TR(bulk * 1024); 350 351 printf("\tinside: "); 352 b0 = sectorcount - bulk * (1024*1024 / sectorsize) - 1; 353 rdsect(fd, b0, sectorsize); 354 T0(); 355 for (i = 0; i < bulk; i++) { 356 rdmega(fd); 357 } 358 TR(bulk * 1024); 359 360 printf("\n"); 361 return; 362 } 363 364 static void 365 commandtime(int fd, off_t mediasize, u_int sectorsize) 366 { 367 double dtmega, dtsector; 368 int i; 369 370 printf("I/O command overhead:\n"); 371 i = mediasize; 372 rdsect(fd, 0, sectorsize); 373 T0(); 374 for (i = 0; i < 10; i++) 375 rdmega(fd); 376 gettimeofday(&tv2, NULL); 377 dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6; 378 dtmega += (tv2.tv_sec - tv1.tv_sec); 379 380 printf("\ttime to read 10MB block %10.6f sec\t= %8.3f msec/sector\n", 381 dtmega, dtmega*100/2048); 382 383 rdsect(fd, 0, sectorsize); 384 T0(); 385 for (i = 0; i < 20480; i++) 386 rdsect(fd, 0, sectorsize); 387 gettimeofday(&tv2, NULL); 388 dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6; 389 dtsector += (tv2.tv_sec - tv1.tv_sec); 390 391 printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n", 392 dtsector, dtsector*100/2048); 393 printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n", 394 (dtsector - dtmega)*100/2048); 395 396 printf("\n"); 397 return; 398 } 399 400 static int 401 zonecheck(int fd, uint32_t *zone_mode, char *zone_str, size_t zone_str_len) 402 { 403 struct disk_zone_args zone_args; 404 int error; 405 406 bzero(&zone_args, sizeof(zone_args)); 407 408 zone_args.zone_cmd = DISK_ZONE_GET_PARAMS; 409 error = ioctl(fd, DIOCZONECMD, &zone_args); 410 411 if (error == 0) { 412 *zone_mode = zone_args.zone_params.disk_params.zone_mode; 413 414 switch (*zone_mode) { 415 case DISK_ZONE_MODE_NONE: 416 snprintf(zone_str, zone_str_len, "Not_Zoned"); 417 break; 418 case DISK_ZONE_MODE_HOST_AWARE: 419 snprintf(zone_str, zone_str_len, "Host_Aware"); 420 break; 421 case DISK_ZONE_MODE_DRIVE_MANAGED: 422 snprintf(zone_str, zone_str_len, "Drive_Managed"); 423 break; 424 case DISK_ZONE_MODE_HOST_MANAGED: 425 snprintf(zone_str, zone_str_len, "Host_Managed"); 426 break; 427 default: 428 snprintf(zone_str, zone_str_len, "Unknown_zone_mode_%u", 429 *zone_mode); 430 break; 431 } 432 } 433 return (error); 434 } 435