Annotation of src/usr.bin/vmstat/dkstats.c, Revision 1.23
1.23 ! millert 1: /* $OpenBSD: dkstats.c,v 1.22 2002/12/16 01:57:04 tdeval 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. */
1.19 deraadt 89: #if !defined(NOKVM)
1.20 deraadt 90: extern kvm_t *kd;
1.19 deraadt 91: #endif
1.1 tholo 92: extern char *nlistf;
93: extern char *memf;
94:
1.19 deraadt 95: #if !defined(NOKVM)
1.1 tholo 96: /* Pointer to list of disks. */
97: static struct disk *dk_drivehead = NULL;
1.19 deraadt 98: #endif
1.1 tholo 99:
100: /* Backward compatibility references. */
101: int dk_ndrive = 0;
102: int *dk_select;
103: char **dr_name;
104:
105: /* Missing from <sys/time.h> */
1.16 deraadt 106: #define timerset(tvp, uvp) \
107: ((uvp)->tv_sec = (tvp)->tv_sec); \
108: ((uvp)->tv_usec = (tvp)->tv_usec)
109:
110: #define SWAP(fld) tmp = cur.fld; \
111: cur.fld -= last.fld; \
112: last.fld = tmp
1.1 tholo 113:
114: /*
115: * Take the delta between the present values and the last recorded
116: * values, storing the present values in the 'last' structure, and
117: * the delta values in the 'cur' structure.
118: */
119: void
1.21 deraadt 120: dkswap(void)
1.1 tholo 121: {
122: u_int64_t tmp;
123: int i;
124:
1.22 tdeval 125: for (i = 0; i < cur.dk_ndrive; i++) {
1.1 tholo 126: struct timeval tmp_timer;
127:
128: if (!cur.dk_select[i])
129: continue;
130:
131: /* Delta Values. */
132: SWAP(dk_xfer[i]);
133: SWAP(dk_seek[i]);
134: SWAP(dk_bytes[i]);
135:
136: /* Delta Time. */
137: timerclear(&tmp_timer);
138: timerset(&(cur.dk_time[i]), &tmp_timer);
139: timersub(&tmp_timer, &(last.dk_time[i]), &(cur.dk_time[i]));
140: timerclear(&(last.dk_time[i]));
141: timerset(&tmp_timer, &(last.dk_time[i]));
142: }
143: for (i = 0; i < CPUSTATES; i++) {
144: SWAP(cp_time[i]);
145: }
146: SWAP(tk_nin);
147: SWAP(tk_nout);
148:
149: #undef SWAP
150: }
151:
152: /*
153: * Read the disk statistics for each disk in the disk list.
154: * Also collect statistics for tty i/o and cpu ticks.
155: */
156: void
1.21 deraadt 157: dkreadstats(void)
1.1 tholo 158: {
1.19 deraadt 159: #if !defined(NOKVM)
1.1 tholo 160: struct disk cur_disk, *p;
1.19 deraadt 161: #endif
1.22 tdeval 162: int i, j, mib[3];
1.10 angelos 163: size_t size;
1.22 tdeval 164: char *disknames, *name, *bufpp, **dk_name;
1.13 angelos 165: struct diskstats *q;
1.1 tholo 166:
1.22 tdeval 167: last.dk_ndrive = cur.dk_ndrive;
168:
1.10 angelos 169: if (nlistf == NULL && memf == NULL) {
1.22 tdeval 170: /* Get the number of attached drives. */
171: mib[0] = CTL_HW;
172: mib[1] = HW_DISKCOUNT;
173: size = sizeof(dk_ndrive);
174: if (sysctl(mib, 2, &dk_ndrive, &size, NULL, 0) < 0 ) {
175: warn("could not read hw.diskcount");
176: dk_ndrive = 0;
177: }
178:
179: if (cur.dk_ndrive != dk_ndrive) {
180: /* Re-read the disk names. */
181: dk_name = calloc(dk_ndrive, sizeof(char *));
182: if (dk_name == NULL)
183: err(1, NULL);
184: mib[0] = CTL_HW;
185: mib[1] = HW_DISKNAMES;
186: size = 0;
187: if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
188: err(1, "can't get hw.disknames");
189: disknames = malloc(size);
190: if (disknames == NULL)
191: err(1, NULL);
192: if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
193: err(1, "can't get hw.disknames");
194: bufpp = disknames;
1.23 ! millert 195: for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++)
! 196: dk_name[i] = name;
1.22 tdeval 197: disknames = cur.dk_name[0]; /* To free old names. */
198:
199: if (dk_ndrive < cur.dk_ndrive) {
200: for (i = 0, j = 0; i < dk_ndrive; i++, j++) {
201: while (j < cur.dk_ndrive &&
202: strcmp(cur.dk_name[j], dk_name[i]))
203: j++;
204: if (i == j) continue;
205:
206: if (j >= cur.dk_ndrive) {
207: cur.dk_select[i] = 1;
208: last.dk_xfer[i] = 0;
209: last.dk_seek[i] = 0;
210: last.dk_bytes[i] = 0;
211: bzero(&last.dk_time[i],
212: sizeof(struct timeval));
213: continue;
214: }
215:
216: cur.dk_select[i] = cur.dk_select[j];
217: last.dk_xfer[i] = last.dk_xfer[j];
218: last.dk_seek[i] = last.dk_seek[j];
219: last.dk_bytes[i] = last.dk_bytes[j];
220: last.dk_time[i] = last.dk_time[j];
221: }
222:
223: cur.dk_select = realloc(cur.dk_select,
224: dk_ndrive * sizeof(*cur.dk_select));
225: cur.dk_xfer = realloc(cur.dk_xfer,
226: dk_ndrive * sizeof(*cur.dk_xfer));
227: cur.dk_seek = realloc(cur.dk_seek,
228: dk_ndrive * sizeof(*cur.dk_seek));
229: cur.dk_bytes = realloc(cur.dk_bytes,
230: dk_ndrive * sizeof(*cur.dk_bytes));
231: cur.dk_time = realloc(cur.dk_time,
232: dk_ndrive * sizeof(*cur.dk_time));
233: last.dk_xfer = realloc(last.dk_xfer,
234: dk_ndrive * sizeof(*last.dk_xfer));
235: last.dk_seek = realloc(last.dk_seek,
236: dk_ndrive * sizeof(*last.dk_seek));
237: last.dk_bytes = realloc(last.dk_bytes,
238: dk_ndrive * sizeof(*last.dk_bytes));
239: last.dk_time = realloc(last.dk_time,
240: dk_ndrive * sizeof(*last.dk_time));
241: } else {
242: cur.dk_select = realloc(cur.dk_select,
243: dk_ndrive * sizeof(*cur.dk_select));
244: cur.dk_xfer = realloc(cur.dk_xfer,
245: dk_ndrive * sizeof(*cur.dk_xfer));
246: cur.dk_seek = realloc(cur.dk_seek,
247: dk_ndrive * sizeof(*cur.dk_seek));
248: cur.dk_bytes = realloc(cur.dk_bytes,
249: dk_ndrive * sizeof(*cur.dk_bytes));
250: cur.dk_time = realloc(cur.dk_time,
251: dk_ndrive * sizeof(*cur.dk_time));
252: last.dk_xfer = realloc(last.dk_xfer,
253: dk_ndrive * sizeof(*last.dk_xfer));
254: last.dk_seek = realloc(last.dk_seek,
255: dk_ndrive * sizeof(*last.dk_seek));
256: last.dk_bytes = realloc(last.dk_bytes,
257: dk_ndrive * sizeof(*last.dk_bytes));
258: last.dk_time = realloc(last.dk_time,
259: dk_ndrive * sizeof(*last.dk_time));
260:
261: for (i = dk_ndrive - 1, j = cur.dk_ndrive - 1;
262: i >= 0; i--) {
263:
264: if (j < 0 ||
265: strcmp(cur.dk_name[j], dk_name[i]))
266: {
267: cur.dk_select[i] = 1;
268: last.dk_xfer[i] = 0;
269: last.dk_seek[i] = 0;
270: last.dk_bytes[i] = 0;
271: bzero(&last.dk_time[i],
272: sizeof(struct timeval));
273: continue;
274: }
275:
276: if (i > j) {
277: cur.dk_select[i] =
278: cur.dk_select[j];
279: last.dk_xfer[i] =
280: last.dk_xfer[j];
281: last.dk_seek[i] =
282: last.dk_seek[j];
283: last.dk_bytes[i] =
284: last.dk_bytes[j];
285: last.dk_time[i] =
286: last.dk_time[j];
287: }
288: j--;
289: }
290: }
291:
292: cur.dk_ndrive = dk_ndrive;
293: free(disknames);
294: cur.dk_name = dk_name;
295: dr_name = cur.dk_name;
296: dk_select = cur.dk_select;
297: }
298:
299: size = cur.dk_ndrive * sizeof(struct diskstats);
1.10 angelos 300: mib[0] = CTL_HW;
301: mib[1] = HW_DISKSTATS;
1.13 angelos 302: q = malloc(size);
303: if (q == NULL)
1.10 angelos 304: err(1, NULL);
1.13 angelos 305: if (sysctl(mib, 2, q, &size, NULL, 0) < 0) {
1.22 tdeval 306: #ifdef DEBUG
1.10 angelos 307: warn("could not read hw.diskstats");
1.22 tdeval 308: #endif /* DEBUG */
309: bzero(q, cur.dk_ndrive * sizeof(struct diskstats));
1.10 angelos 310: }
311:
1.22 tdeval 312: for (i = 0; i < cur.dk_ndrive; i++) {
1.13 angelos 313: cur.dk_xfer[i] = q[i].ds_xfer;
314: cur.dk_seek[i] = q[i].ds_seek;
315: cur.dk_bytes[i] = q[i].ds_bytes;
316: timerset(&(q[i].ds_time), &(cur.dk_time[i]));
1.10 angelos 317: }
1.14 deraadt 318: free(q);
1.10 angelos 319:
320: size = sizeof(cur.cp_time);
321: mib[0] = CTL_KERN;
322: mib[1] = KERN_CPTIME;
323: if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0) {
1.11 angelos 324: warn("could not read kern.cp_time");
1.10 angelos 325: bzero(cur.cp_time, sizeof(cur.cp_time));
326: }
327: size = sizeof(cur.tk_nin);
328: mib[0] = CTL_KERN;
329: mib[1] = KERN_TTY;
330: mib[2] = KERN_TTY_TKNIN;
331: if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0) {
1.11 angelos 332: warn("could not read kern.tty.tk_nin");
1.10 angelos 333: cur.tk_nin = 0;
334: }
335: size = sizeof(cur.tk_nin);
336: mib[0] = CTL_KERN;
337: mib[1] = KERN_TTY;
338: mib[2] = KERN_TTY_TKNOUT;
339: if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0) {
1.11 angelos 340: warn("could not read kern.tty.tk_nout");
1.10 angelos 341: cur.tk_nout = 0;
342: }
343: } else {
1.16 deraadt 344: #if !defined(NOKVM)
1.10 angelos 345: p = dk_drivehead;
346:
1.22 tdeval 347: for (i = 0; i < cur.dk_ndrive; i++) {
1.10 angelos 348: deref_kptr(p, &cur_disk, sizeof(cur_disk));
349: cur.dk_xfer[i] = cur_disk.dk_xfer;
350: cur.dk_seek[i] = cur_disk.dk_seek;
351: cur.dk_bytes[i] = cur_disk.dk_bytes;
352: timerset(&(cur_disk.dk_time), &(cur.dk_time[i]));
353: p = cur_disk.dk_link.tqe_next;
354: }
355: deref_nl(X_CP_TIME, cur.cp_time, sizeof(cur.cp_time));
356: deref_nl(X_TK_NIN, &cur.tk_nin, sizeof(cur.tk_nin));
357: deref_nl(X_TK_NOUT, &cur.tk_nout, sizeof(cur.tk_nout));
1.16 deraadt 358: #endif /* !defined(NOKVM) */
1.1 tholo 359: }
360: }
361:
362: /*
363: * Perform all of the initialization and memory allocation needed to
364: * track disk statistics.
365: */
366: int
1.21 deraadt 367: dkinit(int select)
1.1 tholo 368: {
1.19 deraadt 369: #if !defined(NOKVM)
1.1 tholo 370: struct disklist_head disk_head;
371: struct disk cur_disk, *p;
372: char errbuf[_POSIX2_LINE_MAX];
1.19 deraadt 373: #endif
1.1 tholo 374: static int once = 0;
375: extern int hz;
1.10 angelos 376: int i, mib[2];
377: size_t size;
378: struct clockinfo clkinfo;
379: char *disknames, *name, *bufpp;
1.1 tholo 380:
381: if (once)
382: return(1);
383:
1.10 angelos 384: if (nlistf != NULL || memf != NULL) {
1.16 deraadt 385: #if !defined(NOKVM)
1.20 deraadt 386: if (memf != NULL) {
387: setegid(getgid());
388: setgid(getgid());
389: }
390:
1.10 angelos 391: /* Open the kernel. */
1.20 deraadt 392: if (kd == NULL &&
393: (kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
1.10 angelos 394: errbuf)) == NULL)
395: errx(1, "kvm_openfiles: %s", errbuf);
1.20 deraadt 396:
397: setegid(getgid());
398: setgid(getgid());
1.10 angelos 399:
400: /* Obtain the namelist symbols from the kernel. */
401: if (kvm_nlist(kd, namelist))
402: KVM_ERROR("kvm_nlist failed to read symbols.");
403:
404: /* Get the number of attached drives. */
1.22 tdeval 405: deref_nl(X_DISK_COUNT, &cur.dk_ndrive, sizeof(cur.dk_ndrive));
1.10 angelos 406:
1.22 tdeval 407: if (cur.dk_ndrive < 0)
408: errx(1, "invalid _disk_count %d.", cur.dk_ndrive);
1.10 angelos 409:
1.1 tholo 410: /* Get a pointer to the first disk. */
411: deref_nl(X_DISKLIST, &disk_head, sizeof(disk_head));
412: dk_drivehead = disk_head.tqh_first;
1.10 angelos 413:
414: /* Get ticks per second. */
415: deref_nl(X_STATHZ, &hz, sizeof(hz));
416: if (!hz)
417: deref_nl(X_HZ, &hz, sizeof(hz));
1.16 deraadt 418: #endif /* !defined(NOKVM) */
1.10 angelos 419: } else {
420: /* Get the number of attached drives. */
421: mib[0] = CTL_HW;
422: mib[1] = HW_DISKCOUNT;
1.22 tdeval 423: size = sizeof(cur.dk_ndrive);
424: if (sysctl(mib, 2, &cur.dk_ndrive, &size, NULL, 0) < 0 ) {
1.11 angelos 425: warn("could not read hw.diskcount");
1.22 tdeval 426: cur.dk_ndrive = 0;
1.10 angelos 427: }
428:
429: /* Get ticks per second. */
430: mib[0] = CTL_KERN;
431: mib[1] = KERN_CLOCKRATE;
432: size = sizeof(clkinfo);
433: if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
1.11 angelos 434: warn("could not read kern.clockrate");
1.10 angelos 435: hz = 0;
436: } else
437: hz = clkinfo.stathz;
1.1 tholo 438: }
439:
440: /* allocate space for the statistics */
1.22 tdeval 441: cur.dk_time = calloc(cur.dk_ndrive, sizeof(struct timeval));
442: cur.dk_xfer = calloc(cur.dk_ndrive, sizeof(u_int64_t));
443: cur.dk_seek = calloc(cur.dk_ndrive, sizeof(u_int64_t));
444: cur.dk_bytes = calloc(cur.dk_ndrive, sizeof(u_int64_t));
445: last.dk_time = calloc(cur.dk_ndrive, sizeof(struct timeval));
446: last.dk_xfer = calloc(cur.dk_ndrive, sizeof(u_int64_t));
447: last.dk_seek = calloc(cur.dk_ndrive, sizeof(u_int64_t));
448: last.dk_bytes = calloc(cur.dk_ndrive, sizeof(u_int64_t));
449: cur.dk_select = calloc(cur.dk_ndrive, sizeof(int));
450: cur.dk_name = calloc(cur.dk_ndrive, sizeof(char *));
1.1 tholo 451:
1.9 deraadt 452: if (!cur.dk_time || !cur.dk_xfer || !cur.dk_seek || !cur.dk_bytes ||
453: !last.dk_time || !last.dk_xfer || !last.dk_seek ||
454: !last.dk_bytes || !cur.dk_select || !cur.dk_name)
1.1 tholo 455: errx(1, "Memory allocation failure.");
456:
457: /* Set up the compatibility interfaces. */
1.22 tdeval 458: dk_ndrive = cur.dk_ndrive;
1.1 tholo 459: dk_select = cur.dk_select;
460: dr_name = cur.dk_name;
461:
1.22 tdeval 462: /* Read the disk names and set initial selection. */
1.10 angelos 463: if (nlistf == NULL && memf == NULL) {
464: mib[0] = CTL_HW;
465: mib[1] = HW_DISKNAMES;
466: size = 0;
467: if (sysctl(mib, 2, NULL, &size, NULL, 0) < 0)
468: err(1, "can't get hw.disknames");
1.12 angelos 469: disknames = malloc(size);
1.10 angelos 470: if (disknames == NULL)
471: err(1, NULL);
472: if (sysctl(mib, 2, disknames, &size, NULL, 0) < 0)
473: err(1, "can't get hw.disknames");
474: bufpp = disknames;
1.23 ! millert 475: for (i = 0; i < dk_ndrive && (name = strsep(&bufpp, ",")) != NULL; i++) {
! 476: cur.dk_name[i] = name;
! 477: cur.dk_select[i] = select;
1.10 angelos 478: }
479: } else {
1.16 deraadt 480: #if !defined(NOKVM)
1.10 angelos 481: p = dk_drivehead;
1.22 tdeval 482: for (i = 0; i < cur.dk_ndrive; i++) {
1.10 angelos 483: char buf[10];
1.16 deraadt 484:
1.10 angelos 485: deref_kptr(p, &cur_disk, sizeof(cur_disk));
486: deref_kptr(cur_disk.dk_name, buf, sizeof(buf));
487: cur.dk_name[i] = strdup(buf);
488: if (!cur.dk_name[i])
489: errx(1, "Memory allocation failure.");
490: cur.dk_select[i] = select;
1.1 tholo 491:
1.10 angelos 492: p = cur_disk.dk_link.tqe_next;
493: }
1.16 deraadt 494: #endif /* !defined(NOKVM) */
1.1 tholo 495: }
496:
497: /* Never do this initalization again. */
498: once = 1;
499: return(1);
500: }
501:
1.16 deraadt 502: #if !defined(NOKVM)
1.1 tholo 503: /*
504: * Dereference the kernel pointer `kptr' and fill in the local copy
505: * pointed to by `ptr'. The storage space must be pre-allocated,
506: * and the size of the copy passed in `len'.
507: */
508: static void
1.21 deraadt 509: deref_kptr(void *kptr, void *ptr, size_t len)
1.1 tholo 510: {
511: char buf[128];
512:
1.6 art 513: if (kvm_read(kd, (u_long)kptr, ptr, len) != len) {
1.1 tholo 514: bzero(buf, sizeof(buf));
515: snprintf(buf, (sizeof(buf) - 1),
1.2 deraadt 516: "can't dereference kptr 0x%lx", (u_long)kptr);
1.1 tholo 517: KVM_ERROR(buf);
518: }
519: }
1.17 drahn 520: #endif /* !defined(NOKVM) */