10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*7656SSherry.Moore@Sun.COM * Common Development and Distribution License (the "License").
6*7656SSherry.Moore@Sun.COM * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate * kernel statistics driver
290Sstevel@tonic-gate */
300Sstevel@tonic-gate
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <sys/time.h>
330Sstevel@tonic-gate #include <sys/param.h>
340Sstevel@tonic-gate #include <sys/sysmacros.h>
350Sstevel@tonic-gate #include <sys/file.h>
360Sstevel@tonic-gate #include <sys/cmn_err.h>
370Sstevel@tonic-gate #include <sys/t_lock.h>
380Sstevel@tonic-gate #include <sys/proc.h>
390Sstevel@tonic-gate #include <sys/fcntl.h>
400Sstevel@tonic-gate #include <sys/uio.h>
410Sstevel@tonic-gate #include <sys/kmem.h>
420Sstevel@tonic-gate #include <sys/cred.h>
430Sstevel@tonic-gate #include <sys/mman.h>
440Sstevel@tonic-gate #include <sys/errno.h>
450Sstevel@tonic-gate #include <sys/ioccom.h>
460Sstevel@tonic-gate #include <sys/cpuvar.h>
470Sstevel@tonic-gate #include <sys/stat.h>
480Sstevel@tonic-gate #include <sys/conf.h>
490Sstevel@tonic-gate #include <sys/ddi.h>
500Sstevel@tonic-gate #include <sys/sunddi.h>
510Sstevel@tonic-gate #include <sys/modctl.h>
520Sstevel@tonic-gate #include <sys/kobj.h>
530Sstevel@tonic-gate #include <sys/kstat.h>
540Sstevel@tonic-gate #include <sys/atomic.h>
550Sstevel@tonic-gate #include <sys/policy.h>
560Sstevel@tonic-gate #include <sys/zone.h>
570Sstevel@tonic-gate
580Sstevel@tonic-gate static dev_info_t *kstat_devi;
590Sstevel@tonic-gate
600Sstevel@tonic-gate static int
read_kstat_data(int * rvalp,void * user_ksp,int flag)610Sstevel@tonic-gate read_kstat_data(int *rvalp, void *user_ksp, int flag)
620Sstevel@tonic-gate {
630Sstevel@tonic-gate kstat_t user_kstat, *ksp;
640Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
650Sstevel@tonic-gate kstat32_t user_kstat32;
660Sstevel@tonic-gate #endif
670Sstevel@tonic-gate void *kbuf = NULL;
680Sstevel@tonic-gate size_t kbufsize, ubufsize, copysize;
690Sstevel@tonic-gate int error = 0;
700Sstevel@tonic-gate uint_t model;
710Sstevel@tonic-gate
720Sstevel@tonic-gate switch (model = ddi_model_convert_from(flag & FMODELS)) {
730Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
740Sstevel@tonic-gate case DDI_MODEL_ILP32:
750Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat32, sizeof (kstat32_t)) != 0)
760Sstevel@tonic-gate return (EFAULT);
770Sstevel@tonic-gate user_kstat.ks_kid = user_kstat32.ks_kid;
780Sstevel@tonic-gate user_kstat.ks_data = (void *)(uintptr_t)user_kstat32.ks_data;
790Sstevel@tonic-gate user_kstat.ks_data_size = (size_t)user_kstat32.ks_data_size;
800Sstevel@tonic-gate break;
810Sstevel@tonic-gate #endif
820Sstevel@tonic-gate default:
830Sstevel@tonic-gate case DDI_MODEL_NONE:
840Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat, sizeof (kstat_t)) != 0)
850Sstevel@tonic-gate return (EFAULT);
860Sstevel@tonic-gate }
870Sstevel@tonic-gate
880Sstevel@tonic-gate ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid());
890Sstevel@tonic-gate if (ksp == NULL) {
900Sstevel@tonic-gate /*
910Sstevel@tonic-gate * There is no kstat with the specified KID
920Sstevel@tonic-gate */
930Sstevel@tonic-gate return (ENXIO);
940Sstevel@tonic-gate }
950Sstevel@tonic-gate if (ksp->ks_flags & KSTAT_FLAG_INVALID) {
960Sstevel@tonic-gate /*
970Sstevel@tonic-gate * The kstat exists, but is momentarily in some
980Sstevel@tonic-gate * indeterminate state (e.g. the data section is not
990Sstevel@tonic-gate * yet initialized). Try again in a few milliseconds.
1000Sstevel@tonic-gate */
1010Sstevel@tonic-gate kstat_rele(ksp);
1020Sstevel@tonic-gate return (EAGAIN);
1030Sstevel@tonic-gate }
1040Sstevel@tonic-gate
1050Sstevel@tonic-gate /*
1060Sstevel@tonic-gate * If it's a fixed-size kstat, allocate the buffer now, so we
1070Sstevel@tonic-gate * don't have to do it under the kstat's data lock. (If it's a
1080Sstevel@tonic-gate * var-size kstat, we don't know the size until after the update
1090Sstevel@tonic-gate * routine is called, so we can't do this optimization.)
1100Sstevel@tonic-gate * The allocator relies on this behavior to prevent recursive
1110Sstevel@tonic-gate * mutex_enter in its (fixed-size) kstat update routine.
1120Sstevel@tonic-gate * It's a zalloc to prevent unintentional exposure of random
1130Sstevel@tonic-gate * juicy morsels of (old) kernel data.
1140Sstevel@tonic-gate */
1150Sstevel@tonic-gate if (!(ksp->ks_flags & KSTAT_FLAG_VAR_SIZE)) {
1160Sstevel@tonic-gate kbufsize = ksp->ks_data_size;
1170Sstevel@tonic-gate kbuf = kmem_zalloc(kbufsize + 1, KM_NOSLEEP);
1180Sstevel@tonic-gate if (kbuf == NULL) {
1190Sstevel@tonic-gate kstat_rele(ksp);
1200Sstevel@tonic-gate return (EAGAIN);
1210Sstevel@tonic-gate }
1220Sstevel@tonic-gate }
1230Sstevel@tonic-gate KSTAT_ENTER(ksp);
1240Sstevel@tonic-gate if ((error = KSTAT_UPDATE(ksp, KSTAT_READ)) != 0) {
1250Sstevel@tonic-gate KSTAT_EXIT(ksp);
1260Sstevel@tonic-gate kstat_rele(ksp);
1270Sstevel@tonic-gate if (kbuf != NULL)
1280Sstevel@tonic-gate kmem_free(kbuf, kbufsize + 1);
1290Sstevel@tonic-gate return (error);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate kbufsize = ksp->ks_data_size;
1330Sstevel@tonic-gate ubufsize = user_kstat.ks_data_size;
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate if (ubufsize < kbufsize) {
1360Sstevel@tonic-gate error = ENOMEM;
1370Sstevel@tonic-gate } else {
1380Sstevel@tonic-gate if (kbuf == NULL)
1390Sstevel@tonic-gate kbuf = kmem_zalloc(kbufsize + 1, KM_NOSLEEP);
1400Sstevel@tonic-gate if (kbuf == NULL) {
1410Sstevel@tonic-gate error = EAGAIN;
1420Sstevel@tonic-gate } else {
1430Sstevel@tonic-gate error = KSTAT_SNAPSHOT(ksp, kbuf, KSTAT_READ);
1440Sstevel@tonic-gate }
1450Sstevel@tonic-gate }
1460Sstevel@tonic-gate
1470Sstevel@tonic-gate /*
1480Sstevel@tonic-gate * The following info must be returned to user level,
1490Sstevel@tonic-gate * even if the the update or snapshot failed. This allows
1500Sstevel@tonic-gate * kstat readers to get a handle on variable-size kstats,
1510Sstevel@tonic-gate * detect dormant kstats, etc.
1520Sstevel@tonic-gate */
1530Sstevel@tonic-gate user_kstat.ks_ndata = ksp->ks_ndata;
1540Sstevel@tonic-gate user_kstat.ks_data_size = kbufsize;
1550Sstevel@tonic-gate user_kstat.ks_flags = ksp->ks_flags;
1560Sstevel@tonic-gate user_kstat.ks_snaptime = ksp->ks_snaptime;
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate *rvalp = kstat_chain_id;
1590Sstevel@tonic-gate KSTAT_EXIT(ksp);
1600Sstevel@tonic-gate kstat_rele(ksp);
1610Sstevel@tonic-gate
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate * Copy the buffer containing the kstat back to userland.
1640Sstevel@tonic-gate */
1650Sstevel@tonic-gate copysize = kbufsize;
1660Sstevel@tonic-gate if (kbuf != NULL) {
1670Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
1680Sstevel@tonic-gate kstat32_t *k32;
1690Sstevel@tonic-gate kstat_t *k;
1700Sstevel@tonic-gate #endif
1710Sstevel@tonic-gate int i;
1720Sstevel@tonic-gate
1730Sstevel@tonic-gate switch (model) {
1740Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
1750Sstevel@tonic-gate case DDI_MODEL_ILP32:
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate if (ksp->ks_type == KSTAT_TYPE_NAMED) {
1780Sstevel@tonic-gate kstat_named_t *kn = kbuf;
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate for (i = 0; i < user_kstat.ks_ndata; kn++, i++)
1810Sstevel@tonic-gate switch (kn->data_type) {
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate * Named statistics have fields of type
1840Sstevel@tonic-gate * 'long'. For a 32-bit application
1850Sstevel@tonic-gate * looking at a 64-bit kernel,
1860Sstevel@tonic-gate * forcibly truncate these 64-bit
1870Sstevel@tonic-gate * quantities to 32-bit values.
1880Sstevel@tonic-gate */
1890Sstevel@tonic-gate case KSTAT_DATA_LONG:
1900Sstevel@tonic-gate kn->value.i32 =
1910Sstevel@tonic-gate (int32_t)kn->value.l;
1920Sstevel@tonic-gate kn->data_type =
1930Sstevel@tonic-gate KSTAT_DATA_INT32;
1940Sstevel@tonic-gate break;
1950Sstevel@tonic-gate case KSTAT_DATA_ULONG:
1960Sstevel@tonic-gate kn->value.ui32 =
1970Sstevel@tonic-gate (uint32_t)kn->value.ul;
1980Sstevel@tonic-gate kn->data_type =
1990Sstevel@tonic-gate KSTAT_DATA_UINT32;
2000Sstevel@tonic-gate break;
2010Sstevel@tonic-gate /*
2020Sstevel@tonic-gate * Long strings must be massaged before
2030Sstevel@tonic-gate * being copied out to userland. Do
2040Sstevel@tonic-gate * that here.
2050Sstevel@tonic-gate */
2060Sstevel@tonic-gate case KSTAT_DATA_STRING:
2070Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(kn)
2080Sstevel@tonic-gate == NULL)
2090Sstevel@tonic-gate break;
2100Sstevel@tonic-gate /*
2110Sstevel@tonic-gate * The offsets within the
2120Sstevel@tonic-gate * buffers are the same, so add
2130Sstevel@tonic-gate * the offset to the beginning
2140Sstevel@tonic-gate * of the new buffer to fix the
2150Sstevel@tonic-gate * pointer.
2160Sstevel@tonic-gate */
2170Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(kn) =
2180Sstevel@tonic-gate (char *)user_kstat.ks_data +
2190Sstevel@tonic-gate (KSTAT_NAMED_STR_PTR(kn) -
2200Sstevel@tonic-gate (char *)kbuf);
2210Sstevel@tonic-gate /*
2220Sstevel@tonic-gate * Make sure the string pointer
2230Sstevel@tonic-gate * lies within the allocated
2240Sstevel@tonic-gate * buffer.
2250Sstevel@tonic-gate */
2260Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn) +
2270Sstevel@tonic-gate KSTAT_NAMED_STR_BUFLEN(kn)
2280Sstevel@tonic-gate <=
2290Sstevel@tonic-gate ((char *)
2300Sstevel@tonic-gate user_kstat.ks_data +
2310Sstevel@tonic-gate ubufsize));
2320Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn)
2330Sstevel@tonic-gate >=
2340Sstevel@tonic-gate (char *)
2350Sstevel@tonic-gate ((kstat_named_t *)
2360Sstevel@tonic-gate user_kstat.ks_data +
2370Sstevel@tonic-gate user_kstat.ks_ndata));
2380Sstevel@tonic-gate /*
2390Sstevel@tonic-gate * Cast 64-bit ptr to 32-bit.
2400Sstevel@tonic-gate */
241457Sbmc kn->value.str.addr.ptr32 =
2420Sstevel@tonic-gate (caddr32_t)(uintptr_t)
2430Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(kn);
2440Sstevel@tonic-gate break;
2450Sstevel@tonic-gate default:
2460Sstevel@tonic-gate break;
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate }
2490Sstevel@tonic-gate
2500Sstevel@tonic-gate if (user_kstat.ks_kid != 0)
2510Sstevel@tonic-gate break;
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate /*
2540Sstevel@tonic-gate * This is the special case of the kstat header
2550Sstevel@tonic-gate * list for the entire system. Reshape the
2560Sstevel@tonic-gate * array in place, then copy it out.
2570Sstevel@tonic-gate */
2580Sstevel@tonic-gate k32 = kbuf;
2590Sstevel@tonic-gate k = kbuf;
2600Sstevel@tonic-gate for (i = 0; i < user_kstat.ks_ndata; k32++, k++, i++) {
2610Sstevel@tonic-gate k32->ks_crtime = k->ks_crtime;
2620Sstevel@tonic-gate k32->ks_next = 0;
2630Sstevel@tonic-gate k32->ks_kid = k->ks_kid;
2640Sstevel@tonic-gate (void) strcpy(k32->ks_module, k->ks_module);
2650Sstevel@tonic-gate k32->ks_resv = k->ks_resv;
2660Sstevel@tonic-gate k32->ks_instance = k->ks_instance;
2670Sstevel@tonic-gate (void) strcpy(k32->ks_name, k->ks_name);
2680Sstevel@tonic-gate k32->ks_type = k->ks_type;
2690Sstevel@tonic-gate (void) strcpy(k32->ks_class, k->ks_class);
2700Sstevel@tonic-gate k32->ks_flags = k->ks_flags;
2710Sstevel@tonic-gate k32->ks_data = 0;
2720Sstevel@tonic-gate k32->ks_ndata = k->ks_ndata;
2730Sstevel@tonic-gate if (k->ks_data_size > UINT32_MAX) {
2740Sstevel@tonic-gate error = EOVERFLOW;
2750Sstevel@tonic-gate break;
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate k32->ks_data_size = (size32_t)k->ks_data_size;
2780Sstevel@tonic-gate k32->ks_snaptime = k->ks_snaptime;
2790Sstevel@tonic-gate }
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate /*
2820Sstevel@tonic-gate * XXX In this case we copy less data than is
2830Sstevel@tonic-gate * claimed in the header.
2840Sstevel@tonic-gate */
2850Sstevel@tonic-gate copysize = user_kstat.ks_ndata * sizeof (kstat32_t);
2860Sstevel@tonic-gate break;
2870Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
2880Sstevel@tonic-gate default:
2890Sstevel@tonic-gate case DDI_MODEL_NONE:
2900Sstevel@tonic-gate if (ksp->ks_type == KSTAT_TYPE_NAMED) {
2910Sstevel@tonic-gate kstat_named_t *kn = kbuf;
2920Sstevel@tonic-gate
2930Sstevel@tonic-gate for (i = 0; i < user_kstat.ks_ndata; kn++, i++)
2940Sstevel@tonic-gate switch (kn->data_type) {
2950Sstevel@tonic-gate #ifdef _LP64
2960Sstevel@tonic-gate case KSTAT_DATA_LONG:
2970Sstevel@tonic-gate kn->data_type =
2980Sstevel@tonic-gate KSTAT_DATA_INT64;
2990Sstevel@tonic-gate break;
3000Sstevel@tonic-gate case KSTAT_DATA_ULONG:
3010Sstevel@tonic-gate kn->data_type =
3020Sstevel@tonic-gate KSTAT_DATA_UINT64;
3030Sstevel@tonic-gate break;
3040Sstevel@tonic-gate #endif /* _LP64 */
3050Sstevel@tonic-gate case KSTAT_DATA_STRING:
3060Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(kn)
3070Sstevel@tonic-gate == NULL)
3080Sstevel@tonic-gate break;
3090Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(kn) =
3100Sstevel@tonic-gate (char *)user_kstat.ks_data +
3110Sstevel@tonic-gate (KSTAT_NAMED_STR_PTR(kn) -
3120Sstevel@tonic-gate (char *)kbuf);
3130Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn) +
3140Sstevel@tonic-gate KSTAT_NAMED_STR_BUFLEN(kn)
3150Sstevel@tonic-gate <=
3160Sstevel@tonic-gate ((char *)
3170Sstevel@tonic-gate user_kstat.ks_data +
3180Sstevel@tonic-gate ubufsize));
3190Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn)
3200Sstevel@tonic-gate >=
3210Sstevel@tonic-gate (char *)
3220Sstevel@tonic-gate ((kstat_named_t *)
3230Sstevel@tonic-gate user_kstat.ks_data +
3240Sstevel@tonic-gate user_kstat.ks_ndata));
3250Sstevel@tonic-gate break;
3260Sstevel@tonic-gate default:
3270Sstevel@tonic-gate break;
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate break;
3310Sstevel@tonic-gate }
3320Sstevel@tonic-gate
3330Sstevel@tonic-gate if (error == 0 &&
3340Sstevel@tonic-gate copyout(kbuf, user_kstat.ks_data, copysize))
3350Sstevel@tonic-gate error = EFAULT;
3360Sstevel@tonic-gate kmem_free(kbuf, kbufsize + 1);
3370Sstevel@tonic-gate }
3380Sstevel@tonic-gate
3390Sstevel@tonic-gate /*
3400Sstevel@tonic-gate * We have modified the ks_ndata, ks_data_size, ks_flags, and
3410Sstevel@tonic-gate * ks_snaptime fields of the user kstat; now copy it back to userland.
3420Sstevel@tonic-gate */
3430Sstevel@tonic-gate switch (model) {
3440Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
3450Sstevel@tonic-gate case DDI_MODEL_ILP32:
3460Sstevel@tonic-gate if (kbufsize > UINT32_MAX) {
3470Sstevel@tonic-gate error = EOVERFLOW;
3480Sstevel@tonic-gate break;
3490Sstevel@tonic-gate }
3500Sstevel@tonic-gate user_kstat32.ks_ndata = user_kstat.ks_ndata;
3510Sstevel@tonic-gate user_kstat32.ks_data_size = (size32_t)kbufsize;
3520Sstevel@tonic-gate user_kstat32.ks_flags = user_kstat.ks_flags;
3530Sstevel@tonic-gate user_kstat32.ks_snaptime = user_kstat.ks_snaptime;
3540Sstevel@tonic-gate if (copyout(&user_kstat32, user_ksp, sizeof (kstat32_t)) &&
3550Sstevel@tonic-gate error == 0)
3560Sstevel@tonic-gate error = EFAULT;
3570Sstevel@tonic-gate break;
3580Sstevel@tonic-gate #endif
3590Sstevel@tonic-gate default:
3600Sstevel@tonic-gate case DDI_MODEL_NONE:
3610Sstevel@tonic-gate if (copyout(&user_kstat, user_ksp, sizeof (kstat_t)) &&
3620Sstevel@tonic-gate error == 0)
3630Sstevel@tonic-gate error = EFAULT;
3640Sstevel@tonic-gate break;
3650Sstevel@tonic-gate }
3660Sstevel@tonic-gate
3670Sstevel@tonic-gate return (error);
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate static int
write_kstat_data(int * rvalp,void * user_ksp,int flag,cred_t * cred)3710Sstevel@tonic-gate write_kstat_data(int *rvalp, void *user_ksp, int flag, cred_t *cred)
3720Sstevel@tonic-gate {
3730Sstevel@tonic-gate kstat_t user_kstat, *ksp;
3740Sstevel@tonic-gate void *buf = NULL;
3750Sstevel@tonic-gate size_t bufsize;
3760Sstevel@tonic-gate int error = 0;
3770Sstevel@tonic-gate
3780Sstevel@tonic-gate if (secpolicy_sys_config(cred, B_FALSE) != 0)
3790Sstevel@tonic-gate return (EPERM);
3800Sstevel@tonic-gate
3810Sstevel@tonic-gate switch (ddi_model_convert_from(flag & FMODELS)) {
3820Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
3830Sstevel@tonic-gate kstat32_t user_kstat32;
3840Sstevel@tonic-gate
3850Sstevel@tonic-gate case DDI_MODEL_ILP32:
3860Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat32, sizeof (kstat32_t)))
3870Sstevel@tonic-gate return (EFAULT);
3880Sstevel@tonic-gate /*
3890Sstevel@tonic-gate * These are the only fields we actually look at.
3900Sstevel@tonic-gate */
3910Sstevel@tonic-gate user_kstat.ks_kid = user_kstat32.ks_kid;
3920Sstevel@tonic-gate user_kstat.ks_data = (void *)(uintptr_t)user_kstat32.ks_data;
3930Sstevel@tonic-gate user_kstat.ks_data_size = (size_t)user_kstat32.ks_data_size;
3940Sstevel@tonic-gate user_kstat.ks_ndata = user_kstat32.ks_ndata;
3950Sstevel@tonic-gate break;
3960Sstevel@tonic-gate #endif
3970Sstevel@tonic-gate default:
3980Sstevel@tonic-gate case DDI_MODEL_NONE:
3990Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat, sizeof (kstat_t)))
4000Sstevel@tonic-gate return (EFAULT);
4010Sstevel@tonic-gate }
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate bufsize = user_kstat.ks_data_size;
4040Sstevel@tonic-gate buf = kmem_alloc(bufsize + 1, KM_NOSLEEP);
4050Sstevel@tonic-gate if (buf == NULL)
4060Sstevel@tonic-gate return (EAGAIN);
4070Sstevel@tonic-gate
4080Sstevel@tonic-gate if (copyin(user_kstat.ks_data, buf, bufsize)) {
4090Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
4100Sstevel@tonic-gate return (EFAULT);
4110Sstevel@tonic-gate }
4120Sstevel@tonic-gate
4130Sstevel@tonic-gate ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid());
4140Sstevel@tonic-gate if (ksp == NULL) {
4150Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
4160Sstevel@tonic-gate return (ENXIO);
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate if (ksp->ks_flags & KSTAT_FLAG_INVALID) {
4190Sstevel@tonic-gate kstat_rele(ksp);
4200Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
4210Sstevel@tonic-gate return (EAGAIN);
4220Sstevel@tonic-gate }
4230Sstevel@tonic-gate if (!(ksp->ks_flags & KSTAT_FLAG_WRITABLE)) {
4240Sstevel@tonic-gate kstat_rele(ksp);
4250Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
4260Sstevel@tonic-gate return (EACCES);
4270Sstevel@tonic-gate }
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate /*
4300Sstevel@tonic-gate * With KSTAT_FLAG_VARIABLE, one must call the kstat's update callback
4310Sstevel@tonic-gate * routine to ensure ks_data_size is up to date.
4320Sstevel@tonic-gate * In this case it makes sense to do it anyhow, as it will be shortly
4330Sstevel@tonic-gate * followed by a KSTAT_SNAPSHOT().
4340Sstevel@tonic-gate */
4350Sstevel@tonic-gate KSTAT_ENTER(ksp);
4360Sstevel@tonic-gate error = KSTAT_UPDATE(ksp, KSTAT_READ);
4370Sstevel@tonic-gate if (error || user_kstat.ks_data_size != ksp->ks_data_size ||
4380Sstevel@tonic-gate user_kstat.ks_ndata != ksp->ks_ndata) {
4390Sstevel@tonic-gate KSTAT_EXIT(ksp);
4400Sstevel@tonic-gate kstat_rele(ksp);
4410Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
4420Sstevel@tonic-gate return (error ? error : EINVAL);
4430Sstevel@tonic-gate }
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate /*
4460Sstevel@tonic-gate * We have to ensure that we don't accidentally change the type of
4470Sstevel@tonic-gate * existing kstat_named statistics when writing over them.
4480Sstevel@tonic-gate * Since read_kstat_data() modifies some of the types on their way
4490Sstevel@tonic-gate * out, we need to be sure to handle these types seperately.
4500Sstevel@tonic-gate */
4510Sstevel@tonic-gate if (ksp->ks_type == KSTAT_TYPE_NAMED) {
4520Sstevel@tonic-gate void *kbuf;
4530Sstevel@tonic-gate kstat_named_t *kold;
4540Sstevel@tonic-gate kstat_named_t *knew = buf;
4550Sstevel@tonic-gate int i;
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
4580Sstevel@tonic-gate int model = ddi_model_convert_from(flag & FMODELS);
4590Sstevel@tonic-gate #endif
4600Sstevel@tonic-gate
4610Sstevel@tonic-gate /*
4620Sstevel@tonic-gate * Since ksp->ks_data may be NULL, we need to take a snapshot
4630Sstevel@tonic-gate * of the published data to look at the types.
4640Sstevel@tonic-gate */
4650Sstevel@tonic-gate kbuf = kmem_alloc(bufsize + 1, KM_NOSLEEP);
4660Sstevel@tonic-gate if (kbuf == NULL) {
4670Sstevel@tonic-gate KSTAT_EXIT(ksp);
4680Sstevel@tonic-gate kstat_rele(ksp);
4690Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
4700Sstevel@tonic-gate return (EAGAIN);
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate error = KSTAT_SNAPSHOT(ksp, kbuf, KSTAT_READ);
4730Sstevel@tonic-gate if (error) {
4740Sstevel@tonic-gate KSTAT_EXIT(ksp);
4750Sstevel@tonic-gate kstat_rele(ksp);
4760Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1);
4770Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
4780Sstevel@tonic-gate return (error);
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate kold = kbuf;
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate /*
4830Sstevel@tonic-gate * read_kstat_data() changes the types of
4840Sstevel@tonic-gate * KSTAT_DATA_LONG / KSTAT_DATA_ULONG, so we need to
4850Sstevel@tonic-gate * make sure that these (modified) types are considered
4860Sstevel@tonic-gate * valid.
4870Sstevel@tonic-gate */
4880Sstevel@tonic-gate for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++) {
4890Sstevel@tonic-gate switch (kold->data_type) {
4900Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
4910Sstevel@tonic-gate case KSTAT_DATA_LONG:
4920Sstevel@tonic-gate switch (model) {
4930Sstevel@tonic-gate case DDI_MODEL_ILP32:
4940Sstevel@tonic-gate if (knew->data_type ==
4950Sstevel@tonic-gate KSTAT_DATA_INT32) {
4960Sstevel@tonic-gate knew->value.l =
4970Sstevel@tonic-gate (long)knew->value.i32;
4980Sstevel@tonic-gate knew->data_type =
4990Sstevel@tonic-gate KSTAT_DATA_LONG;
5000Sstevel@tonic-gate }
5010Sstevel@tonic-gate break;
5020Sstevel@tonic-gate default:
5030Sstevel@tonic-gate case DDI_MODEL_NONE:
5040Sstevel@tonic-gate #ifdef _LP64
5050Sstevel@tonic-gate if (knew->data_type ==
5060Sstevel@tonic-gate KSTAT_DATA_INT64) {
5070Sstevel@tonic-gate knew->value.l =
5080Sstevel@tonic-gate (long)knew->value.i64;
5090Sstevel@tonic-gate knew->data_type =
5100Sstevel@tonic-gate KSTAT_DATA_LONG;
5110Sstevel@tonic-gate }
5120Sstevel@tonic-gate #endif /* _LP64 */
5130Sstevel@tonic-gate break;
5140Sstevel@tonic-gate }
5150Sstevel@tonic-gate break;
5160Sstevel@tonic-gate case KSTAT_DATA_ULONG:
5170Sstevel@tonic-gate switch (model) {
5180Sstevel@tonic-gate case DDI_MODEL_ILP32:
5190Sstevel@tonic-gate if (knew->data_type ==
5200Sstevel@tonic-gate KSTAT_DATA_UINT32) {
5210Sstevel@tonic-gate knew->value.ul =
5220Sstevel@tonic-gate (ulong_t)knew->value.ui32;
5230Sstevel@tonic-gate knew->data_type =
5240Sstevel@tonic-gate KSTAT_DATA_ULONG;
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate break;
5270Sstevel@tonic-gate default:
5280Sstevel@tonic-gate case DDI_MODEL_NONE:
5290Sstevel@tonic-gate #ifdef _LP64
5300Sstevel@tonic-gate if (knew->data_type ==
5310Sstevel@tonic-gate KSTAT_DATA_UINT64) {
5320Sstevel@tonic-gate knew->value.ul =
5330Sstevel@tonic-gate (ulong_t)knew->value.ui64;
5340Sstevel@tonic-gate knew->data_type =
5350Sstevel@tonic-gate KSTAT_DATA_ULONG;
5360Sstevel@tonic-gate }
5370Sstevel@tonic-gate #endif /* _LP64 */
5380Sstevel@tonic-gate break;
5390Sstevel@tonic-gate }
5400Sstevel@tonic-gate break;
5410Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
5420Sstevel@tonic-gate case KSTAT_DATA_STRING:
5430Sstevel@tonic-gate if (knew->data_type != KSTAT_DATA_STRING) {
5440Sstevel@tonic-gate KSTAT_EXIT(ksp);
5450Sstevel@tonic-gate kstat_rele(ksp);
5460Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1);
5470Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
5480Sstevel@tonic-gate return (EINVAL);
5490Sstevel@tonic-gate }
5500Sstevel@tonic-gate
5510Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
5520Sstevel@tonic-gate if (model == DDI_MODEL_ILP32)
5530Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(knew) =
5540Sstevel@tonic-gate (char *)(uintptr_t)
555*7656SSherry.Moore@Sun.COM knew->value.str.addr.ptr32;
5560Sstevel@tonic-gate #endif
5570Sstevel@tonic-gate /*
5580Sstevel@tonic-gate * Nothing special for NULL
5590Sstevel@tonic-gate */
5600Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(knew) == NULL)
5610Sstevel@tonic-gate break;
5620Sstevel@tonic-gate
5630Sstevel@tonic-gate /*
5640Sstevel@tonic-gate * Check to see that the pointers all point
5650Sstevel@tonic-gate * to within the buffer and after the array
5660Sstevel@tonic-gate * of kstat_named_t's.
5670Sstevel@tonic-gate */
5680Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(knew) <
5690Sstevel@tonic-gate (char *)
5700Sstevel@tonic-gate ((kstat_named_t *)user_kstat.ks_data +
5710Sstevel@tonic-gate ksp->ks_ndata)) {
5720Sstevel@tonic-gate KSTAT_EXIT(ksp);
5730Sstevel@tonic-gate kstat_rele(ksp);
5740Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1);
5750Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
5760Sstevel@tonic-gate return (EINVAL);
5770Sstevel@tonic-gate }
5780Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(knew) +
5790Sstevel@tonic-gate KSTAT_NAMED_STR_BUFLEN(knew) >
5800Sstevel@tonic-gate ((char *)user_kstat.ks_data +
5810Sstevel@tonic-gate ksp->ks_data_size)) {
5820Sstevel@tonic-gate KSTAT_EXIT(ksp);
5830Sstevel@tonic-gate kstat_rele(ksp);
5840Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1);
5850Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
5860Sstevel@tonic-gate return (EINVAL);
5870Sstevel@tonic-gate }
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate /*
5900Sstevel@tonic-gate * Update the pointers within the buffer
5910Sstevel@tonic-gate */
5920Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(knew) =
5930Sstevel@tonic-gate (char *)buf +
5940Sstevel@tonic-gate (KSTAT_NAMED_STR_PTR(knew) -
5950Sstevel@tonic-gate (char *)user_kstat.ks_data);
5960Sstevel@tonic-gate break;
5970Sstevel@tonic-gate default:
5980Sstevel@tonic-gate break;
5990Sstevel@tonic-gate }
6000Sstevel@tonic-gate }
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate kold = kbuf;
6030Sstevel@tonic-gate knew = buf;
6040Sstevel@tonic-gate
6050Sstevel@tonic-gate /*
6060Sstevel@tonic-gate * Now make sure the types are what we expected them to be.
6070Sstevel@tonic-gate */
6080Sstevel@tonic-gate for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++)
6090Sstevel@tonic-gate if (kold->data_type != knew->data_type) {
6100Sstevel@tonic-gate KSTAT_EXIT(ksp);
6110Sstevel@tonic-gate kstat_rele(ksp);
6120Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1);
6130Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
6140Sstevel@tonic-gate return (EINVAL);
6150Sstevel@tonic-gate }
6160Sstevel@tonic-gate
6170Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1);
6180Sstevel@tonic-gate }
6190Sstevel@tonic-gate
6200Sstevel@tonic-gate error = KSTAT_SNAPSHOT(ksp, buf, KSTAT_WRITE);
6210Sstevel@tonic-gate if (!error)
6220Sstevel@tonic-gate error = KSTAT_UPDATE(ksp, KSTAT_WRITE);
6230Sstevel@tonic-gate *rvalp = kstat_chain_id;
6240Sstevel@tonic-gate KSTAT_EXIT(ksp);
6250Sstevel@tonic-gate kstat_rele(ksp);
6260Sstevel@tonic-gate kmem_free(buf, bufsize + 1);
6270Sstevel@tonic-gate return (error);
6280Sstevel@tonic-gate }
6290Sstevel@tonic-gate
6300Sstevel@tonic-gate /*ARGSUSED*/
6310Sstevel@tonic-gate static int
kstat_ioctl(dev_t dev,int cmd,intptr_t data,int flag,cred_t * cr,int * rvalp)6320Sstevel@tonic-gate kstat_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr, int *rvalp)
6330Sstevel@tonic-gate {
6340Sstevel@tonic-gate int rc = 0;
6350Sstevel@tonic-gate
6360Sstevel@tonic-gate switch (cmd) {
6370Sstevel@tonic-gate
6380Sstevel@tonic-gate case KSTAT_IOC_CHAIN_ID:
6390Sstevel@tonic-gate *rvalp = kstat_chain_id;
6400Sstevel@tonic-gate break;
6410Sstevel@tonic-gate
6420Sstevel@tonic-gate case KSTAT_IOC_READ:
6430Sstevel@tonic-gate rc = read_kstat_data(rvalp, (void *)data, flag);
6440Sstevel@tonic-gate break;
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate case KSTAT_IOC_WRITE:
6470Sstevel@tonic-gate rc = write_kstat_data(rvalp, (void *)data, flag, cr);
6480Sstevel@tonic-gate break;
6490Sstevel@tonic-gate
6500Sstevel@tonic-gate default:
6510Sstevel@tonic-gate /* invalid request */
6520Sstevel@tonic-gate rc = EINVAL;
6530Sstevel@tonic-gate }
6540Sstevel@tonic-gate return (rc);
6550Sstevel@tonic-gate }
6560Sstevel@tonic-gate
6570Sstevel@tonic-gate /* ARGSUSED */
6580Sstevel@tonic-gate static int
kstat_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)6590Sstevel@tonic-gate kstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
6600Sstevel@tonic-gate void **result)
6610Sstevel@tonic-gate {
6620Sstevel@tonic-gate switch (infocmd) {
6630Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO:
6640Sstevel@tonic-gate *result = kstat_devi;
6650Sstevel@tonic-gate return (DDI_SUCCESS);
6660Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE:
6670Sstevel@tonic-gate *result = NULL;
6680Sstevel@tonic-gate return (DDI_SUCCESS);
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate return (DDI_FAILURE);
6710Sstevel@tonic-gate }
6720Sstevel@tonic-gate
6730Sstevel@tonic-gate static int
kstat_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)6740Sstevel@tonic-gate kstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
6750Sstevel@tonic-gate {
6760Sstevel@tonic-gate if (cmd != DDI_ATTACH)
6770Sstevel@tonic-gate return (DDI_FAILURE);
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate if (ddi_create_minor_node(devi, "kstat", S_IFCHR,
6800Sstevel@tonic-gate 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
6810Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL);
6820Sstevel@tonic-gate return (DDI_FAILURE);
6830Sstevel@tonic-gate }
6840Sstevel@tonic-gate kstat_devi = devi;
6850Sstevel@tonic-gate return (DDI_SUCCESS);
6860Sstevel@tonic-gate }
6870Sstevel@tonic-gate
6880Sstevel@tonic-gate static int
kstat_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)6890Sstevel@tonic-gate kstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
6900Sstevel@tonic-gate {
6910Sstevel@tonic-gate if (cmd != DDI_DETACH)
6920Sstevel@tonic-gate return (DDI_FAILURE);
6930Sstevel@tonic-gate
6940Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL);
6950Sstevel@tonic-gate return (DDI_SUCCESS);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate
6980Sstevel@tonic-gate static struct cb_ops kstat_cb_ops = {
6990Sstevel@tonic-gate nulldev, /* open */
7000Sstevel@tonic-gate nulldev, /* close */
7010Sstevel@tonic-gate nodev, /* strategy */
7020Sstevel@tonic-gate nodev, /* print */
7030Sstevel@tonic-gate nodev, /* dump */
7040Sstevel@tonic-gate nodev, /* read */
7050Sstevel@tonic-gate nodev, /* write */
7060Sstevel@tonic-gate kstat_ioctl, /* ioctl */
7070Sstevel@tonic-gate nodev, /* devmap */
7080Sstevel@tonic-gate nodev, /* mmap */
7090Sstevel@tonic-gate nodev, /* segmap */
7100Sstevel@tonic-gate nochpoll, /* poll */
7110Sstevel@tonic-gate ddi_prop_op, /* prop_op */
7120Sstevel@tonic-gate 0, /* streamtab */
7130Sstevel@tonic-gate D_NEW | D_MP /* Driver compatibility flag */
7140Sstevel@tonic-gate };
7150Sstevel@tonic-gate
7160Sstevel@tonic-gate static struct dev_ops kstat_ops = {
7170Sstevel@tonic-gate DEVO_REV, /* devo_rev, */
7180Sstevel@tonic-gate 0, /* refcnt */
7190Sstevel@tonic-gate kstat_info, /* get_dev_info */
7200Sstevel@tonic-gate nulldev, /* identify */
7210Sstevel@tonic-gate nulldev, /* probe */
7220Sstevel@tonic-gate kstat_attach, /* attach */
7230Sstevel@tonic-gate kstat_detach, /* detach */
7240Sstevel@tonic-gate nodev, /* reset */
7250Sstevel@tonic-gate &kstat_cb_ops, /* driver operations */
726*7656SSherry.Moore@Sun.COM (struct bus_ops *)0, /* no bus operations */
727*7656SSherry.Moore@Sun.COM NULL, /* power */
728*7656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
7290Sstevel@tonic-gate };
7300Sstevel@tonic-gate
7310Sstevel@tonic-gate static struct modldrv modldrv = {
732*7656SSherry.Moore@Sun.COM &mod_driverops, "kernel statistics driver", &kstat_ops,
7330Sstevel@tonic-gate };
7340Sstevel@tonic-gate
7350Sstevel@tonic-gate static struct modlinkage modlinkage = {
7360Sstevel@tonic-gate MODREV_1, &modldrv, NULL
7370Sstevel@tonic-gate };
7380Sstevel@tonic-gate
7390Sstevel@tonic-gate int
_init(void)7400Sstevel@tonic-gate _init(void)
7410Sstevel@tonic-gate {
7420Sstevel@tonic-gate return (mod_install(&modlinkage));
7430Sstevel@tonic-gate }
7440Sstevel@tonic-gate
7450Sstevel@tonic-gate int
_fini(void)7460Sstevel@tonic-gate _fini(void)
7470Sstevel@tonic-gate {
7480Sstevel@tonic-gate return (mod_remove(&modlinkage));
7490Sstevel@tonic-gate }
7500Sstevel@tonic-gate
7510Sstevel@tonic-gate int
_info(struct modinfo * modinfop)7520Sstevel@tonic-gate _info(struct modinfo *modinfop)
7530Sstevel@tonic-gate {
7540Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
7550Sstevel@tonic-gate }
756