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