• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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