|
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 (the "License"). |
|
6 * You may not use this file except in compliance with the License. |
|
7 * |
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 * or http://www.opensolaris.org/os/licensing. |
|
10 * See the License for the specific language governing permissions |
|
11 * and limitations under the License. |
|
12 * |
|
13 * When distributing Covered Code, include this CDDL HEADER in each |
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 * If applicable, add the following below this CDDL HEADER, with the |
|
16 * fields enclosed by brackets "[]" replaced with your own identifying |
|
17 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 * |
|
19 * CDDL HEADER END |
|
20 */ |
|
21 |
|
22 /* |
|
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. |
|
24 */ |
|
25 |
|
26 #include <sys/list.h> |
|
27 #include <stdio.h> |
|
28 #include <stddef.h> |
|
29 #include <stdlib.h> |
|
30 #include <kstat.h> |
|
31 #include <pthread.h> |
|
32 #include <string.h> |
|
33 #include <assert.h> |
|
34 |
|
35 #include <rad/adr.h> |
|
36 #include <rad/adr_object.h> |
|
37 #include <rad/rad_modapi.h> |
|
38 |
|
39 #include "api_kstat.h" |
|
40 |
|
41 #define KSTAT_DOMAIN "com.oracle.solaris.kstat" |
|
42 |
|
43 static pthread_mutex_t kstat_lock = PTHREAD_MUTEX_INITIALIZER; |
|
44 static list_t kstat_list; |
|
45 static kstat_ctl_t *kc; |
|
46 |
|
47 typedef struct kstat_inst { |
|
48 adr_name_t *ki_name; /* full name */ |
|
49 int ki_instance; /* instance number */ |
|
50 uchar_t ki_type; /* last known type */ |
|
51 hrtime_t ki_crtime; /* last known creation time */ |
|
52 |
|
53 kstat_t *ki_ks; /* current kstat header, if any */ |
|
54 boolean_t ki_read; /* kstat data has been read */ |
|
55 rad_instance_t *ki_inst; |
|
56 list_node_t *ki_node; |
|
57 } kstat_inst_t; |
|
58 |
|
59 static void |
|
60 kstat_inst_unread_all(void) |
|
61 { |
|
62 for (kstat_inst_t *kid = list_head(&kstat_list); kid; |
|
63 kid = list_next(&kstat_list, kid)) |
|
64 kid->ki_read = B_FALSE; |
|
65 } |
|
66 |
|
67 static void |
|
68 kstat_inst_invalidate_all(void) |
|
69 { |
|
70 for (kstat_inst_t *kid = list_head(&kstat_list); kid; |
|
71 kid = list_next(&kstat_list, kid)) { |
|
72 kid->ki_read = B_FALSE; |
|
73 kid->ki_ks = NULL; |
|
74 } |
|
75 } |
|
76 |
|
77 static void |
|
78 kstat_inst_update(void) |
|
79 { |
|
80 while (kstat_chain_update(kc)) |
|
81 kstat_inst_invalidate_all(); |
|
82 kstat_inst_unread_all(); |
|
83 } |
|
84 |
|
85 static boolean_t |
|
86 kstat_inst_validate_int(kstat_inst_t *kid) |
|
87 { |
|
88 if (kid->ki_ks == NULL) { |
|
89 const char *kname = adr_name_key(kid->ki_name, "name"); |
|
90 const char *kmodule = adr_name_key(kid->ki_name, "module"); |
|
91 if ((kid->ki_ks = kstat_lookup(kc, (char *)kmodule, |
|
92 kid->ki_instance, (char *)kname)) == NULL) |
|
93 return (B_FALSE); |
|
94 kid->ki_crtime = kid->ki_ks->ks_crtime; |
|
95 kid->ki_type = kid->ki_ks->ks_type; |
|
96 assert(!kid->ki_read); |
|
97 } |
|
98 |
|
99 return (B_TRUE); |
|
100 } |
|
101 |
|
102 static boolean_t |
|
103 kstat_inst_validate(kstat_inst_t *kid) |
|
104 { |
|
105 rad_mutex_enter(&kstat_lock); |
|
106 if (!kstat_inst_validate_int(kid)) { |
|
107 rad_mutex_exit(&kstat_lock); |
|
108 return (B_FALSE); |
|
109 } |
|
110 |
|
111 return (B_TRUE); |
|
112 } |
|
113 |
|
114 static boolean_t |
|
115 kstat_inst_read(kstat_inst_t *kid, boolean_t fresh) |
|
116 { |
|
117 rad_mutex_enter(&kstat_lock); |
|
118 do { |
|
119 if (!kstat_inst_validate_int(kid)) { |
|
120 rad_mutex_exit(&kstat_lock); |
|
121 return (B_FALSE); |
|
122 } |
|
123 if (!fresh && kid->ki_read) |
|
124 return (B_TRUE); |
|
125 } while (kstat_read(kc, kid->ki_ks, NULL) == -1); |
|
126 |
|
127 kid->ki_read = B_TRUE; |
|
128 return (B_TRUE); |
|
129 } |
|
130 |
|
131 static void |
|
132 kstat_inst_free(kstat_inst_t *kid) |
|
133 { |
|
134 adr_name_rele(kid->ki_name); |
|
135 free(kid); |
|
136 } |
|
137 |
|
138 static adr_name_t * |
|
139 kstat_name_create(kstat_t *ks) |
|
140 { |
|
141 char instance[100]; |
|
142 (void) snprintf(instance, sizeof (instance), "%d", ks->ks_instance); |
|
143 |
|
144 return (adr_name_vcreate(KSTAT_DOMAIN, 5, |
|
145 "type", "kstat", |
|
146 "module", ks->ks_module, |
|
147 "name", ks->ks_name, |
|
148 "class", ks->ks_class, |
|
149 "instance", instance)); |
|
150 } |
|
151 |
|
152 /* |
|
153 * Dynamic namespace callbacks |
|
154 */ |
|
155 |
|
156 static conerr_t |
|
157 kstat_listf(adr_name_t *pattern, data_t **names, void *arg) |
|
158 { |
|
159 *names = data_new_array(&t_array_string, 0); |
|
160 |
|
161 for (kstat_t *ks = kc->kc_chain; ks; ks = ks->ks_next) { |
|
162 /* XXX: round-about filtering; could be much more efficient */ |
|
163 adr_name_t *name = kstat_name_create(ks); |
|
164 if (adr_name_match(name, pattern)) |
|
165 array_add(*names, |
|
166 data_new_string(adr_name_tostr(name), lt_free)); |
|
167 adr_name_rele(name); |
|
168 } |
|
169 |
|
170 return (*names == NULL ? ce_nomem : ce_ok); |
|
171 } |
|
172 |
|
173 static conerr_t |
|
174 kstat_lookupf(adr_name_t **name, rad_instance_t **inst, void *arg) |
|
175 { |
|
176 adr_name_t *aname = *name; |
|
177 const char *kclass = adr_name_key(aname, "class"); |
|
178 const char *kname = adr_name_key(aname, "name"); |
|
179 const char *kmodule = adr_name_key(aname, "module"); |
|
180 const char *kinst = adr_name_key(aname, "instance"); |
|
181 char *end = NULL; |
|
182 int kinstnum; |
|
183 |
|
184 if (kname == NULL || kmodule == NULL || kinst == NULL) |
|
185 return (ce_notfound); |
|
186 |
|
187 kinstnum = strtol(kinst, &end, 10); |
|
188 if (*end != '\0') |
|
189 return (ce_notfound); |
|
190 |
|
191 rad_mutex_enter(&kstat_lock); |
|
192 kstat_t *ks = kstat_lookup(kc, (char *)kmodule, kinstnum, |
|
193 (char *)kname); |
|
194 if (ks == NULL || |
|
195 (kclass != NULL && strcmp(kclass, ks->ks_class) != 0)) { |
|
196 rad_mutex_exit(&kstat_lock); |
|
197 return (ce_notfound); |
|
198 } |
|
199 |
|
200 if (kclass == NULL) { |
|
201 /* |
|
202 * We permit clients to omit the class when doing lookups. |
|
203 * This results in a second kstat_lookup, but *shrug*. |
|
204 */ |
|
205 adr_name_t *cname = kstat_name_create(ks); |
|
206 rad_mutex_exit(&kstat_lock); |
|
207 if (cname == NULL) |
|
208 return (ce_nomem); |
|
209 adr_name_rele(aname); |
|
210 *name = cname; |
|
211 *inst = NULL; |
|
212 return (ce_ok); |
|
213 } |
|
214 |
|
215 kstat_inst_t *kid = malloc(sizeof (kstat_inst_t)); |
|
216 if (kid == NULL) { |
|
217 rad_mutex_exit(&kstat_lock); |
|
218 return (ce_nomem); |
|
219 } |
|
220 |
|
221 rad_instance_t *i = instance_create(adr_name_hold(aname), |
|
222 &api_kstat_svr, kid, (void(*)(void *))kstat_inst_free); |
|
223 if (i == NULL) { |
|
224 rad_mutex_exit(&kstat_lock); |
|
225 return (ce_nomem); |
|
226 } |
|
227 |
|
228 kid->ki_name = adr_name_hold(aname); |
|
229 kid->ki_instance = kinstnum; |
|
230 kid->ki_ks = ks; |
|
231 kid->ki_read = B_FALSE; |
|
232 kid->ki_inst = i; |
|
233 list_insert_tail(&kstat_list, kid); |
|
234 *inst = i; |
|
235 |
|
236 rad_mutex_exit(&kstat_lock); |
|
237 |
|
238 return (ce_ok); |
|
239 } |
|
240 |
|
241 /* |
|
242 * "kstat" interface entry points |
|
243 */ |
|
244 |
|
245 conerr_t |
|
246 api_kstat_read_module(rad_instance_t *inst, adr_attribute_t *attr, |
|
247 data_t **data, data_t **error) |
|
248 { |
|
249 kstat_inst_t *kid = instance_getdata(inst); |
|
250 *data = data_new_string(adr_name_key(kid->ki_name, "module"), lt_copy); |
|
251 return (*data == NULL ? ce_nomem : ce_ok); |
|
252 } |
|
253 |
|
254 conerr_t |
|
255 api_kstat_read_name(rad_instance_t *inst, adr_attribute_t *attr, |
|
256 data_t **data, data_t **error) |
|
257 { |
|
258 kstat_inst_t *kid = instance_getdata(inst); |
|
259 *data = data_new_string(adr_name_key(kid->ki_name, "name"), lt_copy); |
|
260 return (*data == NULL ? ce_nomem : ce_ok); |
|
261 } |
|
262 |
|
263 conerr_t |
|
264 api_kstat_read_class(rad_instance_t *inst, adr_attribute_t *attr, |
|
265 data_t **data, data_t **error) |
|
266 { |
|
267 kstat_inst_t *kid = instance_getdata(inst); |
|
268 *data = data_new_string(adr_name_key(kid->ki_name, "class"), lt_copy); |
|
269 return (*data == NULL ? ce_nomem : ce_ok); |
|
270 } |
|
271 |
|
272 conerr_t |
|
273 api_kstat_read_instance(rad_instance_t *inst, adr_attribute_t *attr, |
|
274 data_t **data, data_t **error) |
|
275 { |
|
276 kstat_inst_t *kid = instance_getdata(inst); |
|
277 *data = data_new_integer(kid->ki_instance); |
|
278 return (*data == NULL ? ce_nomem : ce_ok); |
|
279 } |
|
280 |
|
281 conerr_t |
|
282 api_kstat_read_kstatType(rad_instance_t *inst, adr_attribute_t *attr, |
|
283 data_t **data, data_t **error) |
|
284 { |
|
285 kstat_inst_t *kid = instance_getdata(inst); |
|
286 if (kstat_inst_validate(kid)) |
|
287 rad_mutex_exit(&kstat_lock); |
|
288 |
|
289 switch (kid->ki_type) { |
|
290 case KSTAT_TYPE_RAW: |
|
291 *data = &e__kstype_raw; |
|
292 break; |
|
293 case KSTAT_TYPE_NAMED: |
|
294 *data = &e__kstype_named; |
|
295 break; |
|
296 case KSTAT_TYPE_INTR: |
|
297 *data = &e__kstype_intr; |
|
298 break; |
|
299 case KSTAT_TYPE_IO: |
|
300 *data = &e__kstype_io; |
|
301 break; |
|
302 case KSTAT_TYPE_TIMER: |
|
303 *data = &e__kstype_timer; |
|
304 break; |
|
305 default: |
|
306 /* future? *data = &e__kstype_unknown; */ |
|
307 *data = &e__kstype_raw; |
|
308 } |
|
309 |
|
310 return (ce_ok); |
|
311 } |
|
312 |
|
313 conerr_t |
|
314 api_kstat_read_creationTime(rad_instance_t *inst, adr_attribute_t *attr, |
|
315 data_t **data, data_t **error) |
|
316 { |
|
317 kstat_inst_t *kid = instance_getdata(inst); |
|
318 if (kstat_inst_validate(kid)) |
|
319 rad_mutex_exit(&kstat_lock); |
|
320 |
|
321 *data = data_new_ulong(kid->ki_crtime); |
|
322 return (*data == NULL ? ce_nomem : ce_ok); |
|
323 } |
|
324 |
|
325 conerr_t |
|
326 api_kstat_read_stale(rad_instance_t *inst, adr_attribute_t *attr, |
|
327 data_t **data, data_t **error) |
|
328 { |
|
329 if (kstat_inst_validate(instance_getdata(inst))) { |
|
330 *data = data_new_boolean(B_FALSE); |
|
331 rad_mutex_exit(&kstat_lock); |
|
332 } else { |
|
333 *data = data_new_boolean(B_TRUE); |
|
334 } |
|
335 |
|
336 return (ce_ok); |
|
337 } |
|
338 |
|
339 /* Compensate for lacking a discriminated union type */ |
|
340 static data_t * |
|
341 make_union(void) |
|
342 { |
|
343 data_t *value = data_new_struct(&t__union); |
|
344 struct_set(value, "i32", data_new_integer(0)); |
|
345 struct_set(value, "ui32", data_new_uinteger(0)); |
|
346 struct_set(value, "i64", data_new_long(0)); |
|
347 struct_set(value, "ui64", data_new_ulong(0)); |
|
348 struct_set(value, "f", data_new_float(0)); |
|
349 struct_set(value, "d", data_new_double(0)); |
|
350 return (value); |
|
351 } |
|
352 |
|
353 /* |
|
354 * Constructs kstat value. Future: consider caching. |
|
355 */ |
|
356 static conerr_t |
|
357 read_value(rad_instance_t *inst, data_t **data, boolean_t fresh) |
|
358 { |
|
359 kstat_inst_t *kid = instance_getdata(inst); |
|
360 if (!kstat_inst_read(kid, fresh)) |
|
361 return (ce_object); |
|
362 |
|
363 if (kid->ki_ks->ks_type != KSTAT_TYPE_NAMED) { |
|
364 rad_mutex_exit(&kstat_lock); |
|
365 return (ce_object); |
|
366 } |
|
367 |
|
368 int n = kid->ki_ks->ks_ndata; |
|
369 kstat_named_t *stats = kid->ki_ks->ks_data; |
|
370 |
|
371 data_t *sarray = data_new_array(&t_array__statistic, n); |
|
372 for (int i = 0; i < n; i++) { |
|
373 data_t *type; |
|
374 data_t *value = make_union(); |
|
375 switch (stats[i].data_type) { |
|
376 case KSTAT_DATA_CHAR: |
|
377 type = &e__ksdtype_char; |
|
378 struct_set(value, "str", |
|
379 data_new_nstring(stats[i].value.c, |
|
380 sizeof (stats[i].value.c))); |
|
381 break; |
|
382 case KSTAT_DATA_INT32: |
|
383 /* case KSTAT_DATA_LONG: */ |
|
384 type = &e__ksdtype_int32; |
|
385 struct_set(value, "i32", |
|
386 data_new_integer(stats[i].value.i32)); |
|
387 break; |
|
388 case KSTAT_DATA_STRING: |
|
389 type = &e__ksdtype_string; |
|
390 struct_set(value, "str", |
|
391 data_new_string(stats[i].value.str.addr.ptr, |
|
392 lt_copy)); |
|
393 break; |
|
394 case KSTAT_DATA_UINT32: |
|
395 /* case KSTAT_DATA_ULONG: */ |
|
396 type = &e__ksdtype_uint32; |
|
397 struct_set(value, "ui32", |
|
398 data_new_uinteger(stats[i].value.ui32)); |
|
399 break; |
|
400 case KSTAT_DATA_INT64: |
|
401 /* case KSTAT_DATA_LONGLONG: */ |
|
402 type = &e__ksdtype_int64; |
|
403 struct_set(value, "i64", |
|
404 data_new_long(stats[i].value.i64)); |
|
405 break; |
|
406 case KSTAT_DATA_UINT64: |
|
407 /* case KSTAT_DATA_ULONGLONG: */ |
|
408 type = &e__ksdtype_uint64; |
|
409 struct_set(value, "ui64", |
|
410 data_new_ulong(stats[i].value.ui64)); |
|
411 break; |
|
412 case KSTAT_DATA_FLOAT: |
|
413 type = &e__ksdtype_float; |
|
414 struct_set(value, "f", |
|
415 data_new_float(stats[i].value.f)); |
|
416 break; |
|
417 case KSTAT_DATA_DOUBLE: |
|
418 type = &e__ksdtype_double; |
|
419 struct_set(value, "d", |
|
420 data_new_double(stats[i].value.d)); |
|
421 break; |
|
422 default: |
|
423 continue; |
|
424 } |
|
425 |
|
426 data_t *stat = data_new_struct(&t__statistic); |
|
427 struct_set(stat, "name", |
|
428 data_new_string(stats[i].name, lt_copy)); |
|
429 struct_set(stat, "type", type); |
|
430 struct_set(stat, "value", value); |
|
431 array_add(sarray, stat); |
|
432 } |
|
433 |
|
434 data_t *snap = data_new_struct(&t__snapshot); |
|
435 struct_set(snap, "timestamp", data_new_ulong(kid->ki_ks->ks_snaptime)); |
|
436 struct_set(snap, "statistics", sarray); |
|
437 |
|
438 if (data_verify(snap, NULL, B_TRUE)) { |
|
439 *data = snap; |
|
440 } else { |
|
441 *data = NULL; |
|
442 data_free(snap); |
|
443 } |
|
444 |
|
445 rad_mutex_exit(&kstat_lock); |
|
446 return (*data == NULL ? ce_nomem : ce_ok); |
|
447 } |
|
448 |
|
449 conerr_t |
|
450 api_kstat_read_value(rad_instance_t *inst, adr_attribute_t *attr, |
|
451 data_t **data, data_t **error) |
|
452 { |
|
453 return (read_value(inst, data, B_FALSE)); |
|
454 } |
|
455 |
|
456 conerr_t |
|
457 api_kstat_invoke_get_fresh_value(rad_instance_t *inst, adr_method_t *meth, |
|
458 data_t **ret, data_t **args, int count, data_t **error) |
|
459 { |
|
460 return (read_value(inst, ret, B_TRUE)); |
|
461 } |
|
462 |
|
463 /* |
|
464 * "kstatctl" interface entry point |
|
465 */ |
|
466 |
|
467 conerr_t |
|
468 api_kstatctl_invoke_update_all(rad_instance_t *inst, adr_method_t *meth, |
|
469 data_t **ret, data_t **args, int count, data_t **error) |
|
470 { |
|
471 rad_mutex_enter(&kstat_lock); |
|
472 kstat_inst_update(); |
|
473 rad_mutex_exit(&kstat_lock); |
|
474 } |
|
475 |
|
476 static rad_modinfo_t modinfo = { "kstat", "kernel statistics module" }; |
|
477 |
|
478 int |
|
479 _rad_init(void *handle) |
|
480 { |
|
481 if (rad_module_register(handle, RAD_MODVERSION, &modinfo) == -1) |
|
482 return (-1); |
|
483 |
|
484 if (rad_isproxy) |
|
485 return (0); |
|
486 |
|
487 adr_name_t *domain = adr_name_vcreate(KSTAT_DOMAIN, 0); |
|
488 if (domain == NULL) |
|
489 return (0); |
|
490 |
|
491 if ((kc = kstat_open()) == NULL) |
|
492 return (0); |
|
493 |
|
494 list_create(&kstat_list, sizeof (kstat_inst_t), |
|
495 offsetof(kstat_inst_t, ki_node)); |
|
496 (void) cont_register_dynamic(rad_container, domain, |
|
497 kstat_listf, kstat_lookupf, NULL); |
|
498 |
|
499 adr_name_t *ctlname = |
|
500 adr_name_vcreate(KSTAT_DOMAIN, 1, "type", "control"); |
|
501 (void) cont_insert_singleton(rad_container, ctlname, &api_kstatctl_svr); |
|
502 |
|
503 return (0); |
|
504 } |