xref: /titanic_52/usr/src/cmd/sa/sadp.c (revision ea8dc4b6d2251b437950c0056bc626b311c73c27)
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