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