xref: /titanic_44/usr/src/uts/common/io/kstat.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * kernel statistics driver
31*7c478bd9Sstevel@tonic-gate  */
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/file.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/proc.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/fcntl.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/uio.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/mman.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/ioccom.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
54*7c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
55*7c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
57*7c478bd9Sstevel@tonic-gate #include <sys/policy.h>
58*7c478bd9Sstevel@tonic-gate #include <sys/zone.h>
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate static dev_info_t *kstat_devi;
61*7c478bd9Sstevel@tonic-gate 
62*7c478bd9Sstevel@tonic-gate static int
63*7c478bd9Sstevel@tonic-gate read_kstat_data(int *rvalp, void *user_ksp, int flag)
64*7c478bd9Sstevel@tonic-gate {
65*7c478bd9Sstevel@tonic-gate 	kstat_t user_kstat, *ksp;
66*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
67*7c478bd9Sstevel@tonic-gate 	kstat32_t user_kstat32;
68*7c478bd9Sstevel@tonic-gate #endif
69*7c478bd9Sstevel@tonic-gate 	void *kbuf = NULL;
70*7c478bd9Sstevel@tonic-gate 	size_t kbufsize, ubufsize, copysize;
71*7c478bd9Sstevel@tonic-gate 	int error = 0;
72*7c478bd9Sstevel@tonic-gate 	uint_t model;
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate 	switch (model = ddi_model_convert_from(flag & FMODELS)) {
75*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
76*7c478bd9Sstevel@tonic-gate 	case DDI_MODEL_ILP32:
77*7c478bd9Sstevel@tonic-gate 		if (copyin(user_ksp, &user_kstat32, sizeof (kstat32_t)) != 0)
78*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
79*7c478bd9Sstevel@tonic-gate 		user_kstat.ks_kid = user_kstat32.ks_kid;
80*7c478bd9Sstevel@tonic-gate 		user_kstat.ks_data = (void *)(uintptr_t)user_kstat32.ks_data;
81*7c478bd9Sstevel@tonic-gate 		user_kstat.ks_data_size = (size_t)user_kstat32.ks_data_size;
82*7c478bd9Sstevel@tonic-gate 		break;
83*7c478bd9Sstevel@tonic-gate #endif
84*7c478bd9Sstevel@tonic-gate 	default:
85*7c478bd9Sstevel@tonic-gate 	case DDI_MODEL_NONE:
86*7c478bd9Sstevel@tonic-gate 		if (copyin(user_ksp, &user_kstat, sizeof (kstat_t)) != 0)
87*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
88*7c478bd9Sstevel@tonic-gate 	}
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate 	ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid());
91*7c478bd9Sstevel@tonic-gate 	if (ksp == NULL) {
92*7c478bd9Sstevel@tonic-gate 		/*
93*7c478bd9Sstevel@tonic-gate 		 * There is no kstat with the specified KID
94*7c478bd9Sstevel@tonic-gate 		 */
95*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
96*7c478bd9Sstevel@tonic-gate 	}
97*7c478bd9Sstevel@tonic-gate 	if (ksp->ks_flags & KSTAT_FLAG_INVALID) {
98*7c478bd9Sstevel@tonic-gate 		/*
99*7c478bd9Sstevel@tonic-gate 		 * The kstat exists, but is momentarily in some
100*7c478bd9Sstevel@tonic-gate 		 * indeterminate state (e.g. the data section is not
101*7c478bd9Sstevel@tonic-gate 		 * yet initialized).  Try again in a few milliseconds.
102*7c478bd9Sstevel@tonic-gate 		 */
103*7c478bd9Sstevel@tonic-gate 		kstat_rele(ksp);
104*7c478bd9Sstevel@tonic-gate 		return (EAGAIN);
105*7c478bd9Sstevel@tonic-gate 	}
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate 	/*
108*7c478bd9Sstevel@tonic-gate 	 * If it's a fixed-size kstat, allocate the buffer now, so we
109*7c478bd9Sstevel@tonic-gate 	 * don't have to do it under the kstat's data lock.  (If it's a
110*7c478bd9Sstevel@tonic-gate 	 * var-size kstat, we don't know the size until after the update
111*7c478bd9Sstevel@tonic-gate 	 * routine is called, so we can't do this optimization.)
112*7c478bd9Sstevel@tonic-gate 	 * The allocator relies on this behavior to prevent recursive
113*7c478bd9Sstevel@tonic-gate 	 * mutex_enter in its (fixed-size) kstat update routine.
114*7c478bd9Sstevel@tonic-gate 	 * It's a zalloc to prevent unintentional exposure of random
115*7c478bd9Sstevel@tonic-gate 	 * juicy morsels of (old) kernel data.
116*7c478bd9Sstevel@tonic-gate 	 */
117*7c478bd9Sstevel@tonic-gate 	if (!(ksp->ks_flags & KSTAT_FLAG_VAR_SIZE)) {
118*7c478bd9Sstevel@tonic-gate 		kbufsize = ksp->ks_data_size;
119*7c478bd9Sstevel@tonic-gate 		kbuf = kmem_zalloc(kbufsize + 1, KM_NOSLEEP);
120*7c478bd9Sstevel@tonic-gate 		if (kbuf == NULL) {
121*7c478bd9Sstevel@tonic-gate 			kstat_rele(ksp);
122*7c478bd9Sstevel@tonic-gate 			return (EAGAIN);
123*7c478bd9Sstevel@tonic-gate 		}
124*7c478bd9Sstevel@tonic-gate 	}
125*7c478bd9Sstevel@tonic-gate 	KSTAT_ENTER(ksp);
126*7c478bd9Sstevel@tonic-gate 	if ((error = KSTAT_UPDATE(ksp, KSTAT_READ)) != 0) {
127*7c478bd9Sstevel@tonic-gate 		KSTAT_EXIT(ksp);
128*7c478bd9Sstevel@tonic-gate 		kstat_rele(ksp);
129*7c478bd9Sstevel@tonic-gate 		if (kbuf != NULL)
130*7c478bd9Sstevel@tonic-gate 			kmem_free(kbuf, kbufsize + 1);
131*7c478bd9Sstevel@tonic-gate 		return (error);
132*7c478bd9Sstevel@tonic-gate 	}
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate 	kbufsize = ksp->ks_data_size;
135*7c478bd9Sstevel@tonic-gate 	ubufsize = user_kstat.ks_data_size;
136*7c478bd9Sstevel@tonic-gate 
137*7c478bd9Sstevel@tonic-gate 	if (ubufsize < kbufsize) {
138*7c478bd9Sstevel@tonic-gate 		error = ENOMEM;
139*7c478bd9Sstevel@tonic-gate 	} else {
140*7c478bd9Sstevel@tonic-gate 		if (kbuf == NULL)
141*7c478bd9Sstevel@tonic-gate 			kbuf = kmem_zalloc(kbufsize + 1, KM_NOSLEEP);
142*7c478bd9Sstevel@tonic-gate 		if (kbuf == NULL) {
143*7c478bd9Sstevel@tonic-gate 			error = EAGAIN;
144*7c478bd9Sstevel@tonic-gate 		} else {
145*7c478bd9Sstevel@tonic-gate 			error = KSTAT_SNAPSHOT(ksp, kbuf, KSTAT_READ);
146*7c478bd9Sstevel@tonic-gate 		}
147*7c478bd9Sstevel@tonic-gate 	}
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate 	/*
150*7c478bd9Sstevel@tonic-gate 	 * The following info must be returned to user level,
151*7c478bd9Sstevel@tonic-gate 	 * even if the the update or snapshot failed.  This allows
152*7c478bd9Sstevel@tonic-gate 	 * kstat readers to get a handle on variable-size kstats,
153*7c478bd9Sstevel@tonic-gate 	 * detect dormant kstats, etc.
154*7c478bd9Sstevel@tonic-gate 	 */
155*7c478bd9Sstevel@tonic-gate 	user_kstat.ks_ndata	= ksp->ks_ndata;
156*7c478bd9Sstevel@tonic-gate 	user_kstat.ks_data_size	= kbufsize;
157*7c478bd9Sstevel@tonic-gate 	user_kstat.ks_flags	= ksp->ks_flags;
158*7c478bd9Sstevel@tonic-gate 	user_kstat.ks_snaptime	= ksp->ks_snaptime;
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate 	*rvalp = kstat_chain_id;
161*7c478bd9Sstevel@tonic-gate 	KSTAT_EXIT(ksp);
162*7c478bd9Sstevel@tonic-gate 	kstat_rele(ksp);
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate 	/*
165*7c478bd9Sstevel@tonic-gate 	 * Copy the buffer containing the kstat back to userland.
166*7c478bd9Sstevel@tonic-gate 	 */
167*7c478bd9Sstevel@tonic-gate 	copysize = kbufsize;
168*7c478bd9Sstevel@tonic-gate 	if (kbuf != NULL) {
169*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
170*7c478bd9Sstevel@tonic-gate 		kstat32_t *k32;
171*7c478bd9Sstevel@tonic-gate 		kstat_t *k;
172*7c478bd9Sstevel@tonic-gate #endif
173*7c478bd9Sstevel@tonic-gate 		int i;
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 		switch (model) {
176*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
177*7c478bd9Sstevel@tonic-gate 		case DDI_MODEL_ILP32:
178*7c478bd9Sstevel@tonic-gate 
179*7c478bd9Sstevel@tonic-gate 			if (ksp->ks_type == KSTAT_TYPE_NAMED) {
180*7c478bd9Sstevel@tonic-gate 				kstat_named_t *kn = kbuf;
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate 				for (i = 0; i < user_kstat.ks_ndata; kn++, i++)
183*7c478bd9Sstevel@tonic-gate 					switch (kn->data_type) {
184*7c478bd9Sstevel@tonic-gate 					/*
185*7c478bd9Sstevel@tonic-gate 					 * Named statistics have fields of type
186*7c478bd9Sstevel@tonic-gate 					 * 'long'.  For a 32-bit application
187*7c478bd9Sstevel@tonic-gate 					 * looking at a 64-bit kernel,
188*7c478bd9Sstevel@tonic-gate 					 * forcibly truncate these 64-bit
189*7c478bd9Sstevel@tonic-gate 					 * quantities to 32-bit values.
190*7c478bd9Sstevel@tonic-gate 					 */
191*7c478bd9Sstevel@tonic-gate 					case KSTAT_DATA_LONG:
192*7c478bd9Sstevel@tonic-gate 						kn->value.i32 =
193*7c478bd9Sstevel@tonic-gate 						    (int32_t)kn->value.l;
194*7c478bd9Sstevel@tonic-gate 						kn->data_type =
195*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_INT32;
196*7c478bd9Sstevel@tonic-gate 						break;
197*7c478bd9Sstevel@tonic-gate 					case KSTAT_DATA_ULONG:
198*7c478bd9Sstevel@tonic-gate 						kn->value.ui32 =
199*7c478bd9Sstevel@tonic-gate 						    (uint32_t)kn->value.ul;
200*7c478bd9Sstevel@tonic-gate 						kn->data_type =
201*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_UINT32;
202*7c478bd9Sstevel@tonic-gate 						break;
203*7c478bd9Sstevel@tonic-gate 					/*
204*7c478bd9Sstevel@tonic-gate 					 * Long strings must be massaged before
205*7c478bd9Sstevel@tonic-gate 					 * being copied out to userland.  Do
206*7c478bd9Sstevel@tonic-gate 					 * that here.
207*7c478bd9Sstevel@tonic-gate 					 */
208*7c478bd9Sstevel@tonic-gate 					case KSTAT_DATA_STRING:
209*7c478bd9Sstevel@tonic-gate 						if (KSTAT_NAMED_STR_PTR(kn)
210*7c478bd9Sstevel@tonic-gate 						    == NULL)
211*7c478bd9Sstevel@tonic-gate 							break;
212*7c478bd9Sstevel@tonic-gate 						/*
213*7c478bd9Sstevel@tonic-gate 						 * The offsets within the
214*7c478bd9Sstevel@tonic-gate 						 * buffers are the same, so add
215*7c478bd9Sstevel@tonic-gate 						 * the offset to the beginning
216*7c478bd9Sstevel@tonic-gate 						 * of the new buffer to fix the
217*7c478bd9Sstevel@tonic-gate 						 * pointer.
218*7c478bd9Sstevel@tonic-gate 						 */
219*7c478bd9Sstevel@tonic-gate 						KSTAT_NAMED_STR_PTR(kn) =
220*7c478bd9Sstevel@tonic-gate 						    (char *)user_kstat.ks_data +
221*7c478bd9Sstevel@tonic-gate 						    (KSTAT_NAMED_STR_PTR(kn) -
222*7c478bd9Sstevel@tonic-gate 						    (char *)kbuf);
223*7c478bd9Sstevel@tonic-gate 						/*
224*7c478bd9Sstevel@tonic-gate 						 * Make sure the string pointer
225*7c478bd9Sstevel@tonic-gate 						 * lies within the allocated
226*7c478bd9Sstevel@tonic-gate 						 * buffer.
227*7c478bd9Sstevel@tonic-gate 						 */
228*7c478bd9Sstevel@tonic-gate 						ASSERT(KSTAT_NAMED_STR_PTR(kn) +
229*7c478bd9Sstevel@tonic-gate 						    KSTAT_NAMED_STR_BUFLEN(kn)
230*7c478bd9Sstevel@tonic-gate 						    <=
231*7c478bd9Sstevel@tonic-gate 						    ((char *)
232*7c478bd9Sstevel@tonic-gate 						    user_kstat.ks_data +
233*7c478bd9Sstevel@tonic-gate 						    ubufsize));
234*7c478bd9Sstevel@tonic-gate 						ASSERT(KSTAT_NAMED_STR_PTR(kn)
235*7c478bd9Sstevel@tonic-gate 						    >=
236*7c478bd9Sstevel@tonic-gate 						    (char *)
237*7c478bd9Sstevel@tonic-gate 						    ((kstat_named_t *)
238*7c478bd9Sstevel@tonic-gate 						    user_kstat.ks_data +
239*7c478bd9Sstevel@tonic-gate 						    user_kstat.ks_ndata));
240*7c478bd9Sstevel@tonic-gate 						/*
241*7c478bd9Sstevel@tonic-gate 						 * Cast 64-bit ptr to 32-bit.
242*7c478bd9Sstevel@tonic-gate 						 */
243*7c478bd9Sstevel@tonic-gate 						kn->value.string.addr.ptr32 =
244*7c478bd9Sstevel@tonic-gate 						    (caddr32_t)(uintptr_t)
245*7c478bd9Sstevel@tonic-gate 						    KSTAT_NAMED_STR_PTR(kn);
246*7c478bd9Sstevel@tonic-gate 						break;
247*7c478bd9Sstevel@tonic-gate 					default:
248*7c478bd9Sstevel@tonic-gate 						break;
249*7c478bd9Sstevel@tonic-gate 					}
250*7c478bd9Sstevel@tonic-gate 			}
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 			if (user_kstat.ks_kid != 0)
253*7c478bd9Sstevel@tonic-gate 				break;
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate 			/*
256*7c478bd9Sstevel@tonic-gate 			 * This is the special case of the kstat header
257*7c478bd9Sstevel@tonic-gate 			 * list for the entire system.  Reshape the
258*7c478bd9Sstevel@tonic-gate 			 * array in place, then copy it out.
259*7c478bd9Sstevel@tonic-gate 			 */
260*7c478bd9Sstevel@tonic-gate 			k32 = kbuf;
261*7c478bd9Sstevel@tonic-gate 			k = kbuf;
262*7c478bd9Sstevel@tonic-gate 			for (i = 0; i < user_kstat.ks_ndata; k32++, k++, i++) {
263*7c478bd9Sstevel@tonic-gate 				k32->ks_crtime		= k->ks_crtime;
264*7c478bd9Sstevel@tonic-gate 				k32->ks_next		= 0;
265*7c478bd9Sstevel@tonic-gate 				k32->ks_kid		= k->ks_kid;
266*7c478bd9Sstevel@tonic-gate 				(void) strcpy(k32->ks_module, k->ks_module);
267*7c478bd9Sstevel@tonic-gate 				k32->ks_resv		= k->ks_resv;
268*7c478bd9Sstevel@tonic-gate 				k32->ks_instance	= k->ks_instance;
269*7c478bd9Sstevel@tonic-gate 				(void) strcpy(k32->ks_name, k->ks_name);
270*7c478bd9Sstevel@tonic-gate 				k32->ks_type		= k->ks_type;
271*7c478bd9Sstevel@tonic-gate 				(void) strcpy(k32->ks_class, k->ks_class);
272*7c478bd9Sstevel@tonic-gate 				k32->ks_flags		= k->ks_flags;
273*7c478bd9Sstevel@tonic-gate 				k32->ks_data		= 0;
274*7c478bd9Sstevel@tonic-gate 				k32->ks_ndata		= k->ks_ndata;
275*7c478bd9Sstevel@tonic-gate 				if (k->ks_data_size > UINT32_MAX) {
276*7c478bd9Sstevel@tonic-gate 					error = EOVERFLOW;
277*7c478bd9Sstevel@tonic-gate 					break;
278*7c478bd9Sstevel@tonic-gate 				}
279*7c478bd9Sstevel@tonic-gate 				k32->ks_data_size = (size32_t)k->ks_data_size;
280*7c478bd9Sstevel@tonic-gate 				k32->ks_snaptime	= k->ks_snaptime;
281*7c478bd9Sstevel@tonic-gate 			}
282*7c478bd9Sstevel@tonic-gate 
283*7c478bd9Sstevel@tonic-gate 			/*
284*7c478bd9Sstevel@tonic-gate 			 * XXX	In this case we copy less data than is
285*7c478bd9Sstevel@tonic-gate 			 *	claimed in the header.
286*7c478bd9Sstevel@tonic-gate 			 */
287*7c478bd9Sstevel@tonic-gate 			copysize = user_kstat.ks_ndata * sizeof (kstat32_t);
288*7c478bd9Sstevel@tonic-gate 			break;
289*7c478bd9Sstevel@tonic-gate #endif	/* _MULTI_DATAMODEL */
290*7c478bd9Sstevel@tonic-gate 		default:
291*7c478bd9Sstevel@tonic-gate 		case DDI_MODEL_NONE:
292*7c478bd9Sstevel@tonic-gate 			if (ksp->ks_type == KSTAT_TYPE_NAMED) {
293*7c478bd9Sstevel@tonic-gate 				kstat_named_t *kn = kbuf;
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate 				for (i = 0; i < user_kstat.ks_ndata; kn++, i++)
296*7c478bd9Sstevel@tonic-gate 					switch (kn->data_type) {
297*7c478bd9Sstevel@tonic-gate #ifdef _LP64
298*7c478bd9Sstevel@tonic-gate 					case KSTAT_DATA_LONG:
299*7c478bd9Sstevel@tonic-gate 						kn->data_type =
300*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_INT64;
301*7c478bd9Sstevel@tonic-gate 						break;
302*7c478bd9Sstevel@tonic-gate 					case KSTAT_DATA_ULONG:
303*7c478bd9Sstevel@tonic-gate 						kn->data_type =
304*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_UINT64;
305*7c478bd9Sstevel@tonic-gate 						break;
306*7c478bd9Sstevel@tonic-gate #endif	/* _LP64 */
307*7c478bd9Sstevel@tonic-gate 					case KSTAT_DATA_STRING:
308*7c478bd9Sstevel@tonic-gate 						if (KSTAT_NAMED_STR_PTR(kn)
309*7c478bd9Sstevel@tonic-gate 						    == NULL)
310*7c478bd9Sstevel@tonic-gate 							break;
311*7c478bd9Sstevel@tonic-gate 						KSTAT_NAMED_STR_PTR(kn) =
312*7c478bd9Sstevel@tonic-gate 						    (char *)user_kstat.ks_data +
313*7c478bd9Sstevel@tonic-gate 						    (KSTAT_NAMED_STR_PTR(kn) -
314*7c478bd9Sstevel@tonic-gate 						    (char *)kbuf);
315*7c478bd9Sstevel@tonic-gate 						ASSERT(KSTAT_NAMED_STR_PTR(kn) +
316*7c478bd9Sstevel@tonic-gate 						    KSTAT_NAMED_STR_BUFLEN(kn)
317*7c478bd9Sstevel@tonic-gate 						    <=
318*7c478bd9Sstevel@tonic-gate 						    ((char *)
319*7c478bd9Sstevel@tonic-gate 						    user_kstat.ks_data +
320*7c478bd9Sstevel@tonic-gate 						    ubufsize));
321*7c478bd9Sstevel@tonic-gate 						ASSERT(KSTAT_NAMED_STR_PTR(kn)
322*7c478bd9Sstevel@tonic-gate 						    >=
323*7c478bd9Sstevel@tonic-gate 						    (char *)
324*7c478bd9Sstevel@tonic-gate 						    ((kstat_named_t *)
325*7c478bd9Sstevel@tonic-gate 						    user_kstat.ks_data +
326*7c478bd9Sstevel@tonic-gate 						    user_kstat.ks_ndata));
327*7c478bd9Sstevel@tonic-gate 						break;
328*7c478bd9Sstevel@tonic-gate 					default:
329*7c478bd9Sstevel@tonic-gate 						break;
330*7c478bd9Sstevel@tonic-gate 					}
331*7c478bd9Sstevel@tonic-gate 			}
332*7c478bd9Sstevel@tonic-gate 			break;
333*7c478bd9Sstevel@tonic-gate 		}
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate 		if (error == 0 &&
336*7c478bd9Sstevel@tonic-gate 		    copyout(kbuf, user_kstat.ks_data, copysize))
337*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
338*7c478bd9Sstevel@tonic-gate 		kmem_free(kbuf, kbufsize + 1);
339*7c478bd9Sstevel@tonic-gate 	}
340*7c478bd9Sstevel@tonic-gate 
341*7c478bd9Sstevel@tonic-gate 	/*
342*7c478bd9Sstevel@tonic-gate 	 * We have modified the ks_ndata, ks_data_size, ks_flags, and
343*7c478bd9Sstevel@tonic-gate 	 * ks_snaptime fields of the user kstat; now copy it back to userland.
344*7c478bd9Sstevel@tonic-gate 	 */
345*7c478bd9Sstevel@tonic-gate 	switch (model) {
346*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
347*7c478bd9Sstevel@tonic-gate 	case DDI_MODEL_ILP32:
348*7c478bd9Sstevel@tonic-gate 		if (kbufsize > UINT32_MAX) {
349*7c478bd9Sstevel@tonic-gate 			error = EOVERFLOW;
350*7c478bd9Sstevel@tonic-gate 			break;
351*7c478bd9Sstevel@tonic-gate 		}
352*7c478bd9Sstevel@tonic-gate 		user_kstat32.ks_ndata		= user_kstat.ks_ndata;
353*7c478bd9Sstevel@tonic-gate 		user_kstat32.ks_data_size	= (size32_t)kbufsize;
354*7c478bd9Sstevel@tonic-gate 		user_kstat32.ks_flags		= user_kstat.ks_flags;
355*7c478bd9Sstevel@tonic-gate 		user_kstat32.ks_snaptime	= user_kstat.ks_snaptime;
356*7c478bd9Sstevel@tonic-gate 		if (copyout(&user_kstat32, user_ksp, sizeof (kstat32_t)) &&
357*7c478bd9Sstevel@tonic-gate 		    error == 0)
358*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
359*7c478bd9Sstevel@tonic-gate 		break;
360*7c478bd9Sstevel@tonic-gate #endif
361*7c478bd9Sstevel@tonic-gate 	default:
362*7c478bd9Sstevel@tonic-gate 	case DDI_MODEL_NONE:
363*7c478bd9Sstevel@tonic-gate 		if (copyout(&user_kstat, user_ksp, sizeof (kstat_t)) &&
364*7c478bd9Sstevel@tonic-gate 		    error == 0)
365*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
366*7c478bd9Sstevel@tonic-gate 		break;
367*7c478bd9Sstevel@tonic-gate 	}
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	return (error);
370*7c478bd9Sstevel@tonic-gate }
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate static int
373*7c478bd9Sstevel@tonic-gate write_kstat_data(int *rvalp, void *user_ksp, int flag, cred_t *cred)
374*7c478bd9Sstevel@tonic-gate {
375*7c478bd9Sstevel@tonic-gate 	kstat_t user_kstat, *ksp;
376*7c478bd9Sstevel@tonic-gate 	void *buf = NULL;
377*7c478bd9Sstevel@tonic-gate 	size_t bufsize;
378*7c478bd9Sstevel@tonic-gate 	int error = 0;
379*7c478bd9Sstevel@tonic-gate 
380*7c478bd9Sstevel@tonic-gate 	if (secpolicy_sys_config(cred, B_FALSE) != 0)
381*7c478bd9Sstevel@tonic-gate 		return (EPERM);
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 	switch (ddi_model_convert_from(flag & FMODELS)) {
384*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
385*7c478bd9Sstevel@tonic-gate 		kstat32_t user_kstat32;
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 	case DDI_MODEL_ILP32:
388*7c478bd9Sstevel@tonic-gate 		if (copyin(user_ksp, &user_kstat32, sizeof (kstat32_t)))
389*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
390*7c478bd9Sstevel@tonic-gate 		/*
391*7c478bd9Sstevel@tonic-gate 		 * These are the only fields we actually look at.
392*7c478bd9Sstevel@tonic-gate 		 */
393*7c478bd9Sstevel@tonic-gate 		user_kstat.ks_kid = user_kstat32.ks_kid;
394*7c478bd9Sstevel@tonic-gate 		user_kstat.ks_data = (void *)(uintptr_t)user_kstat32.ks_data;
395*7c478bd9Sstevel@tonic-gate 		user_kstat.ks_data_size = (size_t)user_kstat32.ks_data_size;
396*7c478bd9Sstevel@tonic-gate 		user_kstat.ks_ndata = user_kstat32.ks_ndata;
397*7c478bd9Sstevel@tonic-gate 		break;
398*7c478bd9Sstevel@tonic-gate #endif
399*7c478bd9Sstevel@tonic-gate 	default:
400*7c478bd9Sstevel@tonic-gate 	case DDI_MODEL_NONE:
401*7c478bd9Sstevel@tonic-gate 		if (copyin(user_ksp, &user_kstat, sizeof (kstat_t)))
402*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
403*7c478bd9Sstevel@tonic-gate 	}
404*7c478bd9Sstevel@tonic-gate 
405*7c478bd9Sstevel@tonic-gate 	bufsize = user_kstat.ks_data_size;
406*7c478bd9Sstevel@tonic-gate 	buf = kmem_alloc(bufsize + 1, KM_NOSLEEP);
407*7c478bd9Sstevel@tonic-gate 	if (buf == NULL)
408*7c478bd9Sstevel@tonic-gate 		return (EAGAIN);
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate 	if (copyin(user_kstat.ks_data, buf, bufsize)) {
411*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, bufsize + 1);
412*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
413*7c478bd9Sstevel@tonic-gate 	}
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 	ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid());
416*7c478bd9Sstevel@tonic-gate 	if (ksp == NULL) {
417*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, bufsize + 1);
418*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
419*7c478bd9Sstevel@tonic-gate 	}
420*7c478bd9Sstevel@tonic-gate 	if (ksp->ks_flags & KSTAT_FLAG_INVALID) {
421*7c478bd9Sstevel@tonic-gate 		kstat_rele(ksp);
422*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, bufsize + 1);
423*7c478bd9Sstevel@tonic-gate 		return (EAGAIN);
424*7c478bd9Sstevel@tonic-gate 	}
425*7c478bd9Sstevel@tonic-gate 	if (!(ksp->ks_flags & KSTAT_FLAG_WRITABLE)) {
426*7c478bd9Sstevel@tonic-gate 		kstat_rele(ksp);
427*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, bufsize + 1);
428*7c478bd9Sstevel@tonic-gate 		return (EACCES);
429*7c478bd9Sstevel@tonic-gate 	}
430*7c478bd9Sstevel@tonic-gate 
431*7c478bd9Sstevel@tonic-gate 	/*
432*7c478bd9Sstevel@tonic-gate 	 * With KSTAT_FLAG_VARIABLE, one must call the kstat's update callback
433*7c478bd9Sstevel@tonic-gate 	 * routine to ensure ks_data_size is up to date.
434*7c478bd9Sstevel@tonic-gate 	 * In this case it makes sense to do it anyhow, as it will be shortly
435*7c478bd9Sstevel@tonic-gate 	 * followed by a KSTAT_SNAPSHOT().
436*7c478bd9Sstevel@tonic-gate 	 */
437*7c478bd9Sstevel@tonic-gate 	KSTAT_ENTER(ksp);
438*7c478bd9Sstevel@tonic-gate 	error = KSTAT_UPDATE(ksp, KSTAT_READ);
439*7c478bd9Sstevel@tonic-gate 	if (error || user_kstat.ks_data_size != ksp->ks_data_size ||
440*7c478bd9Sstevel@tonic-gate 	    user_kstat.ks_ndata != ksp->ks_ndata) {
441*7c478bd9Sstevel@tonic-gate 		KSTAT_EXIT(ksp);
442*7c478bd9Sstevel@tonic-gate 		kstat_rele(ksp);
443*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, bufsize + 1);
444*7c478bd9Sstevel@tonic-gate 		return (error ? error : EINVAL);
445*7c478bd9Sstevel@tonic-gate 	}
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 	/*
448*7c478bd9Sstevel@tonic-gate 	 * We have to ensure that we don't accidentally change the type of
449*7c478bd9Sstevel@tonic-gate 	 * existing kstat_named statistics when writing over them.
450*7c478bd9Sstevel@tonic-gate 	 * Since read_kstat_data() modifies some of the types on their way
451*7c478bd9Sstevel@tonic-gate 	 * out, we need to be sure to handle these types seperately.
452*7c478bd9Sstevel@tonic-gate 	 */
453*7c478bd9Sstevel@tonic-gate 	if (ksp->ks_type == KSTAT_TYPE_NAMED) {
454*7c478bd9Sstevel@tonic-gate 		void *kbuf;
455*7c478bd9Sstevel@tonic-gate 		kstat_named_t *kold;
456*7c478bd9Sstevel@tonic-gate 		kstat_named_t *knew = buf;
457*7c478bd9Sstevel@tonic-gate 		int i;
458*7c478bd9Sstevel@tonic-gate 
459*7c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
460*7c478bd9Sstevel@tonic-gate 		int model = ddi_model_convert_from(flag & FMODELS);
461*7c478bd9Sstevel@tonic-gate #endif
462*7c478bd9Sstevel@tonic-gate 
463*7c478bd9Sstevel@tonic-gate 		/*
464*7c478bd9Sstevel@tonic-gate 		 * Since ksp->ks_data may be NULL, we need to take a snapshot
465*7c478bd9Sstevel@tonic-gate 		 * of the published data to look at the types.
466*7c478bd9Sstevel@tonic-gate 		 */
467*7c478bd9Sstevel@tonic-gate 		kbuf = kmem_alloc(bufsize + 1, KM_NOSLEEP);
468*7c478bd9Sstevel@tonic-gate 		if (kbuf == NULL) {
469*7c478bd9Sstevel@tonic-gate 			KSTAT_EXIT(ksp);
470*7c478bd9Sstevel@tonic-gate 			kstat_rele(ksp);
471*7c478bd9Sstevel@tonic-gate 			kmem_free(buf, bufsize + 1);
472*7c478bd9Sstevel@tonic-gate 			return (EAGAIN);
473*7c478bd9Sstevel@tonic-gate 		}
474*7c478bd9Sstevel@tonic-gate 		error = KSTAT_SNAPSHOT(ksp, kbuf, KSTAT_READ);
475*7c478bd9Sstevel@tonic-gate 		if (error) {
476*7c478bd9Sstevel@tonic-gate 			KSTAT_EXIT(ksp);
477*7c478bd9Sstevel@tonic-gate 			kstat_rele(ksp);
478*7c478bd9Sstevel@tonic-gate 			kmem_free(kbuf, bufsize + 1);
479*7c478bd9Sstevel@tonic-gate 			kmem_free(buf, bufsize + 1);
480*7c478bd9Sstevel@tonic-gate 			return (error);
481*7c478bd9Sstevel@tonic-gate 		}
482*7c478bd9Sstevel@tonic-gate 		kold = kbuf;
483*7c478bd9Sstevel@tonic-gate 
484*7c478bd9Sstevel@tonic-gate 		/*
485*7c478bd9Sstevel@tonic-gate 		 * read_kstat_data() changes the types of
486*7c478bd9Sstevel@tonic-gate 		 * KSTAT_DATA_LONG / KSTAT_DATA_ULONG, so we need to
487*7c478bd9Sstevel@tonic-gate 		 * make sure that these (modified) types are considered
488*7c478bd9Sstevel@tonic-gate 		 * valid.
489*7c478bd9Sstevel@tonic-gate 		 */
490*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++) {
491*7c478bd9Sstevel@tonic-gate 			switch (kold->data_type) {
492*7c478bd9Sstevel@tonic-gate #ifdef	_MULTI_DATAMODEL
493*7c478bd9Sstevel@tonic-gate 			case KSTAT_DATA_LONG:
494*7c478bd9Sstevel@tonic-gate 				switch (model) {
495*7c478bd9Sstevel@tonic-gate 				case DDI_MODEL_ILP32:
496*7c478bd9Sstevel@tonic-gate 					if (knew->data_type ==
497*7c478bd9Sstevel@tonic-gate 					    KSTAT_DATA_INT32) {
498*7c478bd9Sstevel@tonic-gate 						knew->value.l =
499*7c478bd9Sstevel@tonic-gate 						    (long)knew->value.i32;
500*7c478bd9Sstevel@tonic-gate 						knew->data_type =
501*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_LONG;
502*7c478bd9Sstevel@tonic-gate 					}
503*7c478bd9Sstevel@tonic-gate 					break;
504*7c478bd9Sstevel@tonic-gate 				default:
505*7c478bd9Sstevel@tonic-gate 				case DDI_MODEL_NONE:
506*7c478bd9Sstevel@tonic-gate #ifdef _LP64
507*7c478bd9Sstevel@tonic-gate 					if (knew->data_type ==
508*7c478bd9Sstevel@tonic-gate 					    KSTAT_DATA_INT64) {
509*7c478bd9Sstevel@tonic-gate 						knew->value.l =
510*7c478bd9Sstevel@tonic-gate 						    (long)knew->value.i64;
511*7c478bd9Sstevel@tonic-gate 						knew->data_type =
512*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_LONG;
513*7c478bd9Sstevel@tonic-gate 					}
514*7c478bd9Sstevel@tonic-gate #endif /* _LP64 */
515*7c478bd9Sstevel@tonic-gate 					break;
516*7c478bd9Sstevel@tonic-gate 				}
517*7c478bd9Sstevel@tonic-gate 				break;
518*7c478bd9Sstevel@tonic-gate 			case KSTAT_DATA_ULONG:
519*7c478bd9Sstevel@tonic-gate 				switch (model) {
520*7c478bd9Sstevel@tonic-gate 				case DDI_MODEL_ILP32:
521*7c478bd9Sstevel@tonic-gate 					if (knew->data_type ==
522*7c478bd9Sstevel@tonic-gate 					    KSTAT_DATA_UINT32) {
523*7c478bd9Sstevel@tonic-gate 						knew->value.ul =
524*7c478bd9Sstevel@tonic-gate 						    (ulong_t)knew->value.ui32;
525*7c478bd9Sstevel@tonic-gate 						knew->data_type =
526*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_ULONG;
527*7c478bd9Sstevel@tonic-gate 					}
528*7c478bd9Sstevel@tonic-gate 					break;
529*7c478bd9Sstevel@tonic-gate 				default:
530*7c478bd9Sstevel@tonic-gate 				case DDI_MODEL_NONE:
531*7c478bd9Sstevel@tonic-gate #ifdef _LP64
532*7c478bd9Sstevel@tonic-gate 					if (knew->data_type ==
533*7c478bd9Sstevel@tonic-gate 					    KSTAT_DATA_UINT64) {
534*7c478bd9Sstevel@tonic-gate 						knew->value.ul =
535*7c478bd9Sstevel@tonic-gate 						    (ulong_t)knew->value.ui64;
536*7c478bd9Sstevel@tonic-gate 						knew->data_type =
537*7c478bd9Sstevel@tonic-gate 						    KSTAT_DATA_ULONG;
538*7c478bd9Sstevel@tonic-gate 					}
539*7c478bd9Sstevel@tonic-gate #endif /* _LP64 */
540*7c478bd9Sstevel@tonic-gate 					break;
541*7c478bd9Sstevel@tonic-gate 				}
542*7c478bd9Sstevel@tonic-gate 				break;
543*7c478bd9Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
544*7c478bd9Sstevel@tonic-gate 			case KSTAT_DATA_STRING:
545*7c478bd9Sstevel@tonic-gate 				if (knew->data_type != KSTAT_DATA_STRING) {
546*7c478bd9Sstevel@tonic-gate 					KSTAT_EXIT(ksp);
547*7c478bd9Sstevel@tonic-gate 					kstat_rele(ksp);
548*7c478bd9Sstevel@tonic-gate 					kmem_free(kbuf, bufsize + 1);
549*7c478bd9Sstevel@tonic-gate 					kmem_free(buf, bufsize + 1);
550*7c478bd9Sstevel@tonic-gate 					return (EINVAL);
551*7c478bd9Sstevel@tonic-gate 				}
552*7c478bd9Sstevel@tonic-gate 
553*7c478bd9Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
554*7c478bd9Sstevel@tonic-gate 				if (model == DDI_MODEL_ILP32)
555*7c478bd9Sstevel@tonic-gate 					KSTAT_NAMED_STR_PTR(knew) =
556*7c478bd9Sstevel@tonic-gate 					    (char *)(uintptr_t)
557*7c478bd9Sstevel@tonic-gate 					    knew->value.string.addr.ptr32;
558*7c478bd9Sstevel@tonic-gate #endif
559*7c478bd9Sstevel@tonic-gate 				/*
560*7c478bd9Sstevel@tonic-gate 				 * Nothing special for NULL
561*7c478bd9Sstevel@tonic-gate 				 */
562*7c478bd9Sstevel@tonic-gate 				if (KSTAT_NAMED_STR_PTR(knew) == NULL)
563*7c478bd9Sstevel@tonic-gate 					break;
564*7c478bd9Sstevel@tonic-gate 
565*7c478bd9Sstevel@tonic-gate 				/*
566*7c478bd9Sstevel@tonic-gate 				 * Check to see that the pointers all point
567*7c478bd9Sstevel@tonic-gate 				 * to within the buffer and after the array
568*7c478bd9Sstevel@tonic-gate 				 * of kstat_named_t's.
569*7c478bd9Sstevel@tonic-gate 				 */
570*7c478bd9Sstevel@tonic-gate 				if (KSTAT_NAMED_STR_PTR(knew) <
571*7c478bd9Sstevel@tonic-gate 				    (char *)
572*7c478bd9Sstevel@tonic-gate 				    ((kstat_named_t *)user_kstat.ks_data +
573*7c478bd9Sstevel@tonic-gate 				    ksp->ks_ndata)) {
574*7c478bd9Sstevel@tonic-gate 					KSTAT_EXIT(ksp);
575*7c478bd9Sstevel@tonic-gate 					kstat_rele(ksp);
576*7c478bd9Sstevel@tonic-gate 					kmem_free(kbuf, bufsize + 1);
577*7c478bd9Sstevel@tonic-gate 					kmem_free(buf, bufsize + 1);
578*7c478bd9Sstevel@tonic-gate 					return (EINVAL);
579*7c478bd9Sstevel@tonic-gate 				}
580*7c478bd9Sstevel@tonic-gate 				if (KSTAT_NAMED_STR_PTR(knew) +
581*7c478bd9Sstevel@tonic-gate 				    KSTAT_NAMED_STR_BUFLEN(knew) >
582*7c478bd9Sstevel@tonic-gate 				    ((char *)user_kstat.ks_data +
583*7c478bd9Sstevel@tonic-gate 				    ksp->ks_data_size)) {
584*7c478bd9Sstevel@tonic-gate 					KSTAT_EXIT(ksp);
585*7c478bd9Sstevel@tonic-gate 					kstat_rele(ksp);
586*7c478bd9Sstevel@tonic-gate 					kmem_free(kbuf, bufsize + 1);
587*7c478bd9Sstevel@tonic-gate 					kmem_free(buf, bufsize + 1);
588*7c478bd9Sstevel@tonic-gate 					return (EINVAL);
589*7c478bd9Sstevel@tonic-gate 				}
590*7c478bd9Sstevel@tonic-gate 
591*7c478bd9Sstevel@tonic-gate 				/*
592*7c478bd9Sstevel@tonic-gate 				 * Update the pointers within the buffer
593*7c478bd9Sstevel@tonic-gate 				 */
594*7c478bd9Sstevel@tonic-gate 				KSTAT_NAMED_STR_PTR(knew) =
595*7c478bd9Sstevel@tonic-gate 				    (char *)buf +
596*7c478bd9Sstevel@tonic-gate 				    (KSTAT_NAMED_STR_PTR(knew) -
597*7c478bd9Sstevel@tonic-gate 				    (char *)user_kstat.ks_data);
598*7c478bd9Sstevel@tonic-gate 				break;
599*7c478bd9Sstevel@tonic-gate 			default:
600*7c478bd9Sstevel@tonic-gate 				break;
601*7c478bd9Sstevel@tonic-gate 			}
602*7c478bd9Sstevel@tonic-gate 		}
603*7c478bd9Sstevel@tonic-gate 
604*7c478bd9Sstevel@tonic-gate 		kold = kbuf;
605*7c478bd9Sstevel@tonic-gate 		knew = buf;
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate 		/*
608*7c478bd9Sstevel@tonic-gate 		 * Now make sure the types are what we expected them to be.
609*7c478bd9Sstevel@tonic-gate 		 */
610*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++)
611*7c478bd9Sstevel@tonic-gate 			if (kold->data_type != knew->data_type) {
612*7c478bd9Sstevel@tonic-gate 				KSTAT_EXIT(ksp);
613*7c478bd9Sstevel@tonic-gate 				kstat_rele(ksp);
614*7c478bd9Sstevel@tonic-gate 				kmem_free(kbuf, bufsize + 1);
615*7c478bd9Sstevel@tonic-gate 				kmem_free(buf, bufsize + 1);
616*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
617*7c478bd9Sstevel@tonic-gate 			}
618*7c478bd9Sstevel@tonic-gate 
619*7c478bd9Sstevel@tonic-gate 		kmem_free(kbuf, bufsize + 1);
620*7c478bd9Sstevel@tonic-gate 	}
621*7c478bd9Sstevel@tonic-gate 
622*7c478bd9Sstevel@tonic-gate 	error = KSTAT_SNAPSHOT(ksp, buf, KSTAT_WRITE);
623*7c478bd9Sstevel@tonic-gate 	if (!error)
624*7c478bd9Sstevel@tonic-gate 		error = KSTAT_UPDATE(ksp, KSTAT_WRITE);
625*7c478bd9Sstevel@tonic-gate 	*rvalp = kstat_chain_id;
626*7c478bd9Sstevel@tonic-gate 	KSTAT_EXIT(ksp);
627*7c478bd9Sstevel@tonic-gate 	kstat_rele(ksp);
628*7c478bd9Sstevel@tonic-gate 	kmem_free(buf, bufsize + 1);
629*7c478bd9Sstevel@tonic-gate 	return (error);
630*7c478bd9Sstevel@tonic-gate }
631*7c478bd9Sstevel@tonic-gate 
632*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
633*7c478bd9Sstevel@tonic-gate static int
634*7c478bd9Sstevel@tonic-gate kstat_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr, int *rvalp)
635*7c478bd9Sstevel@tonic-gate {
636*7c478bd9Sstevel@tonic-gate 	int rc = 0;
637*7c478bd9Sstevel@tonic-gate 
638*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
639*7c478bd9Sstevel@tonic-gate 
640*7c478bd9Sstevel@tonic-gate 	case KSTAT_IOC_CHAIN_ID:
641*7c478bd9Sstevel@tonic-gate 		*rvalp = kstat_chain_id;
642*7c478bd9Sstevel@tonic-gate 		break;
643*7c478bd9Sstevel@tonic-gate 
644*7c478bd9Sstevel@tonic-gate 	case KSTAT_IOC_READ:
645*7c478bd9Sstevel@tonic-gate 		rc = read_kstat_data(rvalp, (void *)data, flag);
646*7c478bd9Sstevel@tonic-gate 		break;
647*7c478bd9Sstevel@tonic-gate 
648*7c478bd9Sstevel@tonic-gate 	case KSTAT_IOC_WRITE:
649*7c478bd9Sstevel@tonic-gate 		rc = write_kstat_data(rvalp, (void *)data, flag, cr);
650*7c478bd9Sstevel@tonic-gate 		break;
651*7c478bd9Sstevel@tonic-gate 
652*7c478bd9Sstevel@tonic-gate 	default:
653*7c478bd9Sstevel@tonic-gate 		/* invalid request */
654*7c478bd9Sstevel@tonic-gate 		rc = EINVAL;
655*7c478bd9Sstevel@tonic-gate 	}
656*7c478bd9Sstevel@tonic-gate 	return (rc);
657*7c478bd9Sstevel@tonic-gate }
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
660*7c478bd9Sstevel@tonic-gate static int
661*7c478bd9Sstevel@tonic-gate kstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
662*7c478bd9Sstevel@tonic-gate 	void **result)
663*7c478bd9Sstevel@tonic-gate {
664*7c478bd9Sstevel@tonic-gate 	switch (infocmd) {
665*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
666*7c478bd9Sstevel@tonic-gate 		*result = kstat_devi;
667*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
668*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
669*7c478bd9Sstevel@tonic-gate 		*result = NULL;
670*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
671*7c478bd9Sstevel@tonic-gate 	}
672*7c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
673*7c478bd9Sstevel@tonic-gate }
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate static int
676*7c478bd9Sstevel@tonic-gate kstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
677*7c478bd9Sstevel@tonic-gate {
678*7c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
679*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
680*7c478bd9Sstevel@tonic-gate 
681*7c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "kstat", S_IFCHR,
682*7c478bd9Sstevel@tonic-gate 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
683*7c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
684*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
685*7c478bd9Sstevel@tonic-gate 	}
686*7c478bd9Sstevel@tonic-gate 	kstat_devi = devi;
687*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
688*7c478bd9Sstevel@tonic-gate }
689*7c478bd9Sstevel@tonic-gate 
690*7c478bd9Sstevel@tonic-gate static int
691*7c478bd9Sstevel@tonic-gate kstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
692*7c478bd9Sstevel@tonic-gate {
693*7c478bd9Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
694*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
695*7c478bd9Sstevel@tonic-gate 
696*7c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
697*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
698*7c478bd9Sstevel@tonic-gate }
699*7c478bd9Sstevel@tonic-gate 
700*7c478bd9Sstevel@tonic-gate static struct cb_ops kstat_cb_ops = {
701*7c478bd9Sstevel@tonic-gate 	nulldev,		/* open */
702*7c478bd9Sstevel@tonic-gate 	nulldev,		/* close */
703*7c478bd9Sstevel@tonic-gate 	nodev,			/* strategy */
704*7c478bd9Sstevel@tonic-gate 	nodev,			/* print */
705*7c478bd9Sstevel@tonic-gate 	nodev,			/* dump */
706*7c478bd9Sstevel@tonic-gate 	nodev,			/* read */
707*7c478bd9Sstevel@tonic-gate 	nodev,			/* write */
708*7c478bd9Sstevel@tonic-gate 	kstat_ioctl,		/* ioctl */
709*7c478bd9Sstevel@tonic-gate 	nodev,			/* devmap */
710*7c478bd9Sstevel@tonic-gate 	nodev,			/* mmap */
711*7c478bd9Sstevel@tonic-gate 	nodev,			/* segmap */
712*7c478bd9Sstevel@tonic-gate 	nochpoll,		/* poll */
713*7c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* prop_op */
714*7c478bd9Sstevel@tonic-gate 	0,			/* streamtab  */
715*7c478bd9Sstevel@tonic-gate 	D_NEW | D_MP		/* Driver compatibility flag */
716*7c478bd9Sstevel@tonic-gate };
717*7c478bd9Sstevel@tonic-gate 
718*7c478bd9Sstevel@tonic-gate static struct dev_ops kstat_ops = {
719*7c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
720*7c478bd9Sstevel@tonic-gate 	0,			/* refcnt  */
721*7c478bd9Sstevel@tonic-gate 	kstat_info,		/* get_dev_info */
722*7c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
723*7c478bd9Sstevel@tonic-gate 	nulldev,		/* probe */
724*7c478bd9Sstevel@tonic-gate 	kstat_attach,		/* attach */
725*7c478bd9Sstevel@tonic-gate 	kstat_detach,		/* detach */
726*7c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
727*7c478bd9Sstevel@tonic-gate 	&kstat_cb_ops,		/* driver operations */
728*7c478bd9Sstevel@tonic-gate 	(struct bus_ops *)0	/* no bus operations */
729*7c478bd9Sstevel@tonic-gate };
730*7c478bd9Sstevel@tonic-gate 
731*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
732*7c478bd9Sstevel@tonic-gate 	&mod_driverops, "kernel statistics driver %I%", &kstat_ops,
733*7c478bd9Sstevel@tonic-gate };
734*7c478bd9Sstevel@tonic-gate 
735*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
736*7c478bd9Sstevel@tonic-gate 	MODREV_1, &modldrv, NULL
737*7c478bd9Sstevel@tonic-gate };
738*7c478bd9Sstevel@tonic-gate 
739*7c478bd9Sstevel@tonic-gate int
740*7c478bd9Sstevel@tonic-gate _init(void)
741*7c478bd9Sstevel@tonic-gate {
742*7c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
743*7c478bd9Sstevel@tonic-gate }
744*7c478bd9Sstevel@tonic-gate 
745*7c478bd9Sstevel@tonic-gate int
746*7c478bd9Sstevel@tonic-gate _fini(void)
747*7c478bd9Sstevel@tonic-gate {
748*7c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
749*7c478bd9Sstevel@tonic-gate }
750*7c478bd9Sstevel@tonic-gate 
751*7c478bd9Sstevel@tonic-gate int
752*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
753*7c478bd9Sstevel@tonic-gate {
754*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
755*7c478bd9Sstevel@tonic-gate }
756