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