netcpu_kstat.c revision 8fbc7e6b026f40333767b2383bbaf0c5da473135
1char   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 */
44static uint64_t  lib_start_count[MAXCPUS];
45static uint64_t  lib_end_count[MAXCPUS];
46
47static  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) \
54if (nk == -1) { \
55  perror("kstat_read "); \
56  exit(1); \
57} \
58if (nk != ok)\
59  goto kcid_changed;
60
61static kstat_ctl_t *kc = NULL;
62static 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
68static int
69open_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 */
189static long
190get_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
214void
215cpu_util_init(void)
216{
217  open_kstat();
218  return;
219}
220
221void
222cpu_util_terminate(void)
223{
224  return;
225}
226
227int
228get_cpu_method(void)
229{
230  return KSTAT;
231}
232
233void
234get_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
248float
249calibrate_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
344float
345calc_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
404void
405cpu_start_internal(void)
406{
407  get_cpu_idle(lib_start_count);
408  return;
409}
410
411void
412cpu_stop_internal(void)
413{
414  get_cpu_idle(lib_end_count);
415}
416