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