Annotation of src/usr.bin/vmstat/dkstats.c, Revision 1.18
1.18 ! deraadt 1: /* $OpenBSD: dkstats.c,v 1.17 2002/06/08 02:33:32 drahn Exp $ */
1.2 deraadt 2: /* $NetBSD: dkstats.c,v 1.1 1996/05/10 23:19:27 thorpej Exp $ */
1.1 tholo 3:
4: /*
1.2 deraadt 5: * Copyright (c) 1996 John M. Vinopal
1.1 tholo 6: * All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed for the NetBSD Project
19: * by John M. Vinopal.
20: * 4. The name of the author may not be used to endorse or promote products
21: * derived from this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
1.10 angelos 36: #include <sys/param.h>
1.1 tholo 37: #include <sys/dkstat.h>
38: #include <sys/time.h>
39: #include <sys/disk.h>
1.10 angelos 40: #include <sys/sysctl.h>
41: #include <sys/tty.h>
1.1 tholo 42:
43: #include <err.h>
44: #include <fcntl.h>
45: #include <kvm.h>
46: #include <limits.h>
47: #include <nlist.h>
48: #include <stdio.h>
49: #include <stdlib.h>
50: #include <string.h>
51: #include <unistd.h>
52: #include "dkstats.h"
53:
1.16 deraadt 54: #if !defined(NOKVM)
1.1 tholo 55: static struct nlist namelist[] = {
1.18 ! deraadt 56: #define X_TK_NIN 0 /* sysctl */
! 57: { "_tk_nin" },
! 58: #define X_TK_NOUT 1 /* sysctl */
! 59: { "_tk_nout" },
! 60: #define X_CP_TIME 2 /* sysctl */
! 61: { "_cp_time" },
! 62: #define X_HZ 3 /* sysctl */
! 63: { "_hz" },
! 64: #define X_STATHZ 4 /* sysctl */
1.1 tholo 65: { "_stathz" },
1.18 ! deraadt 66: #define X_DISK_COUNT 5 /* sysctl */
! 67: { "_disk_count" },
! 68: #define X_DISKLIST 6 /* sysctl */
! 69: { "_disklist" },
1.1 tholo 70: { NULL },
71: };
1.16 deraadt 72: #define KVM_ERROR(_string) { \
73: warnx("%s", (_string)); \
74: errx(1, "%s", kvm_geterr(kd)); \
75: }
76:
77: /*
78: * Dereference the namelist pointer `v' and fill in the local copy
79: * 'p' which is of size 's'.
80: */
81: #define deref_nl(v, p, s) deref_kptr((void *)namelist[(v)].n_value, (p), (s));
82: static void deref_kptr(void *, void *, size_t);
83: #endif /* !defined(NOKVM) */
1.1 tholo 84:
85: /* Structures to hold the statistics. */
86: struct _disk cur, last;
87:
88: /* Kernel pointers: nlistf and memf defined in calling program. */
89: static kvm_t *kd = NULL;
90: extern char *nlistf;
91: extern char *memf;
92:
93: /* Pointer to list of disks. */
94: static struct disk *dk_drivehead = NULL;
95:
96: /* Backward compatibility references. */
97: int dk_ndrive = 0;
98: int *dk_select;
99: char **dr_name;
100:
101: /* Missing from <sys/time.h> */
1.16 deraadt 102: #define timerset(tvp, uvp) \
103: ((uvp)->tv_sec = (tvp)->tv_sec); \
104: ((uvp)->tv_usec = (tvp)->tv_usec)
105:
106: #define SWAP(fld) tmp = cur.fld; \
107: cur.fld -= last.fld; \
108: last.fld = tmp
1.1 tholo 109:
110: /*
111: * Take the delta between the present values and the last recorded
112: * values, storing the present values in the 'last' structure, and
113: * the delta values in the 'cur' structure.
114: */
115: void
116: dkswap()
117: {
118: u_int64_t tmp;
119: int i;
120:
121: for (i = 0; i < dk_ndrive; i++) {
122: struct timeval tmp_timer;
123:
124: if (!cur.dk_select[i])
125: continue;
126:
127: /* Delta Values. */
128: SWAP(dk_xfer[i]);
129: SWAP(dk_seek[i]);
130: SWAP(dk_bytes[i]);
131:
132: /* Delta Time. */
133: timerclear(&tmp_timer);
134: timerset(&(cur.dk_time[i]), &tmp_timer);
135: timersub(&tmp_timer, &(last.dk_time[i]), &(cur.dk_time[i]));
136: timerclear(&(last.dk_time[i]));
137: timerset(&tmp_timer, &(last.dk_time[i]));
138: }
139: for (i = 0; i < CPUSTATES; i++) {
140: SWAP(cp_time[i]);
141: }
142: SWAP(tk_nin);
143: SWAP(tk_nout);
144:
145: #undef SWAP
146: }
147:
148: /*
149: * Read the disk statistics for each disk in the disk list.
150: * Also collect statistics for tty i/o and cpu ticks.
151: */
152: void
153: dkreadstats()
154: {
155: struct disk cur_disk, *p;
1.10 angelos 156: int i, mib[3];
157: size_t size;
1.13 angelos 158: struct diskstats *q;
1.1 tholo 159:
1.10 angelos 160: if (nlistf == NULL && memf == NULL) {
1.13 angelos 161: size = dk_ndrive * sizeof(struct diskstats);
1.10 angelos 162: mib[0] = CTL_HW;
163: mib[1] = HW_DISKSTATS;
1.13 angelos 164: q = malloc(size);
165: if (q == NULL)
1.10 angelos 166: err(1, NULL);
1.13 angelos 167: if (sysctl(mib, 2, q, &size, NULL, 0) < 0) {
1.10 angelos 168: warn("could not read hw.diskstats");
1.14 deraadt 169: bzero(q, dk_ndrive * sizeof(struct diskstats));
1.10 angelos 170: }
171:
172: for (i = 0; i < dk_ndrive; i++) {
1.13 angelos 173: cur.dk_xfer[i] = q[i].ds_xfer;
174: cur.dk_seek[i] = q[i].ds_seek;
175: cur.dk_bytes[i] = q[i].ds_bytes;
176: timerset(&(q[i].ds_time), &(cur.dk_time[i]));
1.10 angelos 177: }
1.14 deraadt 178: free(q);
1.10 angelos 179:
180: size = sizeof(cur.cp_time);
181: mib[0] = CTL_KERN;
182: mib[1] = KERN_CPTIME;
183: if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) {
1.11 angelos 184: warn("could not read kern.cp_time");
1.10 angelos 185: bzero(cur.cp_time, sizeof(cur.cp_time));
186: }
187: size = sizeof(cur.tk_nin);
188: mib[0] = CTL_KERN;
189: mib[1] = KERN_TTY;
190: mib[2] = KERN_TTY_TKNIN;
191: if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) {
1.11 angelos 192: warn("could not read kern.tty.tk_nin");
1.10 angelos 193: cur.tk_nin = 0;
194: }
195: size = sizeof(cur.tk_nin);
196: mib[0] = CTL_KERN;
197: mib[1] = KERN_TTY;
198: mib[2] = KERN_TTY_TKNOUT;
199: if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) {
1.11 angelos 200: warn("could not read kern.tty.tk_nout");
1.10 angelos 201: cur.tk_nout = 0;
202: }
203: } else {
1.16 deraadt 204: #if !defined(NOKVM)
1.10 angelos 205: p = dk_drivehead;
206:
207: for (i = 0; i < dk_ndrive; i++) {
208: deref_kptr(p, &cur_disk, sizeof(cur_disk));
209: cur.dk_xfer[i] = cur_disk.dk_xfer;
210: cur.dk_seek[i] = cur_disk.dk_seek;
211: cur.dk_bytes[i] = cur_disk.dk_bytes;
212: timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
213: p = cur_disk.dk_link.tqe_next;
214: }
215: deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
216: deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
217: deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
1.16 deraadt 218: #endif /* !defined(NOKVM) */
1.1 tholo 219: }
220: }
221:
222: /*
223: * Perform all of the initialization and memory allocation needed to
224: * track disk statistics.
225: */
226: int
227: dkinit(select)
228: int select;
229: {
230: struct disklist_head disk_head;
231: struct disk cur_disk, *p;
232: char errbuf[_POSIX2_LINE_MAX];
233: static int once = 0;
234: extern int hz;
1.10 angelos 235: int i, mib[2];
236: size_t size;
237: struct clockinfo clkinfo;
238: char *disknames, *name, *bufpp;
1.1 tholo 239:
240: if (once)
241: return(1);
242:
1.10 angelos 243: if (nlistf != NULL || memf != NULL) {
1.16 deraadt 244: #if !defined(NOKVM)
1.10 angelos 245: /* Open the kernel. */
246: if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
247: errbuf)) == NULL)
248: errx(1, "kvm_openfiles: %s", errbuf);
249:
250: /* Obtain the namelist symbols from the kernel. */
251: if (kvm_nlist(kd, namelist))
252: KVM_ERROR("kvm_nlist failed to read symbols.");
253:
254: /* Get the number of attached drives. */
255: deref_nl(X_DISK_COUNT, &dk_ndrive, sizeof(dk_ndrive));
256:
257: if (dk_ndrive < 0)
258: errx(1, "invalid _disk_count %d.", dk_ndrive);
259:
1.1 tholo 260: /* Get a pointer to the first disk. */
261: deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
262: dk_drivehead = disk_head.tqh_first;
1.10 angelos 263:
264: /* Get ticks per second. */
265: deref_nl(X_STATHZ, &hz, sizeof(hz));
266: if (!hz)
267: deref_nl(X_HZ, &hz, sizeof(hz));
1.16 deraadt 268: #endif /* !defined(NOKVM) */
1.10 angelos 269: } else {
270: /* Get the number of attached drives. */
271: mib[0] = CTL_HW;
272: mib[1] = HW_DISKCOUNT;
273: size = sizeof(dk_ndrive);
274: if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) < 0 ) {
1.11 angelos 275: warn("could not read hw.diskcount");
1.10 angelos 276: dk_ndrive = 0;
277: }
278:
279: /* Get ticks per second. */
280: mib[0] = CTL_KERN;
281: mib[1] = KERN_CLOCKRATE;
282: size = sizeof(clkinfo);
283: if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
1.11 angelos 284: warn("could not read kern.clockrate");
1.10 angelos 285: hz = 0;
286: } else
287: hz = clkinfo.stathz;
1.1 tholo 288: }
289:
290: /* allocate space for the statistics */
291: cur.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
292: cur.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
293: cur.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
294: cur.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
295: last.dk_time = calloc(dk_ndrive, sizeof(struct timeval));
296: last.dk_xfer = calloc(dk_ndrive, sizeof(u_int64_t));
297: last.dk_seek = calloc(dk_ndrive, sizeof(u_int64_t));
298: last.dk_bytes = calloc(dk_ndrive, sizeof(u_int64_t));
299: cur.dk_select = calloc(dk_ndrive, sizeof(int));
300: cur.dk_name = calloc(dk_ndrive, sizeof(char *));
301:
1.9 deraadt 302: if (!cur.dk_time || !cur.dk_xfer || !cur.dk_seek || !cur.dk_bytes ||
303: !last.dk_time || !last.dk_xfer || !last.dk_seek ||
304: !last.dk_bytes || !cur.dk_select || !cur.dk_name)
1.1 tholo 305: errx(1, "Memory allocation failure.");
306:
307: /* Set up the compatibility interfaces. */
308: dk_select = cur.dk_select;
309: dr_name = cur.dk_name;
310:
311: /* Read the disk names and set intial selection. */
1.10 angelos 312: if (nlistf == NULL && memf == NULL) {
313: mib[0] = CTL_HW;
314: mib[1] = HW_DISKNAMES;
315: size = 0;
316: if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
317: err(1, "can't get hw.disknames");
1.12 angelos 318: disknames = malloc(size);
1.10 angelos 319: if (disknames == NULL)
320: err(1, NULL);
321: if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
322: err(1, "can't get hw.disknames");
323: bufpp = disknames;
324: i = 0;
325: while ((name = strsep(&bufpp, ",")) != NULL) {
326: cur.dk_name[i] = name;
327: cur.dk_select[i++] = select;
328: }
329: } else {
1.16 deraadt 330: #if !defined(NOKVM)
1.10 angelos 331: p = dk_drivehead;
332: for (i = 0; i < dk_ndrive; i++) {
333: char buf[10];
1.16 deraadt 334:
1.10 angelos 335: deref_kptr(p, &cur_disk, sizeof(cur_disk));
336: deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
337: cur.dk_name[i] = strdup(buf);
338: if (!cur.dk_name[i])
339: errx(1, "Memory allocation failure.");
340: cur.dk_select[i] = select;
1.1 tholo 341:
1.10 angelos 342: p = cur_disk.dk_link.tqe_next;
343: }
1.16 deraadt 344: #endif /* !defined(NOKVM) */
1.1 tholo 345: }
346:
347: /* Never do this initalization again. */
348: once = 1;
349: return(1);
350: }
351:
1.16 deraadt 352: #if !defined(NOKVM)
1.1 tholo 353: /*
354: * Dereference the kernel pointer `kptr' and fill in the local copy
355: * pointed to by `ptr'. The storage space must be pre-allocated,
356: * and the size of the copy passed in `len'.
357: */
358: static void
359: deref_kptr(kptr, ptr, len)
360: void *kptr, *ptr;
361: size_t len;
362: {
363: char buf[128];
364:
1.6 art 365: if (kvm_read(kd, (u_long)kptr, ptr, len) != len) {
1.1 tholo 366: bzero(buf, sizeof(buf));
367: snprintf(buf, (sizeof(buf) - 1),
1.2 deraadt 368: "can't dereference kptr 0x%lx", (u_long)kptr);
1.1 tholo 369: KVM_ERROR(buf);
370: }
371: }
1.17 drahn 372: #endif /* !defined(NOKVM) */