1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1994 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 /* sadp.c 1.14.1.12 of 8/9/89 */ 33 /* sadp.c - For VAX and PDP11 machines, 34 disk profiler profiles rp06, rm05 and general disk drives. 35 It reads system buffer header pool, physical buffer header 36 pool and swap buffer header pool once every second, 37 to examine disk drive's I/O queue. 38 For 3b20s system, it profiles the regular disk drives, 39 it reads the circular output queue for each drive 40 once every second. 41 usage : sadp [-th][-d device[-drive]] s [n] 42 */ 43 44 #include <stdio.h> 45 #include <sys/types.h> 46 #include <sys/param.h> 47 #include <sys/sysmacros.h> 48 #include <sys/buf.h> 49 #include <sys/elog.h> 50 #include <nlist.h> 51 52 #include <string.h> 53 #include <fcntl.h> 54 #include <sys/dkio.h> 55 #ifdef FIXME 56 #include <sys/dk.h> 57 #endif 58 59 #include <time.h> 60 #include <sys/utsname.h> 61 #include <sys/var.h> 62 #include <ctype.h> 63 #include <sys/sysinfo.h> 64 #include <kvm.h> 65 66 /* 67 * These includes are for dealing with scsi targets. 68 */ 69 #include <sys/dditypes.h> 70 #include <sys/scsi/scsi.h> 71 #include <sys/scsi/conf/device.h> 72 #include <sys/scsi/targets/sddef.h> 73 74 75 /* cylinder profiling */ 76 #define BLANK ' ' 77 #define BLOB '*' 78 #define TRACE '.' 79 #define BRK '=' 80 #define FOOT '-' 81 #define CYLNO 1 82 #define SEEKD 2 83 84 struct dk_geom dk_geom; 85 #define CHUNK 16 86 #define CHUNKSHIFT 4 87 #define PHYS_CYL dk_geom.dkg_pcyl 88 #define CHPERCYL (int)(PHYS_CYL/CHUNK) /* 89 * the number of CHUNK cylinder 90 * chunks on a disk 91 */ 92 #define SECTPERTRK (int)(dk_geom.dkg_nsect) /* sectors per track */ 93 #define SECTPERCYL (SECTPERTRK * (int)(dk_geom.dkg_nhead)) 94 #define ERR_BAD_DEV "Device %s is not defined, valid devices are: " 95 #define ERR_BAD_UNIT \ 96 "Invalid drive specified for device %s, valid drives are: " 97 #define ERR_NO_DEV "Please specify a device type, valid devices are: " 98 #define DRVNUM(devname) strpbrk(devname, "0123456789") 99 100 #define cylin b_resid 101 #define NDRIVE 10 102 #define SNDRIVE NDRIVE /* Not used */ 103 #define MAX_HDISK_REP NDRIVE 104 #define MAXDRIVE 20 /* maximum number of configured disks */ 105 #define NAMESIZE 10 /* size of device names */ 106 107 108 struct nlist setup[] = { 109 110 #define X1_V 0 111 {"v"}, 112 #define X1_BUF 1 113 {"buf"}, 114 #define X1_PBUF 2 115 {"pbuf"}, 116 #define X1_SDUNITS 3 117 {"sdunits"}, 118 #ifdef FIXME 119 #define X1_DK_NDRIVE 4 120 {"dk_ndrive"}, 121 #define X1_DK_BUSY 5 122 {"dk_busy"}, 123 #define X1_DK_TIME 6 124 {"dk_time"}, 125 #define X1_DK_SEEK 7 126 {"dk_seek"}, 127 #define X1_DK_XFER 8 128 {"dk_xfer"}, 129 #define X1_DK_WDS 9 130 {"dk_wds"}, 131 #define X1_DK_BPS 10 132 {"dk_bps"}, 133 #define X1_DK_READ 11 134 {"dk_read"}, 135 #define X1_DK_IVEC 12 136 {"dk_ivec"}, 137 #define X1_NUMSYMBOLS 13 138 #else 139 #define X1_NUMSYMBOLS 4 140 #endif 141 {0} 142 }; 143 144 void do_disk_stats (); 145 void usage (); 146 void prthist (); 147 void pline (); 148 void cylhdr (); 149 void cylftr (); 150 void cylhist (); 151 void validate_device (); 152 void validate_drive (); 153 void init_geom (); 154 void bad_device (); 155 void read_devinfo_names (); 156 void fail (); 157 void init_disk (); 158 void safe_kvm_read (); 159 160 #undef n_name /* to resolve conflict with <syms.h> */ 161 #define MAXINTSIZE 12 /* sizeof "-2147483648" + 1 */ 162 163 int debug = 1; 164 165 #define Debug if (debug) 166 #define dfprintf if (debug) fprintf 167 168 /* 169 * FETCH_XYZ naming convention: 170 * X = I for nlist index, A for actual address 171 * Y = V for regular variable, A for array variable 172 * Z = L if length explicitly specified 173 */ 174 175 #define FETCH_AAL(addr, var, len, vname)\ 176 safe_kvm_read(kd, (unsigned long) addr, \ 177 (char *) var, len, vname) 178 179 #define FETCH_IV(index, var)\ 180 safe_kvm_read(kd, (unsigned long) setup[index].n_value, \ 181 (char *) &var, sizeof (var), setup[index].n_name) 182 183 #define FETCH_IAL(index, var, len)\ 184 safe_kvm_read(kd, (unsigned long)setup[index].n_value, \ 185 (char *) var, len, setup[index].n_name) 186 187 int dk_ndrive; 188 int ndrives; 189 int all = 0; /* 190 * indicate whether all drives 191 * are implicitly specified 192 */ 193 char *cmdname = "sadp"; 194 char device[NAMESIZE]; 195 char dr_name[NDRIVE][NAMESIZE]; 196 long dk_bps[NDRIVE]; 197 198 struct { 199 long dk_busy[NDRIVE]; 200 long dk_time[NDRIVE]; 201 long dk_wds[NDRIVE]; 202 long dk_seek[NDRIVE]; 203 long dk_xfer[NDRIVE]; 204 long dk_read[NDRIVE]; 205 } dk; 206 207 struct var tbl; 208 char *sbuf, *phybuf; 209 struct buf bp[2]; /* for swap buffers */ 210 int nonblk; 211 int index; 212 int index1; 213 unsigned temp1; 214 #define devnm dr_name 215 216 int fflg, dflg, tflg, hflg, errflg; 217 int s, n, ct; 218 static int ub = 8; 219 int sdist; 220 int m; 221 int dev; 222 int temp; 223 int f; 224 int i; 225 int n1, dleng, dashb, k, devlen; 226 int dashf; 227 int dn; 228 int drvlist[NDRIVE]; 229 230 int Sdrvlist[SNDRIVE]; /* SCSI */ 231 232 struct HISTDATA { 233 long hdata[1]; 234 }; 235 struct utsname name; 236 237 char *nopt; 238 char empty[30]; 239 char drive[30]; 240 char *malloc(); 241 242 int SCSI; /* SCSI */ 243 int ALL; 244 245 long lseek(); 246 long **dkcyl; 247 long **skcyl; 248 long *iocnt; 249 static kvm_t *kd = NULL; 250 struct scsi_device *sdunits[SD_MAXUNIT]; 251 struct scsi_device sdunit[NDRIVE]; 252 int cyl_no, prev_cyl_no; 253 int cyl_bk, prev_cyl_bk; 254 int seek_dist, seek_bk; 255 int max_cyl_no = 0; 256 int max_seek_dist = 0; 257 258 main(argc, argv) 259 int argc; 260 char **argv; 261 { 262 unsigned sleep(); 263 extern int optind; 264 extern char *optarg; 265 int c, j; 266 char *ctime(), *stime; 267 long curt; 268 extern time_t time(); 269 long *skdist; 270 long *disk; 271 272 fail("sadp does not work yet -- no disk statistics in the kernel", 0); 273 274 while ((c = getopt(argc, argv, "thd:")) != EOF) 275 switch (c) { 276 case 't': 277 tflg++; 278 break; 279 case 'h': 280 hflg++; 281 break; 282 case 'd': 283 dleng = strlen(optarg); 284 285 /* 286 * Controller types can be arbitrary length. 287 */ 288 devlen = strchr(optarg, '-') ? 289 strchr(optarg, '-') - optarg : dleng; 290 SCSI = 0; 291 strncpy(device, optarg, devlen); 292 293 if (dleng == (devlen+1)) { 294 errflg++; 295 break; 296 } 297 if (dleng > devlen) { 298 for (i = (devlen+1), n1 = (devlen+1); i < dleng; i++){ 299 300 if (optarg[i] == ','){ 301 if (n1 == i){ 302 errflg++; 303 break; 304 } 305 if (getdrvn() != 0) { 306 errflg++; 307 break; 308 } 309 if (dashf != 0) { 310 if (dashb >= dn){ 311 errflg++; 312 break; 313 } 314 for (j = dashb; j < dn+1; j++){ 315 if (SCSI) /* SCSI */ 316 Sdrvlist[j] = 1; 317 else 318 drvlist[j] = 1; 319 } 320 dashb = 0; 321 dashf = 0; 322 } 323 else 324 { 325 if (SCSI) 326 Sdrvlist[dn] = 1; 327 else 328 drvlist[dn] = 1; 329 } 330 n1 = i+1; 331 } else { 332 if (optarg[i] == '-'){ 333 if (dashf != 0) { 334 errflg++; 335 break; 336 } 337 if (getdrvn() != 0) { 338 errflg++; 339 break; 340 } 341 if (SCSI) 342 Sdrvlist[dn] = 1; 343 else 344 drvlist[dn] = 1; 345 dashb = dn; 346 dashf = 1; 347 n1 = i+1; 348 } else { 349 if (i == dleng-1){ 350 i++; 351 if (getdrvn() != 0) { 352 errflg++; 353 break; 354 } 355 if (dashf != 0) 356 for (j = dashb; j < dn+1; j++){ 357 if (SCSI) 358 Sdrvlist[j] = 1; 359 else 360 drvlist[j] = 1; 361 } 362 else 363 { 364 if (SCSI) 365 Sdrvlist[dn] = 1; 366 else 367 drvlist[dn] = 1; 368 } 369 } 370 } 371 } 372 } 373 } else { 374 if (dleng != devlen){ 375 errflg++; 376 break; 377 } 378 all++; 379 if (SCSI) 380 ALL++; 381 else 382 for (i = 0; i < MAX_HDISK_REP; i++) 383 drvlist[i] = 1; 384 } 385 if (errflg) 386 break; 387 dflg++; 388 break; 389 case '?': 390 errflg++; 391 break; 392 } 393 if (errflg) { 394 fprintf (stderr, "%s: errors in arguments\n", cmdname); 395 usage(); 396 } 397 398 /* 399 * If no frequency arguments present, exit. 400 */ 401 if (optind == argc) 402 usage(); 403 /* 404 * If a non-dash field is presented as an argument, 405 * check if it is a numerical arg. 406 */ 407 nopt = argv[optind]; 408 if (tstdigit(nopt) != 0) 409 usage(); 410 /* 411 * For frequency arguments, if only s is presented , set n to 1 412 */ 413 if ((optind +1) == argc) { 414 s = atoi(argv[optind]); 415 n = 1; 416 } 417 /* 418 * If both s and n are specified, check if 419 * arg n is numeric. 420 */ 421 if ((optind +1) < argc) { 422 nopt = argv[optind + 1]; 423 if (tstdigit(nopt) != 0) 424 usage(); 425 s = atoi(argv[optind]); 426 n = atoi(argv[optind+1]); 427 } 428 if (s <= 0) 429 fail("bad value of s", 0); 430 if (n <= 0) 431 fail("bad value of n", 0); 432 ct = s; 433 /* 434 * Get entries defined in setup from /stand/unix 435 */ 436 if ((kd = kvm_open (NULL, NULL, NULL, O_RDONLY, "Bad kvm open")) 437 == NULL) 438 fail("kvm_open failed", 1); 439 440 dfprintf (stderr, "main: successful kvm_open\n"); 441 /* 442 * Search name list to get offsets. 443 */ 444 dfprintf (stderr, "main: about to kvm_nlist\n"); 445 if (kvm_nlist(kd, setup) == -1) { 446 fail("kvm_nlist failed", 1); 447 } 448 dfprintf (stderr, "main: good nlist\n"); 449 Debug dump_nlist (setup, "main"); 450 451 /* 452 * Initialize buffers and get disk info. 453 */ 454 init_disk(); 455 dfprintf (stderr, "init_disk\n"); 456 skdist = (long *)calloc(dk_ndrive, sizeof (long)); 457 disk = (long *)calloc(dk_ndrive, sizeof (long)); 458 459 /* 460 * Make sure device and drive specified is legitimate. 461 */ 462 validate_device(); 463 dfprintf (stderr, "validate_device\n"); 464 validate_drive(); 465 dfprintf (stderr, "validate_drive\n"); 466 467 /* 468 * Get storage from memory for sysbuf pool and physical buf pool 469 */ 470 FETCH_IV (X1_V, tbl); 471 Debug dump_v_struct (&tbl); 472 sbuf = malloc(sizeof (struct buf) * tbl.v_buf); 473 if (sbuf == NULL) 474 fail("malloc of sbuf failed", 1); 475 phybuf = malloc(sizeof (struct buf) * tbl.v_pbuf); 476 if (phybuf == NULL) 477 fail("malloc of physbuf failed", 1); 478 479 /* 480 * Determine the number of CHUNK cylinder chunks on the disk. 481 * This will be referenced as CHPERCYL. 482 */ 483 init_geom(); 484 dfprintf (stderr, "init_geom\n"); 485 486 ub = dk_ndrive; 487 #ifdef FIXME 488 FETCH_IAL(X1_DK_XFER, dk.dk_xfer, dk_ndrive*sizeof (long)); 489 FETCH_IAL(X1_DK_READ, dk.dk_read, dk_ndrive*sizeof (long)); 490 FETCH_IAL(X1_DK_SEEK, dk.dk_seek, dk_ndrive*sizeof (long)); 491 FETCH_IAL(X1_DK_WDS, dk.dk_wds, dk_ndrive*sizeof (long)); 492 FETCH_IAL(X1_DK_TIME, dk.dk_time, dk_ndrive*sizeof (long)); 493 #endif 494 dfprintf (stderr, "%s: ub %d\n", cmdname, ub); 495 /* 496 * Get the list of scsi device pointers from kernel space. 497 */ 498 FETCH_IAL(X1_SDUNITS, sdunits, SD_MAXUNIT); 499 for (i = 0; i < SD_MAXUNIT; i++) { 500 dfprintf (stderr, "sdunits[%d] 0x%x ", i, (int)sdunits[i]); 501 } 502 dfprintf (stderr, "\n"); 503 for (k = 0, i = 0; k < ub; k++) { 504 if (drvlist[k] == 0) 505 continue; 506 /* 507 * Make sure that there is a scsi_device struct for 508 * the chosen device. 509 */ 510 if (!sdunits[k]) { 511 fprintf (stderr, "%s: no valid scsi_device struct\n", 512 cmdname); 513 } 514 dfprintf (stderr, "%s: read unit %d\n", cmdname, k); 515 /* 516 * Read the scsi_device struct for the device. 517 */ 518 FETCH_AAL(sdunits[k], &sdunit[k], 519 sizeof (struct scsi_device), "sdunits"); 520 dfprintf (stderr, "%s: sd_private 0x%x\n", 521 cmdname, (int)sdunit[k].sd_private); 522 } 523 524 /* 525 * Get current I/O count for each drive. 526 */ 527 for (;;) { 528 s = ct; 529 for (k = 0, i = 0; k < ub; k++) { 530 if (drvlist[k] == 0) 531 continue; 532 for (j = 0; j < CHPERCYL; j++) { 533 dkcyl[i][j] = 0; 534 skcyl[i][j] = 0; 535 } 536 iocnt[i] = 0; 537 disk[i] = 0; 538 skdist[i] = 0; 539 i++; 540 } 541 /* 542 * If no drives are selected or illegal drive number 543 * is specified, exit. 544 */ 545 if (i == 0) 546 usage(); 547 548 /* 549 * Get i/o count for each disk. 550 */ 551 552 for (k = 0, i = 0; k < ub; k++) { 553 if (drvlist[k] == 0) 554 continue; 555 iocnt[i] = dk.dk_xfer[k]; 556 i++; 557 } 558 559 cyl_no = 0; prev_cyl_no = 0; cyl_bk = 0; prev_cyl_bk = 0; seek_dist = 0; 560 for (;;) { 561 562 /* 563 * Take a snapshot of buffer header pool, swap 564 * buffer pool and physical buffer header. 565 */ 566 567 /* 568 * read system buffer header pool. 569 */ 570 FETCH_IAL(X1_BUF, sbuf, tbl.v_buf*sizeof (struct buf)); 571 572 /* 573 * Read physical buffer header pool. 574 */ 575 FETCH_IAL(X1_PBUF, phybuf, tbl.v_pbuf*sizeof (struct buf)); 576 577 for (k = 0, i = 0; k < ub; k++) { 578 if (drvlist[k] == 0) 579 continue; 580 do_disk_stats (i, k); 581 i++; 582 dfprintf (stderr, "%s: i %d\n", cmdname, i); 583 } 584 585 /* TBD - get more samples */ 586 587 if (--s) 588 sleep(1); 589 else { 590 591 /* 592 * At the end of sampling, get the present I/O 593 * count, and system name. 594 */ 595 uname(&name); 596 597 /* 598 * Print the report, there are two parts: 599 * cylinder profile, seeking distance profile. 600 */ 601 curt = time((long *) 0); 602 stime = ctime (&curt); 603 printf("\n\n%s\n", stime); 604 printf("%s %s %s %s %s\n", 605 name.sysname, 606 name.nodename, 607 name.release, 608 name.version, 609 name.machine); 610 for (k = 0, i = 0; k < ub; k++) { 611 if (drvlist[k] == 0) 612 continue; 613 for (j = 0; j < CHPERCYL; j++) { 614 disk[i] = disk[i] +dkcyl[i][j]; 615 skdist[i] = skdist[i] + skcyl[i][j]; 616 617 } 618 i++; 619 } 620 if ((tflg == 0) && (hflg == 0)) 621 tflg = 1; 622 if (tflg){ 623 printf("\nCYLINDER ACCESS PROFILE\n"); 624 for (k = 0, i = 0; k < ub; k++) { 625 if (drvlist[k] == 0) 626 continue; 627 if (disk[i] != 0){ 628 iocnt[i] = dk.dk_xfer[k] - iocnt[i]; 629 printf("\n%s-%d:\n", 630 device, k); 631 printf("Cylinders\tTransfers\n"); 632 for (j = 0; j < CHPERCYL; j++) { 633 if (dkcyl[i][j] > 0) 634 printf("%3d - %3d\t%ld\n", 635 j*8, j*8+7, dkcyl[i][j]); 636 } 637 printf("\nSampled I/O = %ld, Actual I/O = %ld\n", 638 disk[i], iocnt[i]); 639 if (iocnt[i] > 0) 640 printf("Percentage of I/O sampled = %2.2f\n", 641 ((float)disk[i] /(float)iocnt[i]) * 100.0); 642 } 643 i++; 644 } 645 646 printf("\n\n\nSEEK DISTANCE PROFILE\n"); 647 for (k = 0, i = 0; k < ub; k++) { 648 if (drvlist[k] == 0) 649 continue; 650 if (skdist[i] != 0){ 651 printf("\n%s-%d:\n", 652 device, k); 653 printf("Seek Distance\tSeeks\n"); 654 for (j = 0; j < CHPERCYL; j++) 655 656 if (skcyl[i][j] > 0){ 657 if (j == 0) 658 printf(" 0\t%ld\n", 659 skcyl[i][j]); 660 else 661 printf("%3d - %3d\t%ld\n", 662 j*8-7, j*8, skcyl[i][j]); 663 } 664 printf("Total Seeks = %ld\n", skdist[i]); 665 } 666 i++; 667 } 668 } 669 if (hflg){ 670 for (k = 0, i = 0; k < ub; k++) { 671 if (drvlist[k] == 0) 672 continue; 673 if (disk[i] != 0) { 674 cylhdr(CYLNO, disk[i]); 675 cylhist(disk[i], dkcyl[i]); 676 cylftr(CYLNO); 677 } 678 i++; 679 } 680 for (k = 0, i = 0; k < ub; k++) { 681 if (drvlist[k] == 0) 682 continue; 683 if (skdist[i] != 0){ 684 cylhdr(SEEKD, skdist[i]); 685 cylhist(skdist[i], skcyl[i]); 686 cylftr(SEEKD); 687 } 688 i++; 689 } 690 } 691 692 break; 693 } 694 } 695 if (--n) 696 continue; 697 exit(0); 698 } 699 } 700 701 void 702 do_disk_stats (i, k) 703 { 704 #ifdef fixed 705 struct scsi_disk sddisk[NDRIVE]; 706 struct diskhd *dp; 707 struct buf buffer, *bp; 708 struct buf *last_bp = 0; 709 710 dfprintf (stderr, "do_disk_stats (i %d, k %d)\n", i, k); 711 /* 712 * In each scsi_device struct there is a sd_private 713 * pointer to a specialised scsi_disk struct which 714 * describes the disk. 715 */ 716 do { 717 FETCH_AAL(sdunit[k].sd_private, &sddisk[k], 718 sizeof (struct scsi_disk), "sdunit"); 719 /* 720 * The diskhd struct describing the active and waiting 721 * queues is embedded in the scsi_disk struct. 722 */ 723 dp = &sddisk[k].un_utab; 724 Debug dump_diskhd (dp); 725 /* 726 * The current SunOS sd.c driver uses the b_forw 727 * pointer for the currently active buffer, and the 728 * b_actf (av_forw) pointer for the waiting queue 729 * of buffers. 730 */ 731 dfprintf (stderr, "%s: b_forw 0x%x\n", cmdname, (int)dp->b_forw); 732 /* 733 * Trace disk queue for I/O location, seek distance. 734 */ 735 if (dp->b_forw) { 736 if (dp->b_forw == last_bp) { 737 continue; 738 } else { 739 last_bp = dp->b_forw; 740 } 741 dfprintf (stderr, "%s: b_forw 0x%x\n", 742 cmdname, (int)dp->b_forw); 743 FETCH_AAL(dp->b_forw, &buffer, sizeof (struct buf), 744 "b_forw"); 745 bp = &buffer; 746 dfprintf (stderr, "%s: b_lblkno 0x%x b_blkno 0x%x\n", 747 cmdname, bp->b_lblkno, bp->b_blkno); 748 cyl_no = bp->b_blkno / SECTPERCYL; 749 cyl_bk = cyl_no >> CHUNKSHIFT; 750 seek_dist = prev_cyl_no - cyl_no; 751 if (seek_dist < 0) 752 seek_dist = -seek_dist; 753 seek_bk = prev_cyl_bk - cyl_bk; 754 if (seek_bk < 0) 755 seek_bk = -seek_bk; 756 prev_cyl_no = cyl_no; 757 prev_cyl_bk = cyl_bk; 758 if (cyl_no > max_cyl_no) { 759 max_cyl_no = cyl_no; 760 } 761 if (seek_dist > max_seek_dist) { 762 max_seek_dist = seek_dist; 763 } 764 skcyl[i][seek_bk]++; 765 dkcyl[i][cyl_bk]++; 766 } 767 } while (dp->b_forw); 768 #endif 769 } 770 771 772 /* 773 * Determine if the I/O is from system buffer pool, 774 * or swap buffer pool or physical buffer. 775 */ 776 777 int 778 testbuf() 779 { 780 if ((temp1 < setup[X1_BUF].n_value) || (index > tbl.v_buf)){ 781 index = (int)(temp1 -setup[X1_PBUF].n_value)/ 782 (sizeof (struct buf)); 783 if (index < tbl.v_pbuf){ 784 nonblk = 1; 785 return (0); 786 787 } 788 /* TBD - Is it possible to access swap buffers on Sun? */ 789 #ifndef sun 790 index = (int)(temp1 -setup[SWP].n_value)/ 791 (sizeof (struct buf)); 792 if (index < NSWP) { 793 m = index; 794 nonblk = 2; 795 return (0); 796 } 797 #endif 798 return (-1); 799 } 800 return (0); 801 } 802 803 /* 804 * Verify the I/O, get the cylinder number. 805 */ 806 807 ckbits(x) 808 register struct buf *x; 809 { 810 register p; 811 for (p = 0; p < index; p++, x++) 812 continue; 813 if ((x->b_flags & B_BUSY) && 814 ((x->b_flags & B_DONE) == 0)){ 815 temp = x->cylin; 816 temp1 = (unsigned)x->av_forw; 817 return (0); 818 } 819 else 820 return (-1); 821 822 } 823 int 824 testdev() 825 { 826 if ((nonblk == 0) && (ckbits((struct buf *)sbuf) != -1)) 827 goto endtest; 828 else { 829 if ((nonblk == 1) && (ckbits((struct buf *)phybuf) != -1)) 830 goto endtest; 831 832 else { 833 834 if ((nonblk == 2) && 835 ((bp[m].b_flags & B_BUSY) && 836 ((bp[m].b_flags & B_DONE) == 0))){ 837 temp = bp[m].cylin; 838 temp1 = (unsigned)bp[m].av_forw; 839 } else { 840 dfprintf (stderr, "testdev -1\n"); 841 return (-1); 842 } 843 } 844 } 845 endtest: 846 dkcyl[i][temp >> 3]++; 847 return (0); 848 } 849 850 851 852 /* 853 * Get drive number routine. 854 */ 855 getdrvn() 856 { 857 extern char *optarg; 858 char *strcpy(); 859 char *strncat(); 860 861 strcpy(drive, empty); 862 strncat(drive, &optarg[n1], i-n1); 863 if (tstdigit(drive) != 0) 864 return (-1); 865 dn = atoi(drive); 866 if (SCSI) { 867 if (dn >= SNDRIVE) 868 return (-1); 869 } else { 870 if (dn >= NDRIVE) 871 return (-1); 872 } 873 return (0); 874 } 875 876 void 877 usage() 878 { 879 fprintf(stderr, "usage: sadp [-th][-d device[-drive]] s [n]\n"); 880 exit(1); 881 } 882 883 int tstdigit(ss) 884 char *ss; 885 { 886 int kk, cc; 887 kk = 0; 888 while ((cc = ss[kk]) != '\0'){ 889 if (isdigit(cc) == 0) 890 return (-1); 891 kk++; 892 } 893 return (0); 894 } 895 896 /* 897 * The following routines are obtained from iostat. 898 * 899 * Output Cylinder Histogram. 900 */ 901 void 902 cylhist(at, dp) 903 long at; 904 register struct HISTDATA *dp; 905 { 906 register ii; 907 int maxrow; 908 long *graph = (long *)calloc(CHPERCYL, sizeof (long)); 909 long max, max2; 910 long data; 911 long scale; 912 913 for (ii = 0; ii < CHPERCYL; ii++) { 914 dfprintf (stderr, "(%d %d) ", ii, (int)dp->hdata[ii]); 915 } 916 dfprintf (stderr, "\n"); 917 max = 0; 918 for (ii = 0; ii < CHPERCYL; ii++) { 919 if (data = dp->hdata[ii]) { 920 maxrow = ii; 921 if (data > max) { 922 max2 = max; 923 max = data; 924 } else if (data > max2 && data != max) 925 max2 = data; 926 } 927 } 928 maxrow++; 929 930 /* determine scaling */ 931 scale = 1; 932 if (max2) { 933 scale = at / (max2 * 2); 934 if (scale > 48) 935 scale = 48; 936 } 937 938 for (ii = 0; ii < maxrow; ii++) { 939 if (dp->hdata[ii]) 940 graph[ii] = (scale * 100 * dp->hdata[ii]) / at; 941 else 942 graph[ii] = -1; 943 } 944 945 prthist(graph, maxrow, scale, (long) (max*100*scale/at)); 946 } 947 /* 948 * Print Histogram. 949 */ 950 void 951 prthist(array, mrow, scale, gmax) 952 long array[], scale, gmax; 953 register mrow; 954 { 955 long line; 956 957 line = 50; 958 /* handle overflow in scaling */ 959 if (gmax > 51) { 960 line = 52; 961 printf("\n%2ld%% -|", gmax/scale); 962 pline(line--, array, mrow, BLOB); 963 printf("\n %c", BRK); 964 pline(line--, array, mrow, BRK); 965 } else if (gmax = 51) 966 line = 51; 967 while (line > 0) { 968 if ((line & 07) == 0) { 969 printf("\n%2ld%% -|", line/scale); 970 } else { 971 printf("\n |"); 972 } 973 pline(line--, array, mrow, BLOB); 974 } 975 printf("\n 0%% -+"); 976 line = -1; 977 pline(line, array, mrow, FOOT); 978 } 979 980 /* 981 * Print Histogram Line. 982 */ 983 void 984 pline(line, array, mrow, dot) 985 long line, array[]; 986 int mrow; 987 char dot; 988 { 989 register ii; 990 register char *lp; 991 char lbuff[132]; 992 993 dfprintf (stderr, 994 "pline(line 0x%x, array 0x%x, mrow 0x%x, dot 0x%x)\n", 995 line, array, mrow, dot); 996 lp = lbuff; 997 for (ii = 0; ii < mrow; ii++) 998 if (array[ii] < line) 999 if (line == 1 && array[ii] == 0) 1000 *lp++ = TRACE; 1001 else 1002 *lp++ = BLANK; 1003 else 1004 *lp++ = dot; 1005 *lp++ = 0; 1006 printf("%s", lbuff); 1007 } 1008 /* 1009 * Print Cylinder Profiling Headers. 1010 */ 1011 void 1012 cylhdr(flag, total) 1013 long total; 1014 { 1015 1016 dfprintf (stderr, "cylhdr(flag 0x%x, total 0x%x)\n", flag, total); 1017 if (fflg) 1018 printf("\014\n"); 1019 if (flag == CYLNO) 1020 printf("\nCYLINDER ACCESS HISTOGRAM\n"); 1021 if (flag == SEEKD) 1022 printf("\nSEEK DISTANCE HISTOGRAM\n"); 1023 printf("\n%s-%d:\n", 1024 device, k); 1025 printf("Total %s = %ld\n", 1026 flag == CYLNO ? "transfers" : "seeks", total); 1027 } 1028 1029 #define MAXCOL 80 1030 /* Print Histogram Footers */ 1031 void 1032 cylftr(flag) 1033 { 1034 int i; 1035 int chunk_mult = 1; 1036 int col; 1037 char footer[4][MAXCOL]; 1038 char digits[] = "0123456789"; 1039 int significant = 0; 1040 1041 dfprintf (stderr, "cylftr(flag 0x%x)\n", flag); 1042 if (flag == CYLNO) 1043 printf("\n \t\t\tCylinder number, granularity=%d", CHUNK); 1044 else 1045 printf("\n =<< "); 1046 for (i = 0; i < 4; i++) { 1047 for (col = 0; col < MAXCOL - 1; col++) { 1048 footer[i][col] = ' '; 1049 } 1050 footer[i][MAXCOL - 1] = '\0'; 1051 } 1052 for (i = 0, col = 0; i < (int)PHYS_CYL; 1053 i += (chunk_mult * CHUNK), col += chunk_mult, significant = 0) { 1054 if ((i / 1000) > 0) { 1055 footer[0][col] = digits[(i / 1000)]; 1056 significant = 1; 1057 } 1058 if ((significant) || (((i % 1000) / 100) > 0)) { 1059 footer[1][col] = digits[((i % 1000) / 100)]; 1060 significant = 1; 1061 } 1062 if ((significant) || (((i % 100) / 10) > 0)) { 1063 footer[2][col] = digits[((i % 100) / 10)]; 1064 significant = 1; 1065 } 1066 if ((i == 0) || (significant) || ((i % 10) > 0)) { 1067 footer[3][col] = digits[(i % 10)]; 1068 } 1069 if (i > CHUNK) { 1070 chunk_mult = 2; 1071 } 1072 if (i > (3 * CHUNK)) { 1073 chunk_mult = 4; 1074 if (flag != CYLNO) 1075 printf ("< "); 1076 } 1077 } 1078 for (i = 0; i < 4; i++) { 1079 printf (" %s\n", footer[i]); 1080 } 1081 printf ("\n"); 1082 } 1083 1084 void 1085 validate_device() 1086 { 1087 int i; 1088 char tempdev[NAMESIZE]; 1089 1090 if (dflg == 0) { 1091 1092 /* 1093 * No device specified, so default to the first 1094 * one if it is the only one, otherwise prompt 1095 * user to enter one. 1096 */ 1097 strcpy(device, devnm[0]); 1098 *DRVNUM(device) = NULL; 1099 devlen = strlen(device); 1100 for (i = 0; i < dk_ndrive; i++) 1101 drvlist[i] = 1; 1102 if (dk_ndrive > 1) 1103 bad_device(device, ERR_NO_DEV); 1104 dev = 0; 1105 } else { 1106 1107 /* 1108 * Device was specified. Make sure it matches 1109 * one that is configured in the system. 1110 */ 1111 for (i = 0; i < dk_ndrive; i++) { 1112 strncpy(tempdev, devnm[i], DRVNUM(devnm[i])-devnm[i]); 1113 tempdev[DRVNUM(devnm[i])-devnm[i]] = NULL; 1114 if (strcmp(device, tempdev) == 0) 1115 break; 1116 } 1117 if (i == dk_ndrive) 1118 bad_device(device, ERR_BAD_DEV); 1119 dev = i; 1120 } 1121 } 1122 1123 void 1124 validate_drive() 1125 { 1126 int i, j, c; 1127 1128 /* 1129 * For each controller number specified, make sure it exists 1130 * in the configured device list. 1131 */ 1132 for (i = 0; i < dk_ndrive; i++) { 1133 if (drvlist[i] == 0) 1134 continue; 1135 1136 /* 1137 * Since this controller number (i) was specified, 1138 * find the corresponding entry (j) in the device list. 1139 * If found, save the device list index in drvlist[]. 1140 */ 1141 for (j = 0; j < dk_ndrive; j++) { 1142 if (strncmp(device, devnm[j], devlen) != 0) 1143 continue; 1144 c = atoi(DRVNUM(devnm[j])); 1145 if (c == i) { 1146 /* 1147 * NOTE: saved value actual index+1 1148 * as entries with 0 imply don't care. 1149 */ 1150 drvlist[i] = j+1; /* not a flag anymore! */ 1151 1152 break; 1153 } 1154 } 1155 1156 /* 1157 * If not found, output error, except if all drives 1158 * were implied by only specifying controller type. 1159 * In this case, flag it as don't care. 1160 */ 1161 if (j == dk_ndrive) { 1162 if (all) 1163 drvlist[i] = 0; 1164 else 1165 bad_device(device, ERR_BAD_UNIT); 1166 } 1167 } 1168 } 1169 1170 void 1171 init_geom() 1172 { 1173 char tempdev[NAMESIZE]; 1174 int i, fd; 1175 /* 1176 * When the new device naming convention is in effect, switch to it 1177 */ 1178 #ifdef NEW_DEVICE_NAMES 1179 #define DEV_PREFIX "/dev/rdsk/" 1180 #else 1181 #define DEV_PREFIX "/dev/r" 1182 #endif 1183 1184 for (i = 0; drvlist[i] == 0; i++); 1185 sprintf(tempdev, "%s%s%da", DEV_PREFIX, device, i); 1186 if ((fd = open(tempdev, O_RDONLY)) == -1) 1187 fail("open failed", 1); 1188 if (ioctl(fd, DKIOCGGEOM, &dk_geom) == -1) { 1189 close(fd); 1190 fail("ioctl failed", 1); 1191 } 1192 close(fd); 1193 1194 /* 1195 * dk_geom structure now has data, and the number 1196 * of 8 cylinder chunks on the disk can now be 1197 * referenced via the CHPERCYL macro. So allocate 1198 * appropriate buffers based on this value. 1199 */ 1200 iocnt = (long *)calloc(dk_ndrive, sizeof (long)); 1201 dkcyl = (long **)calloc(dk_ndrive, sizeof (long *)); 1202 skcyl = (long **)calloc(dk_ndrive, sizeof (long *)); 1203 for (i = 0; i < dk_ndrive; i++) { 1204 dkcyl[i] = (long *)calloc(CHPERCYL, sizeof (long)); 1205 skcyl[i] = (long *)calloc(CHPERCYL, sizeof (long)); 1206 } 1207 } 1208 1209 /* 1210 * General routine for printing out an error message 1211 * when the specified device/drive is insufficient. 1212 */ 1213 void 1214 bad_device(device, errmsg) 1215 char *device; 1216 char *errmsg; 1217 { 1218 int i, j; 1219 int unique = 0; 1220 char *p, *p1, **buf; 1221 char s[NAMESIZE]; 1222 char *msg; 1223 1224 1225 /* 1226 * Print usage statement if no device is specified. 1227 */ 1228 if (device[0] == NULL) 1229 usage(); 1230 1231 /* 1232 * Compose a list of unique device controller types, or 1233 * unit numbers for a specified controller type, from 1234 * the complete device list. 1235 */ 1236 buf = (char **)calloc(dk_ndrive, sizeof (char *)); 1237 for (i = 0; i < dk_ndrive; i++) { 1238 1239 /* 1240 * Get controller type or unit 1241 */ 1242 p = devnm[i]; 1243 p1 = DRVNUM(devnm[i]); 1244 if (!strcmp(errmsg, ERR_BAD_UNIT)) { 1245 if (strncmp(devnm[i], device, devlen)) 1246 continue; 1247 p = p1; 1248 p1++; 1249 } 1250 strncpy(s, p, p1-p); 1251 s[p1-p] = NULL; 1252 1253 /* 1254 * Have we already logged this one as unique? 1255 * If not, then do so now. 1256 */ 1257 for (j = 0; j < unique; j++) 1258 if (!strcmp(s, buf[j])) 1259 break; 1260 if (j == unique) 1261 buf[unique++] = strdup(s); 1262 } 1263 1264 /* 1265 * Invalid device was specified. Compose message containing 1266 * list of valid devices. 1267 */ 1268 msg = (char *)malloc(strlen(errmsg) + 1269 strlen(device) + unique*(NAMESIZE+1) + 1); 1270 sprintf(msg, errmsg, device); 1271 for (p = msg + strlen(msg), i = 0; i < unique; i++) { 1272 sprintf(p, "%s ", buf[i]); 1273 p += (strlen(buf[i])+ 1); 1274 } 1275 1276 /* 1277 * Output the message and exit. 1278 */ 1279 fail(msg, 0); 1280 } 1281 1282 /* 1283 * Code below here was taken from the SunOS 5.0 iostat command. 1284 */ 1285 1286 #ifdef FIXME 1287 1288 void 1289 read_devinfo_names() 1290 { 1291 int i; 1292 struct dk_ivec dkivec[NDRIVE]; 1293 1294 safe_kvm_read (kd, nl_4c[X1_DK_IVEC].n_value, dkivec, sizeof dkivec, 1295 "dk_ivec"); 1296 for (i = 0; i < NDRIVE; i++) { 1297 if (dkivec[i].dk_name) { 1298 safe_kvm_read (kd, dkivec[i].dk_name, dr_name[i], 2, 1299 "dk_name"); 1300 sprintf(dr_name[i] + 2, "%d", dkivec[i].dk_unit); 1301 } 1302 } 1303 } 1304 1305 #endif 1306 1307 void 1308 init_disk() 1309 { 1310 #ifdef FIXME 1311 int i; 1312 1313 for (i = 0; i < NDRIVE; i++) { 1314 dr_select[i] = 0; 1315 dk_bps[i] = 0; 1316 } 1317 1318 /* 1319 * The default device names: dk# 1320 */ 1321 for (i = 0; i < dk_ndrive; i++) { 1322 dr_name[i] = buf; 1323 (void) sprintf(buf, "dk%d", i); 1324 buf += NAMESIZE; 1325 } 1326 1327 /* 1328 * Device names must be discovered in this program, and output 1329 * with its io data via the "sa" structure. 1330 */ 1331 1332 read_devinfo_names(); 1333 #else 1334 return; 1335 #endif 1336 } 1337 1338 /* 1339 * issue failure message and exit 1340 */ 1341 void 1342 fail(message, doperror) 1343 char *message; 1344 int doperror; 1345 { 1346 if (kd != NULL) 1347 (void) kvm_close(kd); 1348 1349 if (doperror) { 1350 fprintf(stderr, "%s: ", cmdname); 1351 perror(message); 1352 } 1353 fprintf(stderr, "%s: %s\n", cmdname, message); 1354 exit(2); 1355 } 1356 1357 void 1358 safe_kvm_read(kd, addr, buf, size, who) 1359 kvm_t *kd; 1360 unsigned long addr; 1361 char *buf; 1362 unsigned size; 1363 { 1364 int ret_code; 1365 char errmsg[100]; 1366 1367 if (addr == 0) { 1368 sprintf(errmsg, "kvm_read of %s failed -- no address", who); 1369 fail(errmsg, 0); 1370 } 1371 1372 ret_code = kvm_read(kd, addr, buf, size); 1373 if (ret_code != size) { 1374 sprintf(errmsg, "kvm_read of %s failed with code %d", 1375 who, ret_code); 1376 fail(errmsg, 0); 1377 } 1378 } 1379 1380 /* 1381 * code for debugging dumps 1382 */ 1383 1384 #include <sys/tuneable.h> 1385 #include <sys/var.h> 1386 #include <sys/file.h> 1387 #include <sys/vnode.h> 1388 #include <sys/stat.h> 1389 #include <sys/buf.h> 1390 #include <sys/fs/rf_acct.h> 1391 1392 int dump_iodev (); 1393 1394 dump_diskhd (dp) 1395 struct diskhd *dp; 1396 { 1397 1398 dfprintf (stderr, "dump_diskhd: dp 0x%x\n", (int)dp); 1399 dfprintf (stderr, "flags\tb_forw\tb_back\tav_forw\tav_back\tb_bcount\n0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t%d\n", 1400 (int)dp->b_flags, (int)dp->b_forw, (int)dp->b_back, 1401 (int)dp->av_forw, (int)dp->av_back, (int)dp->b_bcount); 1402 1403 return (0); 1404 } 1405 1406 dump_nlist (nlist, str) 1407 struct nlist nlist[]; 1408 char *str; 1409 { 1410 int i; 1411 1412 for (i = 0; nlist[i].n_name; i++) { 1413 dfprintf (stderr, "%s: i %d n_name '%s' n_value 0x%x\n", 1414 str, i, nlist[i].n_name, (int)nlist[i].n_value); 1415 } 1416 1417 return (0); 1418 } 1419 1420 dump_v_struct (tbl) 1421 struct var *tbl; 1422 { 1423 dfprintf (stderr, "dump_v_struct: tbl 0x%x\n", (int)tbl); 1424 dfprintf (stderr, "v_buf\tv_call\tv_proc\tv_nglobpris\n%d\t%d\t%d\t%d\n", 1425 tbl->v_buf, tbl->v_call, tbl->v_proc, tbl->v_nglobpris); 1426 dfprintf (stderr, "v_maxsyspri\tv_clist\tv_maxup\tv_hbuf\n%d\t\t%d\t%d\t%d\n", 1427 tbl->v_maxsyspri, tbl->v_clist, tbl->v_maxup, tbl->v_hbuf); 1428 dfprintf (stderr, "v_hmask\tv_pbuf\tv_sptmap\tv_maxpmem\n0x%x\t%d\t%d\t\t%d\n", 1429 tbl->v_hmask, tbl->v_pbuf, tbl->v_sptmap, tbl->v_maxpmem); 1430 dfprintf (stderr, "v_autoup\tv_bufhwm\n%d\t\t%d\n", 1431 tbl->v_autoup, tbl->v_bufhwm); 1432 1433 return (0); 1434 } 1435 1436 dump_tblmap (tbl, size) 1437 int *tbl; 1438 int size; 1439 { 1440 int i; 1441 1442 dfprintf (stderr, "tblmap size %d/4 = %d ", size, size/4); 1443 for (i = 0; i < size/4; i++) { 1444 dfprintf (stderr, "tblmap[%d] %d ", i, tbl[i]); 1445 } 1446 dfprintf (stderr, "\n"); 1447 1448 return (0); 1449 } 1450