char netcpu_kstat_id[]="\ @(#)netcpu_kstat.c Version 2.4.0"; #if HAVE_CONFIG_H # include <config.h> #endif #include <stdio.h> #if HAVE_INTTYPES_H # include <inttypes.h> #else # if HAVE_STDINT_H # include <stdint.h> # endif #endif #if HAVE_UNISTD_H # include <unistd.h> #endif #if HAVE_STRINGS_H # include <strings.h> #endif #if STDC_HEADERS # include <stdlib.h> # include <stddef.h> #else # if HAVE_STDLIB_H # include <stdlib.h> # endif #endif #include <kstat.h> #include <sys/sysinfo.h> #include "netsh.h" #include "netlib.h" /* the lib_start_count and lib_end_count arrays hold the starting and ending values of whatever is counting when the system is idle. The rate at which this increments during a test is compared with a previous calibrarion to arrive at a CPU utilization percentage. raj 2005-01-26 */ static uint64_t lib_start_count[MAXCPUS]; static uint64_t lib_end_count[MAXCPUS]; static kstat_t *cpu_ks[MAXCPUS]; /* the addresses that kstat will need to pull the cpu info from the kstat interface. at least I think that is what this is :) raj 8/2000 */ #define UPDKCID(nk,ok) \ if (nk == -1) { \ perror("kstat_read "); \ exit(1); \ } \ if (nk != ok)\ goto kcid_changed; static kstat_ctl_t *kc = NULL; static kid_t kcid = 0; /* do the initial open of the kstat interface, get the chain id's all straightened-out and set-up the addresses for get_kstat_idle to do its thing. liberally borrowed from the sources to TOP. raj 8/2000 */ static int open_kstat() { kstat_t *ks; kid_t nkcid; int i; int changed = 0; static int ncpu = 0; kstat_named_t *kn; if (debug) { fprintf(where,"open_kstat: enter\n"); fflush(where); } /* * 0. kstat_open */ if (!kc) { kc = kstat_open(); if (!kc) { perror("kstat_open "); exit(1); } changed = 1; kcid = kc->kc_chain_id; } #ifdef rickwasstupid else { fprintf(where,"open_kstat double open!\n"); fflush(where); exit(1); } #endif /* keep doing it until no more changes */ kcid_changed: if (debug) { fprintf(where,"passing kcid_changed\n"); fflush(where); } /* * 1. kstat_chain_update */ nkcid = kstat_chain_update(kc); if (nkcid) { /* UPDKCID will abort if nkcid is -1, so no need to check */ changed = 1; kcid = nkcid; } UPDKCID(nkcid,0); if (debug) { fprintf(where,"kstat_lookup for unix/system_misc\n"); fflush(where); } ks = kstat_lookup(kc, "unix", 0, "system_misc"); if (kstat_read(kc, ks, 0) == -1) { perror("kstat_read"); exit(1); } if (changed) { /* * 2. get data addresses */ ncpu = 0; kn = kstat_data_lookup(ks, "ncpus"); if (kn && kn->value.ui32 > lib_num_loc_cpus) { fprintf(stderr,"number of CPU's mismatch!"); exit(1); } for (ks = kc->kc_chain; ks; ks = ks->ks_next) { if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) { nkcid = kstat_read(kc, ks, NULL); /* if kcid changed, pointer might be invalid. we'll deal wtih changes at this stage, but will not accept them when we are actually in the middle of reading values. hopefully this is not going to be a big issue. raj 8/2000 */ UPDKCID(nkcid, kcid); if (debug) { fprintf(where,"cpu_ks[%d] getting %p\n",ncpu,ks); fflush(where); } cpu_ks[ncpu] = ks; ncpu++; if (ncpu > lib_num_loc_cpus) { /* with the check above, would we ever hit this? */ fprintf(stderr, "kstat finds too many cpus %d: should be %d\n", ncpu,lib_num_loc_cpus); exit(1); } } } /* note that ncpu could be less than ncpus, but that's okay */ changed = 0; } } /* return the value of the idle tick counter for the specified CPU */ static long get_kstat_idle(cpu) int cpu; { cpu_stat_t cpu_stat; kid_t nkcid; if (debug) { fprintf(where, "get_kstat_idle reading with kc %x and ks %p\n", kc, cpu_ks[cpu]); } nkcid = kstat_read(kc, cpu_ks[cpu], &cpu_stat); /* if kcid changed, pointer might be invalid, fail the test */ UPDKCID(nkcid, kcid); return(cpu_stat.cpu_sysinfo.cpu[CPU_IDLE]); kcid_changed: perror("kcid changed midstream and I cannot deal with that!"); exit(1); } void cpu_util_init(void) { open_kstat(); return; } void cpu_util_terminate(void) { return; } int get_cpu_method(void) { return KSTAT; } void get_cpu_idle(uint64_t *res) { int i; /* this open may be redundant */ open_kstat(); for (i = 0; i < lib_num_loc_cpus; i++){ res[i] = get_kstat_idle(i); } return; } float calibrate_idle_rate(int iterations, int interval) { long firstcnt[MAXCPUS], secondcnt[MAXCPUS]; float elapsed, temp_rate, rate[MAXTIMES], local_maxrate; long sec, usec; int i, j; struct timeval time1, time2 ; struct timezone tz; if (debug) { fprintf(where,"calling open_kstat from calibrate_kstat\n"); fflush(where); } open_kstat(); if (iterations > MAXTIMES) { iterations = MAXTIMES; } local_maxrate = (float)-1.0; for(i = 0; i < iterations; i++) { rate[i] = (float)0.0; for (j = 0; j < lib_num_loc_cpus; j++) { firstcnt[j] = get_kstat_idle(j); } gettimeofday (&time1, &tz); sleep(interval); gettimeofday (&time2, &tz); if (time2.tv_usec < time1.tv_usec) { time2.tv_usec += 1000000; time2.tv_sec -=1; } sec = time2.tv_sec - time1.tv_sec; usec = time2.tv_usec - time1.tv_usec; elapsed = (float)sec + ((float)usec/(float)1000000.0); if(debug) { fprintf(where, "Calibration for kstat counter run: %d\n",i); fprintf(where,"\tsec = %ld usec = %ld\n",sec,usec); fprintf(where,"\telapsed time = %g\n",elapsed); } for (j = 0; j < lib_num_loc_cpus; j++) { secondcnt[j] = get_kstat_idle(j); if(debug) { /* I know that there are situations where compilers know about */ /* long long, but the library functions do not... raj 4/95 */ fprintf(where, "\tfirstcnt[%d] = 0x%8.8lx%8.8lx secondcnt[%d] = 0x%8.8lx%8.8lx\n", j, firstcnt[j], firstcnt[j], j, secondcnt[j], secondcnt[j]); } /* we assume that it would wrap no more than once. we also */ /* assume that the result of subtracting will "fit" raj 4/95 */ temp_rate = (secondcnt[j] >= firstcnt[j]) ? (float)(secondcnt[j] - firstcnt[j])/elapsed : (float)(secondcnt[j]-firstcnt[j]+MAXLONG)/elapsed; if (temp_rate > rate[i]) rate[i] = temp_rate; if(debug) { fprintf(where,"\trate[%d] = %g\n",i,rate[i]); fflush(where); } if (local_maxrate < rate[i]) local_maxrate = rate[i]; } } if(debug) { fprintf(where,"\tlocal maxrate = %g per sec. \n",local_maxrate); fflush(where); } return local_maxrate; } float calc_cpu_util_internal(float elapsed_time) { int i; float correction_factor; float actual_rate; lib_local_cpu_util = (float)0.0; /* It is possible that the library measured a time other than */ /* the one that the user want for the cpu utilization */ /* calculations - for example, tests that were ended by */ /* watchdog timers such as the udp stream test. We let these */ /* tests tell up what the elapsed time should be. */ if (elapsed_time != 0.0) { correction_factor = (float) 1.0 + ((lib_elapsed - elapsed_time) / elapsed_time); } else { correction_factor = (float) 1.0; } for (i = 0; i < lib_num_loc_cpus; i++) { /* it would appear that on some systems, in loopback, nice is *very* effective, causing the looper process to stop dead in its tracks. if this happens, we need to ensure that the calculation does not go south. raj 6/95 and if we run completely out of idle, the same thing could in theory happen to the USE_KSTAT path. raj 8/2000 */ if (lib_end_count[i] == lib_start_count[i]) { lib_end_count[i]++; } actual_rate = (lib_end_count[i] > lib_start_count[i]) ? (float)(lib_end_count[i] - lib_start_count[i])/lib_elapsed : (float)(lib_end_count[i] - lib_start_count[i] + MAXLONG)/ lib_elapsed; if (debug) { fprintf(where, "calc_cpu_util: actual_rate on processor %d is %f start %lx end %lx\n", i, actual_rate, lib_start_count[i], lib_end_count[i]); } lib_local_per_cpu_util[i] = (lib_local_maxrate - actual_rate) / lib_local_maxrate * 100; lib_local_cpu_util += lib_local_per_cpu_util[i]; } /* we want the average across all n processors */ lib_local_cpu_util /= (float)lib_num_loc_cpus; lib_local_cpu_util *= correction_factor; return lib_local_cpu_util; } void cpu_start_internal(void) { get_cpu_idle(lib_start_count); return; } void cpu_stop_internal(void) { get_cpu_idle(lib_end_count); }