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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
29 * kernel statistics driver
32 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/sysmacros.h>
37 #include <sys/cmn_err.h>
38 #include <sys/t_lock.h>
40 #include <sys/fcntl.h>
45 #include <sys/errno.h>
46 #include <sys/ioccom.h>
47 #include <sys/cpuvar.h>
51 #include <sys/sunddi.h>
52 #include <sys/modctl.h>
54 #include <sys/kstat.h>
55 #include <sys/atomic.h>
56 #include <sys/policy.h>
59 static dev_info_t
*kstat_devi
;
62 read_kstat_data(int *rvalp
, void *user_ksp
, int flag
)
64 kstat_t user_kstat
, *ksp
;
65 #ifdef _MULTI_DATAMODEL
66 kstat32_t user_kstat32
;
69 size_t kbufsize
, ubufsize
, copysize
;
73 switch (model
= ddi_model_convert_from(flag
& FMODELS
)) {
74 #ifdef _MULTI_DATAMODEL
76 if (copyin(user_ksp
, &user_kstat32
, sizeof (kstat32_t
)) != 0)
78 user_kstat
.ks_kid
= user_kstat32
.ks_kid
;
79 user_kstat
.ks_data
= (void *)(uintptr_t)user_kstat32
.ks_data
;
80 user_kstat
.ks_data_size
= (size_t)user_kstat32
.ks_data_size
;
85 if (copyin(user_ksp
, &user_kstat
, sizeof (kstat_t
)) != 0)
89 ksp
= kstat_hold_bykid(user_kstat
.ks_kid
, getzoneid());
92 * There is no kstat with the specified KID
96 if (ksp
->ks_flags
& KSTAT_FLAG_INVALID
) {
98 * The kstat exists, but is momentarily in some
99 * indeterminate state (e.g. the data section is not
100 * yet initialized). Try again in a few milliseconds.
107 * If it's a fixed-size kstat, allocate the buffer now, so we
108 * don't have to do it under the kstat's data lock. (If it's a
109 * var-size kstat or one with long strings, we don't know the size
110 * until after the update routine is called, so we can't do this
112 * The allocator relies on this behavior to prevent recursive
113 * mutex_enter in its (fixed-size) kstat update routine.
114 * It's a zalloc to prevent unintentional exposure of random
115 * juicy morsels of (old) kernel data.
117 if (!(ksp
->ks_flags
& (KSTAT_FLAG_VAR_SIZE
| KSTAT_FLAG_LONGSTRINGS
))) {
118 kbufsize
= ksp
->ks_data_size
;
119 kbuf
= kmem_zalloc(kbufsize
+ 1, KM_NOSLEEP
);
126 if ((error
= KSTAT_UPDATE(ksp
, KSTAT_READ
)) != 0) {
130 kmem_free(kbuf
, kbufsize
+ 1);
134 kbufsize
= ksp
->ks_data_size
;
135 ubufsize
= user_kstat
.ks_data_size
;
137 if (ubufsize
< kbufsize
) {
141 kbuf
= kmem_zalloc(kbufsize
+ 1, KM_NOSLEEP
);
145 error
= KSTAT_SNAPSHOT(ksp
, kbuf
, KSTAT_READ
);
150 * The following info must be returned to user level,
151 * even if the the update or snapshot failed. This allows
152 * kstat readers to get a handle on variable-size kstats,
153 * detect dormant kstats, etc.
155 user_kstat
.ks_ndata
= ksp
->ks_ndata
;
156 user_kstat
.ks_data_size
= kbufsize
;
157 user_kstat
.ks_flags
= ksp
->ks_flags
;
158 user_kstat
.ks_snaptime
= ksp
->ks_snaptime
;
160 *rvalp
= kstat_chain_id
;
168 * Copy the buffer containing the kstat back to userland.
174 #ifdef _MULTI_DATAMODEL
178 case DDI_MODEL_ILP32
:
180 if (ksp
->ks_type
== KSTAT_TYPE_NAMED
) {
181 kstat_named_t
*kn
= kbuf
;
182 char *strbuf
= (char *)((kstat_named_t
*)kn
+
185 for (i
= 0; i
< user_kstat
.ks_ndata
; kn
++, i
++)
186 switch (kn
->data_type
) {
188 * Named statistics have fields of type 'long'.
189 * For a 32-bit application looking at a 64-bit
190 * kernel, forcibly truncate these 64-bit
191 * quantities to 32-bit values.
193 case KSTAT_DATA_LONG
:
194 kn
->value
.i32
= (int32_t)kn
->value
.l
;
195 kn
->data_type
= KSTAT_DATA_INT32
;
197 case KSTAT_DATA_ULONG
:
198 kn
->value
.ui32
= (uint32_t)kn
->value
.ul
;
199 kn
->data_type
= KSTAT_DATA_UINT32
;
202 * Long strings must be massaged before being
203 * copied out to userland. Do that here.
205 case KSTAT_DATA_STRING
:
206 if (KSTAT_NAMED_STR_PTR(kn
) == NULL
)
209 * If the string lies outside of kbuf
210 * copy it there and update the pointer.
212 if (KSTAT_NAMED_STR_PTR(kn
) <
214 KSTAT_NAMED_STR_PTR(kn
) +
215 KSTAT_NAMED_STR_BUFLEN(kn
) >
216 (char *)kbuf
+ kbufsize
+ 1) {
217 bcopy(KSTAT_NAMED_STR_PTR(kn
),
219 KSTAT_NAMED_STR_BUFLEN(kn
));
221 KSTAT_NAMED_STR_PTR(kn
) =
224 KSTAT_NAMED_STR_BUFLEN(kn
);
230 * The offsets within the buffers are
231 * the same, so add the offset to the
232 * beginning of the new buffer to fix
235 KSTAT_NAMED_STR_PTR(kn
) =
236 (char *)user_kstat
.ks_data
+
237 (KSTAT_NAMED_STR_PTR(kn
) -
240 * Make sure the string pointer lies
241 * within the allocated buffer.
243 ASSERT(KSTAT_NAMED_STR_PTR(kn
) +
244 KSTAT_NAMED_STR_BUFLEN(kn
) <=
245 ((char *)user_kstat
.ks_data
+
247 ASSERT(KSTAT_NAMED_STR_PTR(kn
) >=
248 (char *)((kstat_named_t
*)
250 user_kstat
.ks_ndata
));
252 * Cast 64-bit ptr to 32-bit.
254 kn
->value
.str
.addr
.ptr32
=
255 (caddr32_t
)(uintptr_t)
256 KSTAT_NAMED_STR_PTR(kn
);
263 if (user_kstat
.ks_kid
!= 0)
267 * This is the special case of the kstat header
268 * list for the entire system. Reshape the
269 * array in place, then copy it out.
273 for (i
= 0; i
< user_kstat
.ks_ndata
; k32
++, k
++, i
++) {
274 k32
->ks_crtime
= k
->ks_crtime
;
276 k32
->ks_kid
= k
->ks_kid
;
277 (void) strcpy(k32
->ks_module
, k
->ks_module
);
278 k32
->ks_resv
= k
->ks_resv
;
279 k32
->ks_instance
= k
->ks_instance
;
280 (void) strcpy(k32
->ks_name
, k
->ks_name
);
281 k32
->ks_type
= k
->ks_type
;
282 (void) strcpy(k32
->ks_class
, k
->ks_class
);
283 k32
->ks_flags
= k
->ks_flags
;
285 k32
->ks_ndata
= k
->ks_ndata
;
286 if (k
->ks_data_size
> UINT32_MAX
) {
290 k32
->ks_data_size
= (size32_t
)k
->ks_data_size
;
291 k32
->ks_snaptime
= k
->ks_snaptime
;
295 * XXX In this case we copy less data than is
296 * claimed in the header.
298 copysize
= user_kstat
.ks_ndata
* sizeof (kstat32_t
);
300 #endif /* _MULTI_DATAMODEL */
303 if (ksp
->ks_type
== KSTAT_TYPE_NAMED
) {
304 kstat_named_t
*kn
= kbuf
;
305 char *strbuf
= (char *)((kstat_named_t
*)kn
+
308 for (i
= 0; i
< user_kstat
.ks_ndata
; kn
++, i
++)
309 switch (kn
->data_type
) {
311 case KSTAT_DATA_LONG
:
315 case KSTAT_DATA_ULONG
:
320 case KSTAT_DATA_STRING
:
321 if (KSTAT_NAMED_STR_PTR(kn
) == NULL
)
324 * If the string lies outside of kbuf
325 * copy it there and update the pointer.
327 if (KSTAT_NAMED_STR_PTR(kn
) <
329 KSTAT_NAMED_STR_PTR(kn
) +
330 KSTAT_NAMED_STR_BUFLEN(kn
) >
331 (char *)kbuf
+ kbufsize
+ 1) {
332 bcopy(KSTAT_NAMED_STR_PTR(kn
),
334 KSTAT_NAMED_STR_BUFLEN(kn
));
336 KSTAT_NAMED_STR_PTR(kn
) =
339 KSTAT_NAMED_STR_BUFLEN(kn
);
345 KSTAT_NAMED_STR_PTR(kn
) =
346 (char *)user_kstat
.ks_data
+
347 (KSTAT_NAMED_STR_PTR(kn
) -
349 ASSERT(KSTAT_NAMED_STR_PTR(kn
) +
350 KSTAT_NAMED_STR_BUFLEN(kn
) <=
351 ((char *)user_kstat
.ks_data
+
353 ASSERT(KSTAT_NAMED_STR_PTR(kn
) >=
354 (char *)((kstat_named_t
*)
356 user_kstat
.ks_ndata
));
366 copyout(kbuf
, user_kstat
.ks_data
, copysize
))
368 kmem_free(kbuf
, kbufsize
+ 1);
372 * We have modified the ks_ndata, ks_data_size, ks_flags, and
373 * ks_snaptime fields of the user kstat; now copy it back to userland.
376 #ifdef _MULTI_DATAMODEL
377 case DDI_MODEL_ILP32
:
378 if (kbufsize
> UINT32_MAX
) {
382 user_kstat32
.ks_ndata
= user_kstat
.ks_ndata
;
383 user_kstat32
.ks_data_size
= (size32_t
)kbufsize
;
384 user_kstat32
.ks_flags
= user_kstat
.ks_flags
;
385 user_kstat32
.ks_snaptime
= user_kstat
.ks_snaptime
;
386 if (copyout(&user_kstat32
, user_ksp
, sizeof (kstat32_t
)) &&
393 if (copyout(&user_kstat
, user_ksp
, sizeof (kstat_t
)) &&
403 write_kstat_data(int *rvalp
, void *user_ksp
, int flag
, cred_t
*cred
)
405 kstat_t user_kstat
, *ksp
;
410 if (secpolicy_sys_config(cred
, B_FALSE
) != 0)
413 switch (ddi_model_convert_from(flag
& FMODELS
)) {
414 #ifdef _MULTI_DATAMODEL
415 kstat32_t user_kstat32
;
417 case DDI_MODEL_ILP32
:
418 if (copyin(user_ksp
, &user_kstat32
, sizeof (kstat32_t
)))
421 * These are the only fields we actually look at.
423 user_kstat
.ks_kid
= user_kstat32
.ks_kid
;
424 user_kstat
.ks_data
= (void *)(uintptr_t)user_kstat32
.ks_data
;
425 user_kstat
.ks_data_size
= (size_t)user_kstat32
.ks_data_size
;
426 user_kstat
.ks_ndata
= user_kstat32
.ks_ndata
;
431 if (copyin(user_ksp
, &user_kstat
, sizeof (kstat_t
)))
435 bufsize
= user_kstat
.ks_data_size
;
436 buf
= kmem_alloc(bufsize
+ 1, KM_NOSLEEP
);
440 if (copyin(user_kstat
.ks_data
, buf
, bufsize
)) {
441 kmem_free(buf
, bufsize
+ 1);
445 ksp
= kstat_hold_bykid(user_kstat
.ks_kid
, getzoneid());
447 kmem_free(buf
, bufsize
+ 1);
450 if (ksp
->ks_flags
& KSTAT_FLAG_INVALID
) {
452 kmem_free(buf
, bufsize
+ 1);
455 if (!(ksp
->ks_flags
& KSTAT_FLAG_WRITABLE
)) {
457 kmem_free(buf
, bufsize
+ 1);
462 * With KSTAT_FLAG_VAR_SIZE, one must call the kstat's update callback
463 * routine to ensure ks_data_size is up to date.
464 * In this case it makes sense to do it anyhow, as it will be shortly
465 * followed by a KSTAT_SNAPSHOT().
468 error
= KSTAT_UPDATE(ksp
, KSTAT_READ
);
469 if (error
|| user_kstat
.ks_data_size
!= ksp
->ks_data_size
||
470 user_kstat
.ks_ndata
!= ksp
->ks_ndata
) {
473 kmem_free(buf
, bufsize
+ 1);
474 return (error
? error
: EINVAL
);
478 * We have to ensure that we don't accidentally change the type of
479 * existing kstat_named statistics when writing over them.
480 * Since read_kstat_data() modifies some of the types on their way
481 * out, we need to be sure to handle these types seperately.
483 if (ksp
->ks_type
== KSTAT_TYPE_NAMED
) {
486 kstat_named_t
*knew
= buf
;
489 #ifdef _MULTI_DATAMODEL
490 int model
= ddi_model_convert_from(flag
& FMODELS
);
494 * Since ksp->ks_data may be NULL, we need to take a snapshot
495 * of the published data to look at the types.
497 kbuf
= kmem_alloc(bufsize
+ 1, KM_NOSLEEP
);
501 kmem_free(buf
, bufsize
+ 1);
504 error
= KSTAT_SNAPSHOT(ksp
, kbuf
, KSTAT_READ
);
508 kmem_free(kbuf
, bufsize
+ 1);
509 kmem_free(buf
, bufsize
+ 1);
515 * read_kstat_data() changes the types of
516 * KSTAT_DATA_LONG / KSTAT_DATA_ULONG, so we need to
517 * make sure that these (modified) types are considered
520 for (i
= 0; i
< ksp
->ks_ndata
; i
++, kold
++, knew
++) {
521 switch (kold
->data_type
) {
522 #ifdef _MULTI_DATAMODEL
523 case KSTAT_DATA_LONG
:
525 case DDI_MODEL_ILP32
:
526 if (knew
->data_type
==
529 (long)knew
->value
.i32
;
537 if (knew
->data_type
==
540 (long)knew
->value
.i64
;
548 case KSTAT_DATA_ULONG
:
550 case DDI_MODEL_ILP32
:
551 if (knew
->data_type
==
554 (ulong_t
)knew
->value
.ui32
;
562 if (knew
->data_type
==
565 (ulong_t
)knew
->value
.ui64
;
573 #endif /* _MULTI_DATAMODEL */
574 case KSTAT_DATA_STRING
:
575 if (knew
->data_type
!= KSTAT_DATA_STRING
) {
578 kmem_free(kbuf
, bufsize
+ 1);
579 kmem_free(buf
, bufsize
+ 1);
583 #ifdef _MULTI_DATAMODEL
584 if (model
== DDI_MODEL_ILP32
)
585 KSTAT_NAMED_STR_PTR(knew
) =
587 knew
->value
.str
.addr
.ptr32
;
590 * Nothing special for NULL
592 if (KSTAT_NAMED_STR_PTR(knew
) == NULL
)
596 * Check to see that the pointers all point
597 * to within the buffer and after the array
598 * of kstat_named_t's.
600 if (KSTAT_NAMED_STR_PTR(knew
) <
602 ((kstat_named_t
*)user_kstat
.ks_data
+
606 kmem_free(kbuf
, bufsize
+ 1);
607 kmem_free(buf
, bufsize
+ 1);
610 if (KSTAT_NAMED_STR_PTR(knew
) +
611 KSTAT_NAMED_STR_BUFLEN(knew
) >
612 ((char *)user_kstat
.ks_data
+
613 ksp
->ks_data_size
)) {
616 kmem_free(kbuf
, bufsize
+ 1);
617 kmem_free(buf
, bufsize
+ 1);
622 * Update the pointers within the buffer
624 KSTAT_NAMED_STR_PTR(knew
) =
626 (KSTAT_NAMED_STR_PTR(knew
) -
627 (char *)user_kstat
.ks_data
);
638 * Now make sure the types are what we expected them to be.
640 for (i
= 0; i
< ksp
->ks_ndata
; i
++, kold
++, knew
++)
641 if (kold
->data_type
!= knew
->data_type
) {
644 kmem_free(kbuf
, bufsize
+ 1);
645 kmem_free(buf
, bufsize
+ 1);
649 kmem_free(kbuf
, bufsize
+ 1);
652 error
= KSTAT_SNAPSHOT(ksp
, buf
, KSTAT_WRITE
);
654 error
= KSTAT_UPDATE(ksp
, KSTAT_WRITE
);
655 *rvalp
= kstat_chain_id
;
658 kmem_free(buf
, bufsize
+ 1);
664 kstat_ioctl(dev_t dev
, int cmd
, intptr_t data
, int flag
, cred_t
*cr
, int *rvalp
)
670 case KSTAT_IOC_CHAIN_ID
:
671 *rvalp
= kstat_chain_id
;
675 rc
= read_kstat_data(rvalp
, (void *)data
, flag
);
678 case KSTAT_IOC_WRITE
:
679 rc
= write_kstat_data(rvalp
, (void *)data
, flag
, cr
);
683 /* invalid request */
691 kstat_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
695 case DDI_INFO_DEVT2DEVINFO
:
696 *result
= kstat_devi
;
697 return (DDI_SUCCESS
);
698 case DDI_INFO_DEVT2INSTANCE
:
700 return (DDI_SUCCESS
);
702 return (DDI_FAILURE
);
706 kstat_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
708 if (cmd
!= DDI_ATTACH
)
709 return (DDI_FAILURE
);
711 if (ddi_create_minor_node(devi
, "kstat", S_IFCHR
,
712 0, DDI_PSEUDO
, 0) == DDI_FAILURE
) {
713 ddi_remove_minor_node(devi
, NULL
);
714 return (DDI_FAILURE
);
717 return (DDI_SUCCESS
);
721 kstat_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
723 if (cmd
!= DDI_DETACH
)
724 return (DDI_FAILURE
);
726 ddi_remove_minor_node(devi
, NULL
);
727 return (DDI_SUCCESS
);
730 static struct cb_ops kstat_cb_ops
= {
733 nodev
, /* strategy */
738 kstat_ioctl
, /* ioctl */
743 ddi_prop_op
, /* prop_op */
745 D_NEW
| D_MP
/* Driver compatibility flag */
748 static struct dev_ops kstat_ops
= {
749 DEVO_REV
, /* devo_rev, */
751 kstat_info
, /* get_dev_info */
752 nulldev
, /* identify */
754 kstat_attach
, /* attach */
755 kstat_detach
, /* detach */
757 &kstat_cb_ops
, /* driver operations */
758 NULL
, /* no bus operations */
760 ddi_quiesce_not_needed
, /* quiesce */
763 static struct modldrv modldrv
= {
764 &mod_driverops
, "kernel statistics driver", &kstat_ops
,
767 static struct modlinkage modlinkage
= {
768 MODREV_1
, &modldrv
, NULL
774 return (mod_install(&modlinkage
));
780 return (mod_remove(&modlinkage
));
784 _info(struct modinfo
*modinfop
)
786 return (mod_info(&modlinkage
, modinfop
));