1 char netcpu_kstat_id[]="\
2 @(#)netcpu_kstat.c Version 2.4.0";
3
4 #if HAVE_CONFIG_H
5 # include <config.h>
6 #endif
7
8 #include <stdio.h>
9
10 #if HAVE_INTTYPES_H
11 # include <inttypes.h>
12 #else
13 # if HAVE_STDINT_H
14 # include <stdint.h>
15 # endif
16 #endif
17
18 #if HAVE_UNISTD_H
19 # include <unistd.h>
20 #endif
21 #if HAVE_STRINGS_H
22 # include <strings.h>
23 #endif
24 #if STDC_HEADERS
25 # include <stdlib.h>
26 # include <stddef.h>
27 #else
28 # if HAVE_STDLIB_H
29 # include <stdlib.h>
30 # endif
31 #endif
32
33 #include <kstat.h>
34 #include <sys/sysinfo.h>
35
36 #include "netsh.h"
37 #include "netlib.h"
38
39 /* the lib_start_count and lib_end_count arrays hold the starting
40 and ending values of whatever is counting when the system is
41 idle. The rate at which this increments during a test is compared
42 with a previous calibrarion to arrive at a CPU utilization
43 percentage. raj 2005-01-26 */
44 static uint64_t lib_start_count[MAXCPUS];
45 static uint64_t lib_end_count[MAXCPUS];
46
47 static kstat_t *cpu_ks[MAXCPUS]; /* the addresses that kstat will
48 need to pull the cpu info from
49 the kstat interface. at least I
50 think that is what this is :) raj
51 8/2000 */
52
53 #define UPDKCID(nk,ok) \
54 if (nk == -1) { \
55 perror("kstat_read "); \
56 exit(1); \
57 } \
58 if (nk != ok)\
59 goto kcid_changed;
60
61 static kstat_ctl_t *kc = NULL;
62 static kid_t kcid = 0;
63
64 /* do the initial open of the kstat interface, get the chain id's all
65 straightened-out and set-up the addresses for get_kstat_idle to do
66 its thing. liberally borrowed from the sources to TOP. raj 8/2000 */
67
68 static int
open_kstat()69 open_kstat()
70 {
71 kstat_t *ks;
72 kid_t nkcid;
73 int i;
74 int changed = 0;
75 static int ncpu = 0;
76
77 kstat_named_t *kn;
78
79 if (debug) {
80 fprintf(where,"open_kstat: enter\n");
81 fflush(where);
82 }
83
84 /*
85 * 0. kstat_open
86 */
87
88 if (!kc)
89 {
90 kc = kstat_open();
91 if (!kc)
92 {
93 perror("kstat_open ");
94 exit(1);
95 }
96 changed = 1;
97 kcid = kc->kc_chain_id;
98 }
99 #ifdef rickwasstupid
100 else {
101 fprintf(where,"open_kstat double open!\n");
102 fflush(where);
103 exit(1);
104 }
105 #endif
106
107 /* keep doing it until no more changes */
108 kcid_changed:
109
110 if (debug) {
111 fprintf(where,"passing kcid_changed\n");
112 fflush(where);
113 }
114
115 /*
116 * 1. kstat_chain_update
117 */
118 nkcid = kstat_chain_update(kc);
119 if (nkcid)
120 {
121 /* UPDKCID will abort if nkcid is -1, so no need to check */
122 changed = 1;
123 kcid = nkcid;
124 }
125 UPDKCID(nkcid,0);
126
127 if (debug) {
128 fprintf(where,"kstat_lookup for unix/system_misc\n");
129 fflush(where);
130 }
131
132 ks = kstat_lookup(kc, "unix", 0, "system_misc");
133 if (kstat_read(kc, ks, 0) == -1) {
134 perror("kstat_read");
135 exit(1);
136 }
137
138
139 if (changed) {
140
141 /*
142 * 2. get data addresses
143 */
144
145 ncpu = 0;
146
147 kn = kstat_data_lookup(ks, "ncpus");
148 if (kn && kn->value.ui32 > lib_num_loc_cpus) {
149 fprintf(stderr,"number of CPU's mismatch!");
150 exit(1);
151 }
152
153 for (ks = kc->kc_chain; ks;
154 ks = ks->ks_next)
155 {
156 if (strncmp(ks->ks_name, "cpu_stat", 8) == 0)
157 {
158 nkcid = kstat_read(kc, ks, NULL);
159 /* if kcid changed, pointer might be invalid. we'll deal
160 wtih changes at this stage, but will not accept them
161 when we are actually in the middle of reading
162 values. hopefully this is not going to be a big
163 issue. raj 8/2000 */
164 UPDKCID(nkcid, kcid);
165
166 if (debug) {
167 fprintf(where,"cpu_ks[%d] getting %p\n",ncpu,ks);
168 fflush(where);
169 }
170
171 cpu_ks[ncpu] = ks;
172 ncpu++;
173 if (ncpu > lib_num_loc_cpus)
174 {
175 /* with the check above, would we ever hit this? */
176 fprintf(stderr,
177 "kstat finds too many cpus %d: should be %d\n",
178 ncpu,lib_num_loc_cpus);
179 exit(1);
180 }
181 }
182 }
183 /* note that ncpu could be less than ncpus, but that's okay */
184 changed = 0;
185 }
186 }
187
188 /* return the value of the idle tick counter for the specified CPU */
189 static long
get_kstat_idle(cpu)190 get_kstat_idle(cpu)
191 int cpu;
192 {
193 cpu_stat_t cpu_stat;
194 kid_t nkcid;
195
196 if (debug) {
197 fprintf(where,
198 "get_kstat_idle reading with kc %x and ks %p\n",
199 kc,
200 cpu_ks[cpu]);
201 }
202
203 nkcid = kstat_read(kc, cpu_ks[cpu], &cpu_stat);
204 /* if kcid changed, pointer might be invalid, fail the test */
205 UPDKCID(nkcid, kcid);
206
207 return(cpu_stat.cpu_sysinfo.cpu[CPU_IDLE]);
208
209 kcid_changed:
210 perror("kcid changed midstream and I cannot deal with that!");
211 exit(1);
212 }
213
214 void
cpu_util_init(void)215 cpu_util_init(void)
216 {
217 open_kstat();
218 return;
219 }
220
221 void
cpu_util_terminate(void)222 cpu_util_terminate(void)
223 {
224 return;
225 }
226
227 int
get_cpu_method(void)228 get_cpu_method(void)
229 {
230 return KSTAT;
231 }
232
233 void
get_cpu_idle(uint64_t * res)234 get_cpu_idle(uint64_t *res)
235 {
236
237 int i;
238
239 /* this open may be redundant */
240 open_kstat();
241
242 for (i = 0; i < lib_num_loc_cpus; i++){
243 res[i] = get_kstat_idle(i);
244 }
245 return;
246 }
247
248 float
calibrate_idle_rate(int iterations,int interval)249 calibrate_idle_rate(int iterations, int interval)
250 {
251
252 long
253 firstcnt[MAXCPUS],
254 secondcnt[MAXCPUS];
255
256 float
257 elapsed,
258 temp_rate,
259 rate[MAXTIMES],
260 local_maxrate;
261
262 long
263 sec,
264 usec;
265
266 int
267 i,
268 j;
269
270 struct timeval time1, time2 ;
271 struct timezone tz;
272
273 if (debug) {
274 fprintf(where,"calling open_kstat from calibrate_kstat\n");
275 fflush(where);
276 }
277
278 open_kstat();
279
280 if (iterations > MAXTIMES) {
281 iterations = MAXTIMES;
282 }
283
284 local_maxrate = (float)-1.0;
285
286 for(i = 0; i < iterations; i++) {
287 rate[i] = (float)0.0;
288 for (j = 0; j < lib_num_loc_cpus; j++) {
289 firstcnt[j] = get_kstat_idle(j);
290 }
291 gettimeofday (&time1, &tz);
292 sleep(interval);
293 gettimeofday (&time2, &tz);
294
295 if (time2.tv_usec < time1.tv_usec)
296 {
297 time2.tv_usec += 1000000;
298 time2.tv_sec -=1;
299 }
300 sec = time2.tv_sec - time1.tv_sec;
301 usec = time2.tv_usec - time1.tv_usec;
302 elapsed = (float)sec + ((float)usec/(float)1000000.0);
303
304 if(debug) {
305 fprintf(where, "Calibration for kstat counter run: %d\n",i);
306 fprintf(where,"\tsec = %ld usec = %ld\n",sec,usec);
307 fprintf(where,"\telapsed time = %g\n",elapsed);
308 }
309
310 for (j = 0; j < lib_num_loc_cpus; j++) {
311 secondcnt[j] = get_kstat_idle(j);
312 if(debug) {
313 /* I know that there are situations where compilers know about */
314 /* long long, but the library functions do not... raj 4/95 */
315 fprintf(where,
316 "\tfirstcnt[%d] = 0x%8.8lx%8.8lx secondcnt[%d] = 0x%8.8lx%8.8lx\n",
317 j,
318 firstcnt[j],
319 firstcnt[j],
320 j,
321 secondcnt[j],
322 secondcnt[j]);
323 }
324 /* we assume that it would wrap no more than once. we also */
325 /* assume that the result of subtracting will "fit" raj 4/95 */
326 temp_rate = (secondcnt[j] >= firstcnt[j]) ?
327 (float)(secondcnt[j] - firstcnt[j])/elapsed :
328 (float)(secondcnt[j]-firstcnt[j]+MAXLONG)/elapsed;
329 if (temp_rate > rate[i]) rate[i] = temp_rate;
330 if(debug) {
331 fprintf(where,"\trate[%d] = %g\n",i,rate[i]);
332 fflush(where);
333 }
334 if (local_maxrate < rate[i]) local_maxrate = rate[i];
335 }
336 }
337 if(debug) {
338 fprintf(where,"\tlocal maxrate = %g per sec. \n",local_maxrate);
339 fflush(where);
340 }
341 return local_maxrate;
342 }
343
344 float
calc_cpu_util_internal(float elapsed_time)345 calc_cpu_util_internal(float elapsed_time)
346 {
347 int i;
348 float correction_factor;
349 float actual_rate;
350
351 lib_local_cpu_util = (float)0.0;
352 /* It is possible that the library measured a time other than */
353 /* the one that the user want for the cpu utilization */
354 /* calculations - for example, tests that were ended by */
355 /* watchdog timers such as the udp stream test. We let these */
356 /* tests tell up what the elapsed time should be. */
357
358 if (elapsed_time != 0.0) {
359 correction_factor = (float) 1.0 +
360 ((lib_elapsed - elapsed_time) / elapsed_time);
361 }
362 else {
363 correction_factor = (float) 1.0;
364 }
365
366 for (i = 0; i < lib_num_loc_cpus; i++) {
367
368 /* it would appear that on some systems, in loopback, nice is
369 *very* effective, causing the looper process to stop dead in its
370 tracks. if this happens, we need to ensure that the calculation
371 does not go south. raj 6/95 and if we run completely out of idle,
372 the same thing could in theory happen to the USE_KSTAT path. raj
373 8/2000 */
374
375 if (lib_end_count[i] == lib_start_count[i]) {
376 lib_end_count[i]++;
377 }
378
379 actual_rate = (lib_end_count[i] > lib_start_count[i]) ?
380 (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed :
381 (float)(lib_end_count[i] - lib_start_count[i] +
382 MAXLONG)/ lib_elapsed;
383 if (debug) {
384 fprintf(where,
385 "calc_cpu_util: actual_rate on processor %d is %f start %lx end %lx\n",
386 i,
387 actual_rate,
388 lib_start_count[i],
389 lib_end_count[i]);
390 }
391 lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) /
392 lib_local_maxrate * 100;
393 lib_local_cpu_util += lib_local_per_cpu_util[i];
394 }
395 /* we want the average across all n processors */
396 lib_local_cpu_util /= (float)lib_num_loc_cpus;
397
398 lib_local_cpu_util *= correction_factor;
399 return lib_local_cpu_util;
400
401
402 }
403
404 void
cpu_start_internal(void)405 cpu_start_internal(void)
406 {
407 get_cpu_idle(lib_start_count);
408 return;
409 }
410
411 void
cpu_stop_internal(void)412 cpu_stop_internal(void)
413 {
414 get_cpu_idle(lib_end_count);
415 }
416