• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 char   netcpu_kstat10_id[]="\
2 @(#)netcpu_kstat10.c (c) Copyright 2005-2007, Hewlett-Packard Company Version 2.4.3";
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 <errno.h>
34 
35 #include <kstat.h>
36 #include <sys/sysinfo.h>
37 
38 #include "netsh.h"
39 #include "netlib.h"
40 
41 static kstat_ctl_t *kc = NULL;
42 static kid_t kcid = 0;
43 
44 typedef struct cpu_time_counters {
45   uint64_t idle;
46   uint64_t user;
47   uint64_t kernel;
48   uint64_t interrupt;
49 } cpu_time_counters_t;
50 
51 static cpu_time_counters_t starting_cpu_counters[MAXCPUS];
52 static cpu_time_counters_t ending_cpu_counters[MAXCPUS];
53 static cpu_time_counters_t delta_cpu_counters[MAXCPUS];
54 static cpu_time_counters_t corrected_cpu_counters[MAXCPUS];
55 
56 static void
print_cpu_time_counters(char * name,int instance,cpu_time_counters_t * counters)57 print_cpu_time_counters(char *name, int instance, cpu_time_counters_t *counters)
58 {
59   fprintf(where,"%s[%d]:\n",name,instance);
60   fprintf(where,
61 	  "\t idle %llu\n",counters[instance].idle);
62   fprintf(where,
63 	  "\t user %llu\n",counters[instance].user);
64   fprintf(where,
65 	  "\t kernel %llu\n",counters[instance].kernel);
66   fprintf(where,
67 	  "\t interrupt %llu\n",counters[instance].interrupt);
68 }
69 
70 void
cpu_util_init(void)71 cpu_util_init(void)
72 {
73   kc = kstat_open();
74 
75   if (kc == NULL) {
76     fprintf(where,
77 	    "cpu_util_init: kstat_open: errno %d %s\n",
78 	    errno,
79 	    strerror(errno));
80     fflush(where);
81     exit(-1);
82   }
83   return;
84 }
85 
86 void
cpu_util_terminate(void)87 cpu_util_terminate(void)
88 {
89   kstat_close(kc);
90   return;
91 }
92 
93 int
get_cpu_method(void)94 get_cpu_method(void)
95 {
96   return KSTAT_10;
97 }
98 
99 static void
print_unexpected_statistic_warning(char * who,char * what,char * why)100 print_unexpected_statistic_warning(char *who, char *what, char *why)
101 {
102   if (why) {
103     fprintf(where,
104 	    "WARNING! WARNING! WARNING! WARNING!\n");
105     fprintf(where,
106 	    "%s found an unexpected %s statistic %.16s\n",
107 	    who,
108 	    why,
109 	    what);
110   }
111   else {
112     fprintf(where,
113 	    "%s is ignoring statistic %.16s\n",
114 	    who,
115 	    what);
116   }
117 }
118 
119 static void
get_cpu_counters(int cpu_num,cpu_time_counters_t * counters)120 get_cpu_counters(int cpu_num, cpu_time_counters_t *counters)
121 {
122 
123   kstat_t *ksp;
124   int found=0;
125   kid_t nkcid;
126   kstat_named_t *knp;
127   int i;
128 
129   ksp = kstat_lookup(kc, "cpu", cpu_num, "sys");
130   if ((ksp) && (ksp->ks_type == KSTAT_TYPE_NAMED)) {
131     /* happiness and joy, keep going */
132     nkcid = kstat_read(kc, ksp, NULL);
133     if (nkcid != -1) {
134       /* happiness and joy, keep going. we could consider adding a
135 	 "found < 3" to the end conditions, but then we wouldn't
136 	 search to the end and find that Sun added some nsec. we
137 	 probably want to see if they add an nsec. raj 2005-01-28 */
138       for (i = ksp->ks_ndata, knp = ksp->ks_data;
139 	   i > 0;
140 	   knp++,i--) {
141 	/* we would be hosed if the same name could appear twice */
142 	if (!strcmp("cpu_nsec_idle",knp->name)) {
143 	  found++;
144 	  counters[cpu_num].idle = knp->value.ui64;
145 	}
146 	else if (!strcmp("cpu_nsec_user",knp->name)) {
147 	  found++;
148 	  counters[cpu_num].user = knp->value.ui64;
149 	}
150 	else if (!strcmp("cpu_nsec_kernel",knp->name)) {
151 	  found++;
152 	  counters[cpu_num].kernel = knp->value.ui64;
153 	}
154 	else if (strstr(knp->name,"nsec")) {
155 	  /* finding another nsec here means Sun have changed
156 	     something and we need to warn the user. raj 2005-01-28 */
157 	  print_unexpected_statistic_warning("get_cpu_counters",
158 					     knp->name,
159 					     "nsec");
160 	}
161 	else if (debug >=2) {
162 
163 	  /* might want to tell people about what we are skipping.
164 	     however, only display other names debug >=2. raj
165 	     2005-01-28
166 	  */
167 
168 	  print_unexpected_statistic_warning("get_cpu_counters",
169 					     knp->name,
170 					     NULL);
171 	}
172       }
173       if (3 == found) {
174 	/* happiness and joy */
175 	return;
176       }
177       else {
178 	fprintf(where,
179 		"get_cpu_counters could not find one or more of the expected counters!\n");
180 	fflush(where);
181 	exit(-1);
182       }
183     }
184     else {
185       /* the kstat_read returned an error or the chain changed */
186       fprintf(where,
187 	      "get_cpu_counters: kstat_read failed or chain id changed %d %s\n",
188 	      errno,
189 	      strerror(errno));
190       fflush(where);
191       exit(-1);
192     }
193   }
194   else {
195     /* the lookup failed or found the wrong type */
196     fprintf(where,
197 	    "get_cpu_counters: kstat_lookup failed for module 'cpu' instance %d name 'sys' and KSTAT_TYPE_NAMED: errno %d %s\n",
198 	    cpu_num,
199 	    errno,
200 	    strerror(errno));
201     fflush(where);
202     exit(-1);
203   }
204 }
205 
206 static void
get_interrupt_counters(int cpu_num,cpu_time_counters_t * counters)207 get_interrupt_counters(int cpu_num, cpu_time_counters_t *counters)
208 {
209   kstat_t *ksp;
210   int found=0;
211   kid_t nkcid;
212   kstat_named_t *knp;
213   int i;
214 
215   ksp = kstat_lookup(kc, "cpu", cpu_num, "intrstat");
216 
217   counters[cpu_num].interrupt = 0;
218   if ((ksp) && (ksp->ks_type == KSTAT_TYPE_NAMED)) {
219     /* happiness and joy, keep going */
220     nkcid = kstat_read(kc, ksp, NULL);
221     if (nkcid != -1) {
222       /* happiness and joy, keep going. we could consider adding a
223 	 "found < 15" to the end conditions, but then we wouldn't
224 	 search to the end and find that Sun added some "time." we
225 	 probably want to see if they add a "nsec." raj 2005-01-28 */
226       for (i = ksp->ks_ndata, knp = ksp->ks_data;
227 	   i > 0;
228 	   knp++,i--) {
229 	if (strstr(knp->name,"time")) {
230 	  found++;
231 	  counters[cpu_num].interrupt += knp->value.ui64;
232 	}
233 	else if (debug >=2) {
234 
235 	  /* might want to tell people about what we are skipping.
236 	     however, only display other names debug >=2. raj
237 	     2005-01-28
238 	  */
239 
240 	  print_unexpected_statistic_warning("get_cpu_counters",
241 					     knp->name,
242 					     NULL);
243 	}
244       }
245       if (15 == found) {
246 	/* happiness and joy */
247 	return;
248       }
249       else {
250 	fprintf(where,
251 		"get_cpu_counters could not find one or more of the expected counters!\n");
252 	fflush(where);
253 	exit(-1);
254       }
255     }
256     else {
257       /* the kstat_read returned an error or the chain changed */
258       fprintf(where,
259 	      "get_cpu_counters: kstat_read failed or chain id changed %d %s\n",
260 	      errno,
261 	      strerror(errno));
262       fflush(where);
263       exit(-1);
264     }
265   }
266   else {
267     /* the lookup failed or found the wrong type */
268     fprintf(where,
269 	    "get_cpu_counters: kstat_lookup failed for module 'cpu' instance %d class 'intrstat' and KSTAT_TYPE_NAMED: errno %d %s\n",
270 	    cpu_num,
271 	    errno,
272 	    strerror(errno));
273     fflush(where);
274     exit(-1);
275   }
276 
277 }
278 
279 static void
get_cpu_time_counters(cpu_time_counters_t * counters)280 get_cpu_time_counters(cpu_time_counters_t *counters)
281 {
282 
283   int i;
284 
285   for (i = 0; i < lib_num_loc_cpus; i++){
286     get_cpu_counters(i, counters);
287     get_interrupt_counters(i, counters);
288   }
289 
290   return;
291 }
292 
293 /* the kstat10 mechanism, since it is based on actual nanosecond
294    counters is not going to use a comparison to an idle rate. so, the
295    calibrate_idle_rate routine will be rather simple :) raj 2005-01-28
296    */
297 
298 float
calibrate_idle_rate(int iterations,int interval)299 calibrate_idle_rate(int iterations, int interval)
300 {
301   return 0.0;
302 }
303 
304 float
calc_cpu_util_internal(float elapsed_time)305 calc_cpu_util_internal(float elapsed_time)
306 {
307   int i;
308   float correction_factor;
309   float actual_rate;
310 
311   uint64_t total_cpu_nsec;
312 
313   /* multiply by 100 and divide by total and you get whole
314      percentages. multiply by 1000 and divide by total and you get
315      tenths of percentages.  multiply by 10000 and divide by total and
316      you get hundredths of percentages. etc etc etc raj 2005-01-28 */
317 
318 #define CALC_PERCENT 100
319 #define CALC_TENTH_PERCENT 1000
320 #define CALC_HUNDREDTH_PERCENT 10000
321 #define CALC_THOUSANDTH_PERCENT 100000
322 #define CALC_ACCURACY CALC_THOUSANDTH_PERCENT
323 
324   uint64_t fraction_idle;
325   uint64_t fraction_user;
326   uint64_t fraction_kernel;
327   uint64_t fraction_interrupt;
328 
329   uint64_t interrupt_idle;
330   uint64_t interrupt_user;
331   uint64_t interrupt_kernel;
332 
333   lib_local_cpu_util = (float)0.0;
334 
335   /* It is possible that the library measured a time other than */
336   /* the one that the user want for the cpu utilization */
337   /* calculations - for example, tests that were ended by */
338   /* watchdog timers such as the udp stream test. We let these */
339   /* tests tell up what the elapsed time should be. */
340 
341   if (elapsed_time != 0.0) {
342     correction_factor = (float) 1.0 +
343       ((lib_elapsed - elapsed_time) / elapsed_time);
344   }
345   else {
346     correction_factor = (float) 1.0;
347   }
348 
349   for (i = 0; i < lib_num_loc_cpus; i++) {
350 
351     /* this is now the fun part.  we have the nanoseconds _allegedly_
352        spent in user, idle and kernel.  We also have nanoseconds spent
353        servicing interrupts.  Sadly, in the developer's finite wisdom,
354        the interrupt time accounting is in parallel with the other
355        accounting. this means that time accounted in user, kernel or
356        idle will also include time spent in interrupt.  for netperf's
357        porpoises we do not really care about that for user and kernel,
358        but we certainly do care for idle.  the $64B question becomes -
359        how to "correct" for this?
360 
361        we could just subtract interrupt time from idle.  that has the
362        virtue of simplicity and also "punishes" Sun for doing
363        something that seems to be so stupid.  however, we probably
364        have to be "fair" even to the allegedly stupid so the other
365        mechanism, suggested by a Sun engineer is to subtract interrupt
366        time from each of user, kernel and idle in proportion to their
367        numbers.  then we sum the corrected user, kernel and idle along
368        with the interrupt time and use that to calculate a new idle
369        percentage and thus a CPU util percentage.
370 
371        that is what we will attempt to do here.  raj 2005-01-28
372 
373        of course, we also have to wonder what we should do if there is
374        more interrupt time than the sum of user, kernel and idle.
375        that is a theoretical possibility I suppose, but for the
376        time-being, one that we will blythly ignore, except perhaps for
377        a quick check. raj 2005-01-31
378     */
379 
380     /* we ass-u-me that these counters will never wrap during a
381        netperf run.  this may not be a particularly safe thing to
382        do. raj 2005-01-28 */
383     delta_cpu_counters[i].idle = ending_cpu_counters[i].idle -
384       starting_cpu_counters[i].idle;
385     delta_cpu_counters[i].user = ending_cpu_counters[i].user -
386       starting_cpu_counters[i].user;
387     delta_cpu_counters[i].kernel = ending_cpu_counters[i].kernel -
388       starting_cpu_counters[i].kernel;
389     delta_cpu_counters[i].interrupt = ending_cpu_counters[i].interrupt -
390       starting_cpu_counters[i].interrupt;
391 
392     if (debug) {
393       print_cpu_time_counters("delta_cpu_counters",i,delta_cpu_counters);
394     }
395 
396     /* for this summation, we do not include interrupt time */
397     total_cpu_nsec =
398       delta_cpu_counters[i].idle +
399       delta_cpu_counters[i].user +
400       delta_cpu_counters[i].kernel;
401 
402     if (debug) {
403       fprintf(where,"total_cpu_nsec %llu\n",total_cpu_nsec);
404     }
405 
406     if (delta_cpu_counters[i].interrupt > total_cpu_nsec) {
407       /* we are not in Kansas any more Toto, and I am not quite sure
408 	 the best way to get our tails out of here so let us just
409 	 punt. raj 2005-01-31 */
410       fprintf(where,
411 	      "WARNING! WARNING! WARNING! WARNING! WARNING! \n");
412       fprintf(where,
413 	      "calc_cpu_util_internal: more interrupt time than others combined!\n");
414       fprintf(where,
415 	      "\tso CPU util cannot be estimated\n");
416       fprintf(where,
417 	      "\t delta[%d].interrupt %llu\n",i,delta_cpu_counters[i].interrupt);
418       fprintf(where,
419 	      "\t delta[%d].idle %llu\n",i,delta_cpu_counters[i].idle);
420       fprintf(where,
421 	      "\t delta[%d].user %llu\n",i,delta_cpu_counters[i].user);
422       fprintf(where,
423 	      "\t delta[%d].kernel %llu\n",i,delta_cpu_counters[i].kernel);
424       fflush(where);
425 
426       lib_local_cpu_util = -1.0;
427       lib_local_per_cpu_util[i] = -1.0;
428       return -1.0;
429     }
430 
431     /* and now some fun with integer math.  i initially tried to
432        promote things to long doubled but that didn't seem to result
433        in happiness and joy. raj 2005-01-28 */
434 
435     fraction_idle =
436       (delta_cpu_counters[i].idle * CALC_ACCURACY) / total_cpu_nsec;
437 
438     fraction_user =
439       (delta_cpu_counters[i].user * CALC_ACCURACY) / total_cpu_nsec;
440 
441     fraction_kernel =
442       (delta_cpu_counters[i].kernel * CALC_ACCURACY) / total_cpu_nsec;
443 
444     /* ok, we have our fractions, now we want to take that fraction of
445        the interrupt time and subtract that from the bucket. */
446 
447     interrupt_idle =  ((delta_cpu_counters[i].interrupt * fraction_idle) /
448 		       CALC_ACCURACY);
449 
450     interrupt_user = ((delta_cpu_counters[i].interrupt * fraction_user) /
451 		      CALC_ACCURACY);
452 
453     interrupt_kernel = ((delta_cpu_counters[i].interrupt * fraction_kernel) /
454 			CALC_ACCURACY);
455 
456     if (debug) {
457       fprintf(where,
458 	      "\tfraction_idle %llu interrupt_idle %llu\n",
459 	      fraction_idle,
460 	      interrupt_idle);
461       fprintf(where,
462 	      "\tfraction_user %llu interrupt_user %llu\n",
463 	      fraction_user,
464 	      interrupt_user);
465       fprintf(where,"\tfraction_kernel %llu interrupt_kernel %llu\n",
466 	      fraction_kernel,
467 	      interrupt_kernel);
468     }
469 
470     corrected_cpu_counters[i].idle = delta_cpu_counters[i].idle -
471       interrupt_idle;
472 
473     corrected_cpu_counters[i].user = delta_cpu_counters[i].user -
474       interrupt_user;
475 
476     corrected_cpu_counters[i].kernel = delta_cpu_counters[i].kernel -
477       interrupt_kernel;
478 
479     corrected_cpu_counters[i].interrupt = delta_cpu_counters[i].interrupt;
480 
481     if (debug) {
482       print_cpu_time_counters("corrected_cpu_counters",
483 			      i,
484 			      corrected_cpu_counters);
485     }
486 
487     /* I was going to checkfor going less than zero, but since all the
488        calculations are in unsigned quantities that would seem to be a
489        triffle silly... raj 2005-01-28 */
490 
491     /* ok, now we sum the numbers again, this time including interrupt
492        */
493 
494     total_cpu_nsec =
495       corrected_cpu_counters[i].idle +
496       corrected_cpu_counters[i].user +
497       corrected_cpu_counters[i].kernel +
498       corrected_cpu_counters[i].interrupt;
499 
500     /* and recalculate our fractions we are really only going to use
501        fraction_idle, but lets calculate the rest just for the heck of
502        it. one day we may want to display them. raj 2005-01-28 */
503 
504     /* multiply by 100 and divide by total and you get whole
505        percentages. multiply by 1000 and divide by total and you get
506        tenths of percentages.  multiply by 10000 and divide by total
507        and you get hundredths of percentages. etc etc etc raj
508        2005-01-28 */
509     fraction_idle =
510       (corrected_cpu_counters[i].idle * CALC_ACCURACY) / total_cpu_nsec;
511 
512     fraction_user =
513       (corrected_cpu_counters[i].user * CALC_ACCURACY) / total_cpu_nsec;
514 
515     fraction_kernel =
516       (corrected_cpu_counters[i].kernel * CALC_ACCURACY) / total_cpu_nsec;
517 
518     fraction_interrupt =
519       (corrected_cpu_counters[i].interrupt * CALC_ACCURACY) / total_cpu_nsec;
520 
521     if (debug) {
522       fprintf(where,"\tfraction_idle %lu\n",fraction_idle);
523       fprintf(where,"\tfraction_user %lu\n",fraction_user);
524       fprintf(where,"\tfraction_kernel %lu\n",fraction_kernel);
525       fprintf(where,"\tfraction_interrupt %lu\n",fraction_interrupt);
526     }
527 
528     /* and finally, what is our CPU utilization? */
529     lib_local_per_cpu_util[i] = 100.0 - (((float)fraction_idle /
530 					  (float)CALC_ACCURACY) * 100.0);
531     if (debug) {
532       fprintf(where,
533 	      "lib_local_per_cpu_util[%d] %g\n",
534 	      i,
535 	      lib_local_per_cpu_util[i]);
536     }
537     lib_local_cpu_util += lib_local_per_cpu_util[i];
538   }
539   /* we want the average across all n processors */
540   lib_local_cpu_util /= (float)lib_num_loc_cpus;
541 
542   lib_local_cpu_util *= correction_factor;
543   return lib_local_cpu_util;
544 
545 
546 }
547 
548 void
cpu_start_internal(void)549 cpu_start_internal(void)
550 {
551   get_cpu_time_counters(starting_cpu_counters);
552   return;
553 }
554 
555 void
cpu_stop_internal(void)556 cpu_stop_internal(void)
557 {
558   get_cpu_time_counters(ending_cpu_counters);
559 }
560