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 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 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 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 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 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 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 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 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 7520Sstevel@tonic-gate _info(struct modinfo *modinfop) 7530Sstevel@tonic-gate { 7540Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 7550Sstevel@tonic-gate } 756