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 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 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 215 cpu_util_init(void) 216 { 217 open_kstat(); 218 return; 219 } 220 221 void 222 cpu_util_terminate(void) 223 { 224 return; 225 } 226 227 int 228 get_cpu_method(void) 229 { 230 return KSTAT; 231 } 232 233 void 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 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 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 405 cpu_start_internal(void) 406 { 407 get_cpu_idle(lib_start_count); 408 return; 409 } 410 411 void 412 cpu_stop_internal(void) 413 { 414 get_cpu_idle(lib_end_count); 415 } 416