xref: /netbsd-src/usr.bin/vmstat/drvstats.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: drvstats.c,v 1.9 2014/06/13 11:26:37 joerg Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 John M. Vinopal
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed for the NetBSD Project
18  *      by John M. Vinopal.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/sched.h>
37 #include <sys/sysctl.h>
38 #include <sys/time.h>
39 #include <sys/iostat.h>
40 
41 #include <err.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include "drvstats.h"
49 
50 /* Structures to hold the statistics. */
51 struct _drive	cur, last;
52 
53 extern int	hz;
54 
55 /* sysctl hw.drivestats buffer. */
56 static struct io_sysctl	*drives = NULL;
57 
58 /* Backward compatibility references. */
59 size_t		ndrive = 0;
60 int		*drv_select;
61 char		**dr_name;
62 
63 /* Missing from <sys/time.h> */
64 #define	timerset(tvp, uvp) do {						\
65 	((uvp)->tv_sec = (tvp)->tv_sec);				\
66 	((uvp)->tv_usec = (tvp)->tv_usec);				\
67 } while (/* CONSTCOND */0)
68 
69 /*
70  * Take the delta between the present values and the last recorded
71  * values, storing the present values in the 'last' structure, and
72  * the delta values in the 'cur' structure.
73  */
74 void
75 drvswap(void)
76 {
77 	u_int64_t tmp;
78 	size_t	i;
79 
80 #define	SWAP(fld) do {							\
81 	tmp = cur.fld;							\
82 	cur.fld -= last.fld;						\
83 	last.fld = tmp;							\
84 } while (/* CONSTCOND */0)
85 
86 	for (i = 0; i < ndrive; i++) {
87 		struct timeval	tmp_timer;
88 
89 		if (!cur.select[i])
90 			continue;
91 
92 		/* Delta Values. */
93 		SWAP(rxfer[i]);
94 		SWAP(wxfer[i]);
95 		SWAP(seek[i]);
96 		SWAP(rbytes[i]);
97 		SWAP(wbytes[i]);
98 
99 		/* Delta Time. */
100 		timerclear(&tmp_timer);
101 		timerset(&(cur.time[i]), &tmp_timer);
102 		timersub(&tmp_timer, &(last.time[i]), &(cur.time[i]));
103 		timerclear(&(last.time[i]));
104 		timerset(&tmp_timer, &(last.time[i]));
105 	}
106 }
107 
108 void
109 tkswap(void)
110 {
111 	u_int64_t tmp;
112 
113 	SWAP(tk_nin);
114 	SWAP(tk_nout);
115 }
116 
117 void
118 cpuswap(void)
119 {
120 	double etime;
121 	u_int64_t tmp;
122 	int	i, state;
123 
124 	for (i = 0; i < CPUSTATES; i++)
125 		SWAP(cp_time[i]);
126 
127 	etime = 0;
128 	for (state = 0; state < CPUSTATES; ++state) {
129 		etime += cur.cp_time[state];
130 	}
131 	if (etime == 0)
132 		etime = 1;
133 	etime /= hz;
134 	etime /= cur.cp_ncpu;
135 
136 	cur.cp_etime = etime;
137 }
138 #undef SWAP
139 
140 /*
141  * Read the drive statistics for each drive in the drive list.
142  * Also collect statistics for tty i/o and CPU ticks.
143  */
144 void
145 drvreadstats(void)
146 {
147 	size_t		size, i;
148 	int		mib[3];
149 
150 	mib[0] = CTL_HW;
151 	mib[1] = HW_IOSTATS;
152 	mib[2] = sizeof(struct io_sysctl);
153 
154 	size = ndrive * sizeof(struct io_sysctl);
155 	if (sysctl(mib, 3, drives, &size, NULL, 0) < 0)
156 		err(1, "sysctl hw.iostats failed");
157 	for (i = 0; i < ndrive; i++) {
158 		cur.rxfer[i] = drives[i].rxfer;
159 		cur.wxfer[i] = drives[i].wxfer;
160 		cur.seek[i] = drives[i].seek;
161 		cur.rbytes[i] = drives[i].rbytes;
162 		cur.wbytes[i] = drives[i].wbytes;
163 		cur.time[i].tv_sec = drives[i].time_sec;
164 		cur.time[i].tv_usec = drives[i].time_usec;
165 	}
166 
167 		mib[0] = CTL_KERN;
168 	mib[1] = KERN_TKSTAT;
169 	mib[2] = KERN_TKSTAT_NIN;
170 	size = sizeof(cur.tk_nin);
171 	if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0)
172 		cur.tk_nin = 0;
173 
174 	mib[2] = KERN_TKSTAT_NOUT;
175 	size = sizeof(cur.tk_nout);
176 	if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0)
177 		cur.tk_nout = 0;
178 
179 	size = sizeof(cur.cp_time);
180 	(void)memset(cur.cp_time, 0, size);
181 	mib[0] = CTL_KERN;
182 	mib[1] = KERN_CP_TIME;
183 	if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0)
184 		(void)memset(cur.cp_time, 0, sizeof(cur.cp_time));
185 }
186 
187 /*
188  * Read collect statistics for tty i/o.
189  */
190 
191 void
192 tkreadstats(void)
193 {
194 	size_t		size;
195 	int		mib[3];
196 
197 	mib[0] = CTL_KERN;
198 	mib[1] = KERN_TKSTAT;
199 	mib[2] = KERN_TKSTAT_NIN;
200 	size = sizeof(cur.tk_nin);
201 	if (sysctl(mib, 3, &cur.tk_nin, &size, NULL, 0) < 0)
202 		cur.tk_nin = 0;
203 
204 	mib[2] = KERN_TKSTAT_NOUT;
205 	size = sizeof(cur.tk_nout);
206 	if (sysctl(mib, 3, &cur.tk_nout, &size, NULL, 0) < 0)
207 		cur.tk_nout = 0;
208 }
209 
210 /*
211  * Read collect statistics for CPU ticks.
212  */
213 
214 void
215 cpureadstats(void)
216 {
217 	size_t		size;
218 	int		mib[2];
219 
220 	size = sizeof(cur.cp_time);
221 	(void)memset(cur.cp_time, 0, size);
222 	mib[0] = CTL_KERN;
223 	mib[1] = KERN_CP_TIME;
224 	if (sysctl(mib, 2, cur.cp_time, &size, NULL, 0) < 0)
225 		(void)memset(cur.cp_time, 0, sizeof(cur.cp_time));
226 }
227 
228 /*
229  * Perform all of the initialization and memory allocation needed to
230  * track drive statistics.
231  */
232 int
233 drvinit(int selected)
234 {
235 	struct clockinfo clockinfo;
236 	size_t		size, i;
237 	static int	once = 0;
238 	int		mib[3];
239 
240 	if (once)
241 		return (1);
242 
243 	mib[0] = CTL_HW;
244 	mib[1] = HW_NCPU;
245 	size = sizeof(cur.cp_ncpu);
246 	if (sysctl(mib, 2, &cur.cp_ncpu, &size, NULL, 0) == -1)
247 		err(1, "sysctl hw.ncpu failed");
248 
249 	mib[0] = CTL_KERN;
250 	mib[1] = KERN_CLOCKRATE;
251 	size = sizeof(clockinfo);
252 	if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) == -1)
253 		err(1, "sysctl kern.clockrate failed");
254 	hz = clockinfo.stathz;
255 	if (!hz)
256 		hz = clockinfo.hz;
257 
258 	mib[0] = CTL_HW;
259 	mib[1] = HW_IOSTATS;
260 	mib[2] = sizeof(struct io_sysctl);
261 	if (sysctl(mib, 3, NULL, &size, NULL, 0) == -1)
262 		err(1, "sysctl hw.drivestats failed");
263 	ndrive = size / sizeof(struct io_sysctl);
264 
265 	if (size == 0) {
266 		warnx("No drives attached.");
267 	} else {
268 		drives = (struct io_sysctl *)malloc(size);
269 		if (drives == NULL)
270 			errx(1, "Memory allocation failure.");
271 	}
272 
273 	/* Allocate space for the statistics. */
274 	cur.time = calloc(ndrive, sizeof(struct timeval));
275 	cur.rxfer = calloc(ndrive, sizeof(u_int64_t));
276 	cur.wxfer = calloc(ndrive, sizeof(u_int64_t));
277 	cur.seek = calloc(ndrive, sizeof(u_int64_t));
278 	cur.rbytes = calloc(ndrive, sizeof(u_int64_t));
279 	cur.wbytes = calloc(ndrive, sizeof(u_int64_t));
280 	last.time = calloc(ndrive, sizeof(struct timeval));
281 	last.rxfer = calloc(ndrive, sizeof(u_int64_t));
282 	last.wxfer = calloc(ndrive, sizeof(u_int64_t));
283 	last.seek = calloc(ndrive, sizeof(u_int64_t));
284 	last.rbytes = calloc(ndrive, sizeof(u_int64_t));
285 	last.wbytes = calloc(ndrive, sizeof(u_int64_t));
286 	cur.select = calloc(ndrive, sizeof(int));
287 	cur.name = calloc(ndrive, sizeof(char *));
288 
289 	if (cur.time == NULL || cur.rxfer == NULL ||
290 	    cur.wxfer == NULL || cur.seek == NULL ||
291 	    cur.rbytes == NULL || cur.wbytes == NULL ||
292 	    last.time == NULL || last.rxfer == NULL ||
293 	    last.wxfer == NULL || last.seek == NULL ||
294 	    last.rbytes == NULL || last.wbytes == NULL ||
295 	    cur.select == NULL || cur.name == NULL)
296 		errx(1, "Memory allocation failure.");
297 
298 	/* Set up the compatibility interfaces. */
299 	drv_select = cur.select;
300 	dr_name = cur.name;
301 
302 	/* Read the drive names and set intial selection. */
303 	mib[0] = CTL_HW;		/* Should be still set from */
304 	mib[1] = HW_IOSTATS;		/* ... above, but be safe... */
305 	mib[2] = sizeof(struct io_sysctl);
306 	if (sysctl(mib, 3, drives, &size, NULL, 0) == -1)
307 		err(1, "sysctl hw.iostats failed");
308 	for (i = 0; i < ndrive; i++) {
309 		cur.name[i] = drives[i].name;
310 		cur.select[i] = selected;
311 	}
312 
313 	/* Never do this initialization again. */
314 	once = 1;
315 	return (1);
316 }
317