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