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