1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * sa_kstat - kstat statistic adapter, collects statistic data provided 29 * by kstat. 30 */ 31 32 #include <locale.h> 33 #include <string.h> 34 #include <assert.h> 35 #include <kstat.h> 36 #include <pool.h> 37 #include "utils.h" 38 #include <sys/pset.h> 39 #include "poolstat.h" 40 #include "poolstat_utils.h" 41 42 /* marks 'sdata_t' element as updated */ 43 #define SD_UPDATED 1 44 45 /* Specified value for an invalid set. */ 46 #define INVALID_SET -2 47 48 /* statistic data */ 49 typedef struct sdata { 50 kstat_t sd_oks; /* old kstat */ 51 kstat_t sd_nks; /* new kstat */ 52 void *sd_udata; /* user data */ 53 uint_t sd_state; /* state of this data (UPDATED) */ 54 struct sdata *sd_next; 55 } sdata_t; 56 57 /* pset user data */ 58 typedef struct { 59 psetid_t opset; /* old pset sysid */ 60 psetid_t npset; /* new pset sysid */ 61 } pset_ud_t; 62 63 /* shortcuts to access set's id in 'pset_ud_t' */ 64 #define SD_OPSET(p) (((pset_ud_t *)(p)->sd_udata)->opset) 65 #define SD_NPSET(p) (((pset_ud_t *)(p)->sd_udata)->npset) 66 67 static kstat_ctl_t *ks_ctl; /* libkstat handle */ 68 static sdata_t *cpu_list; /* list with cpu statistics */ 69 70 static sdata_t *update_sdata_list(sdata_t *, kstat_ctl_t *, char *, int, 71 char *, int *); 72 static void update_cpu_list(sdata_t *list); 73 static void update_pset_stats(statistic_bag_t *, sdata_t *); 74 75 /*ARGSUSED*/ 76 void 77 sa_kstat_init(void *unused) 78 { 79 if ((ks_ctl = kstat_open()) == NULL) 80 die(gettext(ERR_KSTAT_OPEN), get_errstr()); 81 } 82 83 void 84 sa_kstat_update(statistic_bag_t *sbag, int flags) 85 { 86 /* The SA_REFRESH flag forces the update of local data structures. */ 87 if (flags & SA_REFRESH) { 88 int ks_error = 0; 89 90 if (kstat_chain_update(ks_ctl) == -1) 91 die(gettext(ERR_KSTAT_DATA), get_errstr()); 92 cpu_list = update_sdata_list(cpu_list, ks_ctl, "cpu", 93 -1, "sys", &ks_error); 94 if (ks_error) 95 die(gettext(ERR_KSTAT_DATA), get_errstr()); 96 97 /* update info about cpu binding to processor sets */ 98 update_cpu_list(cpu_list); 99 } 100 101 if (strcmp(sbag->sb_type, PSET_TYPE_NAME) == 0) { 102 update_pset_stats(sbag, cpu_list); 103 } else if (strcmp(sbag->sb_type, POOL_TYPE_NAME) == 0) { 104 return; 105 } else { 106 die(gettext(ERR_UNSUPP_STYPE), sbag->sb_type); 107 } 108 109 } 110 111 static void * 112 safe_kstat_data_lookup(kstat_t *ksp, char *name) 113 { 114 void *dp; 115 116 if ((dp = kstat_data_lookup(ksp, name)) == NULL) 117 die(gettext(ERR_KSTAT_DLOOKUP), 118 ksp->ks_name, name, get_errstr()); 119 120 return (dp); 121 } 122 123 /* 124 * Find the delta over the interval between new_ksp and old_ksp. 125 * If old_ksp->ks_data is NULL and 'oz' is set then pretend 126 * that old_ksp is zero otherwise return 0. 127 */ 128 static uint64_t 129 delta(kstat_t *new_ksp, kstat_t *old_ksp, char *name, int oz) 130 { 131 kstat_named_t *new_ksn; 132 kstat_named_t *old_ksn; 133 134 new_ksn = (kstat_named_t *)safe_kstat_data_lookup(new_ksp, name); 135 if (old_ksp == NULL || old_ksp->ks_data == NULL) 136 return ((oz == 1) ? new_ksn->value.ui64 : 0); 137 old_ksn = (kstat_named_t *)safe_kstat_data_lookup(old_ksp, name); 138 139 return (new_ksn->value.ui64 - old_ksn->value.ui64); 140 } 141 142 /* 143 * Create a clone of the passed kstat_t structure 'kstat_t'. If 144 * 'fr' flag is set free the old ks_data structure in 'dst'. 145 */ 146 static void 147 kstat_clone(kstat_t *src, kstat_t *dst, int fr) 148 { 149 if (fr) 150 FREE(dst->ks_data); 151 *dst = *src; 152 if (src->ks_data != NULL) { 153 dst->ks_data = ZALLOC(src->ks_data_size); 154 (void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size); 155 } else { 156 dst->ks_data = NULL; 157 dst->ks_data_size = 0; 158 } 159 } 160 161 /* 162 * Erase the data from 'src'. 163 */ 164 static void 165 kstat_erase(kstat_t *src) 166 { 167 FREE(src->ks_data); 168 (void) memset(src, 0, sizeof (*src)); 169 } 170 171 /* 172 * Create a new statistic data object with its own copy of the passed 173 * kstat. 174 */ 175 static sdata_t * 176 sdata_new(kstat_t *ksp) 177 { 178 sdata_t *sdp; 179 180 NEW0(sdp); 181 kstat_clone(ksp, &sdp->sd_nks, 0); 182 183 return (sdp); 184 } 185 186 static void 187 sdata_free(sdata_t *sdp) 188 { 189 FREE(sdp->sd_oks.ks_data); 190 FREE(sdp->sd_nks.ks_data); 191 FREE(sdp->sd_udata); 192 FREE(sdp); 193 } 194 195 /* 196 * Create new or update an existing list of cpu statistics. For each 197 * cpu two kstats are kept. One old kstat which contains the data from 198 * the previous scan, and new with the current data. The old and the new 199 * kstats *must* be for the same instance and have the same kid. 200 * If 'instance' argument is set to -1 don't use it as a filter. 201 */ 202 static sdata_t * 203 update_sdata_list(sdata_t *list, kstat_ctl_t *kc, char *module, 204 int instance, char *name, int *errp) 205 { 206 kstat_t *ksp; 207 sdata_t *sdp, *sdpp; /* kstat instance pointer/previous-pointer */ 208 209 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 210 if (strcmp(ksp->ks_module, module) == 0 && 211 (name == NULL || strcmp(ksp->ks_name, name) == 0) && 212 (instance == -1 || ksp->ks_instance == instance)) { 213 if (kstat_read(kc, ksp, NULL) == -1) { 214 *errp = -1; 215 return (list); 216 } 217 /* 218 * Find the kstat in the existing list: 219 * If we find one for the same instance and with the 220 * same ks_kid we'll save it as old_kstat. 221 * If we find one for the same instance but with a 222 * different ks_kid we'll removed it. 223 */ 224 for (sdpp = sdp = list; sdp; sdp = sdp->sd_next) { 225 if (ksp->ks_instance == 226 sdp->sd_nks.ks_instance) { 227 if (ksp->ks_kid == sdp->sd_nks.ks_kid) { 228 kstat_clone(&sdp->sd_nks, 229 &sdp->sd_oks, 1); 230 } else { 231 kstat_erase(&sdp->sd_oks); 232 } 233 kstat_clone(ksp, &sdp->sd_nks, 1); 234 sdp->sd_state |= SD_UPDATED; 235 break; 236 } 237 sdpp = sdp; 238 } 239 /* add a new kstat instance */ 240 if (!sdp) { 241 /* first instance */ 242 if (!list) { 243 list = sdata_new(ksp); 244 list->sd_state |= SD_UPDATED; 245 } else { 246 sdpp->sd_next = sdata_new(ksp); 247 sdpp->sd_next->sd_state |= SD_UPDATED; 248 } 249 } 250 } 251 } 252 253 /* remove untouched statistics */ 254 sdp = list; 255 sdpp = NULL; 256 while (sdp != NULL) { 257 258 if (sdp->sd_state & SD_UPDATED) { 259 sdp->sd_state &= ~SD_UPDATED; 260 sdpp = sdp; 261 sdp = sdp->sd_next; 262 } else { 263 sdata_t *tmp; 264 265 if (sdpp == NULL) 266 list = sdp->sd_next; 267 else 268 sdpp->sd_next = sdp->sd_next; 269 tmp = sdp->sd_next; 270 sdata_free(sdp); 271 sdp = tmp; 272 } 273 } 274 275 *errp = 0; 276 return (list); 277 } 278 279 /* 280 * Update the pset assignment information for each cpu in the statistic 281 * data list. 282 */ 283 static void 284 update_cpu_list(sdata_t *list) 285 { 286 sdata_t *sdp; 287 288 for (sdp = list; sdp; sdp = sdp->sd_next) { 289 /* for new CPU create a new user data object */ 290 if (sdp->sd_udata == NULL) { 291 sdp->sd_udata = ZALLOC(sizeof (pset_ud_t)); 292 /* 293 * set its pset to invalid, so it will not be 294 * used in statistics calculation. 295 */ 296 SD_NPSET(sdp) = INVALID_SET; 297 } 298 /* copy the pset assignment information to the previous stat */ 299 SD_OPSET(sdp) = SD_NPSET(sdp); 300 /* set the current assignment */ 301 if (pset_assign(PS_QUERY, sdp->sd_nks.ks_instance, 302 &(SD_NPSET(sdp))) == -1) 303 SD_NPSET(sdp) = INVALID_SET; 304 } 305 } 306 307 /* 308 * Update statistic data for pset. Calculate the CPU usage in a pset. 309 */ 310 static void 311 update_pset_stats(statistic_bag_t *sbag, sdata_t *list) 312 { 313 sdata_t *sdp; 314 pset_statistic_bag_t *bag = (pset_statistic_bag_t *)sbag->bag; 315 uint64_t allticks, ust, kst, ist, wst; 316 317 ust = kst = ist = wst = 0; 318 for (sdp = list; sdp; sdp = sdp->sd_next) { 319 /* 320 * only calculate for the asked pset id and if the cpu belongs 321 * to the same set in the previous and in the current snapshot. 322 * It means that the usage for CPUs that were rebound during 323 * the sampling interval are not charged to any set. 324 */ 325 if ((SD_OPSET(sdp) == SD_NPSET(sdp)) && 326 (SD_NPSET(sdp) == bag->pset_sb_sysid)) { 327 ust += delta(&sdp->sd_nks, &sdp->sd_oks, 328 "cpu_ticks_user", 0); 329 kst += delta(&sdp->sd_nks, &sdp->sd_oks, 330 "cpu_ticks_kernel", 0); 331 ist += delta(&sdp->sd_nks, &sdp->sd_oks, 332 "cpu_ticks_idle", 0); 333 wst += delta(&sdp->sd_nks, &sdp->sd_oks, 334 "cpu_ticks_wait", 0); 335 } 336 } 337 338 if ((allticks = ust + kst + wst + ist) != 0) { 339 bag->pset_sb_used = 340 (double)(ust + kst) / allticks * bag->pset_sb_size; 341 } else { 342 bag->pset_sb_used = 0.0; 343 } 344 } 345