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
sa_kstat_init(void * unused)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
sa_kstat_update(statistic_bag_t * sbag,int flags)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 *
safe_kstat_data_lookup(kstat_t * ksp,char * name)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
delta(kstat_t * new_ksp,kstat_t * old_ksp,char * name,int oz)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
kstat_clone(kstat_t * src,kstat_t * dst,int fr)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
kstat_erase(kstat_t * src)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 *
sdata_new(kstat_t * ksp)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
sdata_free(sdata_t * sdp)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 *
update_sdata_list(sdata_t * list,kstat_ctl_t * kc,char * module,int instance,char * name,int * errp)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
update_cpu_list(sdata_t * list)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
update_pset_stats(statistic_bag_t * sbag,sdata_t * list)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