Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / kstat.c
blob722f112d3beca1226ef8cf48b8a2c6221159acc6
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
29 * kernel statistics driver
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>
59 static dev_info_t *kstat_devi;
61 static int
62 read_kstat_data(int *rvalp, void *user_ksp, int flag)
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;
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);
89 ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid());
90 if (ksp == NULL) {
92 * There is no kstat with the specified KID
94 return (ENXIO);
96 if (ksp->ks_flags & KSTAT_FLAG_INVALID) {
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.
102 kstat_rele(ksp);
103 return (EAGAIN);
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 or one with long strings, we don't know the size
110 * until after the update routine is called, so we can't do this
111 * 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.
117 if (!(ksp->ks_flags & (KSTAT_FLAG_VAR_SIZE | KSTAT_FLAG_LONGSTRINGS))) {
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);
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);
134 kbufsize = ksp->ks_data_size;
135 ubufsize = user_kstat.ks_data_size;
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);
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.
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;
160 *rvalp = kstat_chain_id;
161 KSTAT_EXIT(ksp);
162 kstat_rele(ksp);
164 if (kbuf == NULL)
165 goto out;
168 * Copy the buffer containing the kstat back to userland.
170 copysize = kbufsize;
172 switch (model) {
173 int i;
174 #ifdef _MULTI_DATAMODEL
175 kstat32_t *k32;
176 kstat_t *k;
178 case DDI_MODEL_ILP32:
180 if (ksp->ks_type == KSTAT_TYPE_NAMED) {
181 kstat_named_t *kn = kbuf;
182 char *strbuf = (char *)((kstat_named_t *)kn +
183 ksp->ks_ndata);
185 for (i = 0; i < user_kstat.ks_ndata; kn++, i++)
186 switch (kn->data_type) {
188 * Named statistics have fields of type 'long'.
189 * For a 32-bit application looking at a 64-bit
190 * kernel, forcibly truncate these 64-bit
191 * quantities to 32-bit values.
193 case KSTAT_DATA_LONG:
194 kn->value.i32 = (int32_t)kn->value.l;
195 kn->data_type = KSTAT_DATA_INT32;
196 break;
197 case KSTAT_DATA_ULONG:
198 kn->value.ui32 = (uint32_t)kn->value.ul;
199 kn->data_type = KSTAT_DATA_UINT32;
200 break;
202 * Long strings must be massaged before being
203 * copied out to userland. Do that here.
205 case KSTAT_DATA_STRING:
206 if (KSTAT_NAMED_STR_PTR(kn) == NULL)
207 break;
209 * If the string lies outside of kbuf
210 * copy it there and update the pointer.
212 if (KSTAT_NAMED_STR_PTR(kn) <
213 (char *)kbuf ||
214 KSTAT_NAMED_STR_PTR(kn) +
215 KSTAT_NAMED_STR_BUFLEN(kn) >
216 (char *)kbuf + kbufsize + 1) {
217 bcopy(KSTAT_NAMED_STR_PTR(kn),
218 strbuf,
219 KSTAT_NAMED_STR_BUFLEN(kn));
221 KSTAT_NAMED_STR_PTR(kn) =
222 strbuf;
223 strbuf +=
224 KSTAT_NAMED_STR_BUFLEN(kn);
225 ASSERT(strbuf <=
226 (char *)kbuf +
227 kbufsize + 1);
230 * The offsets within the buffers are
231 * the same, so add the offset to the
232 * beginning of the new buffer to fix
233 * the pointer.
235 KSTAT_NAMED_STR_PTR(kn) =
236 (char *)user_kstat.ks_data +
237 (KSTAT_NAMED_STR_PTR(kn) -
238 (char *)kbuf);
240 * Make sure the string pointer lies
241 * within the allocated buffer.
243 ASSERT(KSTAT_NAMED_STR_PTR(kn) +
244 KSTAT_NAMED_STR_BUFLEN(kn) <=
245 ((char *)user_kstat.ks_data +
246 ubufsize));
247 ASSERT(KSTAT_NAMED_STR_PTR(kn) >=
248 (char *)((kstat_named_t *)
249 user_kstat.ks_data +
250 user_kstat.ks_ndata));
252 * Cast 64-bit ptr to 32-bit.
254 kn->value.str.addr.ptr32 =
255 (caddr32_t)(uintptr_t)
256 KSTAT_NAMED_STR_PTR(kn);
257 break;
258 default:
259 break;
263 if (user_kstat.ks_kid != 0)
264 break;
267 * This is the special case of the kstat header
268 * list for the entire system. Reshape the
269 * array in place, then copy it out.
271 k32 = kbuf;
272 k = kbuf;
273 for (i = 0; i < user_kstat.ks_ndata; k32++, k++, i++) {
274 k32->ks_crtime = k->ks_crtime;
275 k32->ks_next = 0;
276 k32->ks_kid = k->ks_kid;
277 (void) strcpy(k32->ks_module, k->ks_module);
278 k32->ks_resv = k->ks_resv;
279 k32->ks_instance = k->ks_instance;
280 (void) strcpy(k32->ks_name, k->ks_name);
281 k32->ks_type = k->ks_type;
282 (void) strcpy(k32->ks_class, k->ks_class);
283 k32->ks_flags = k->ks_flags;
284 k32->ks_data = 0;
285 k32->ks_ndata = k->ks_ndata;
286 if (k->ks_data_size > UINT32_MAX) {
287 error = EOVERFLOW;
288 break;
290 k32->ks_data_size = (size32_t)k->ks_data_size;
291 k32->ks_snaptime = k->ks_snaptime;
295 * XXX In this case we copy less data than is
296 * claimed in the header.
298 copysize = user_kstat.ks_ndata * sizeof (kstat32_t);
299 break;
300 #endif /* _MULTI_DATAMODEL */
301 default:
302 case DDI_MODEL_NONE:
303 if (ksp->ks_type == KSTAT_TYPE_NAMED) {
304 kstat_named_t *kn = kbuf;
305 char *strbuf = (char *)((kstat_named_t *)kn +
306 ksp->ks_ndata);
308 for (i = 0; i < user_kstat.ks_ndata; kn++, i++)
309 switch (kn->data_type) {
310 #ifdef _LP64
311 case KSTAT_DATA_LONG:
312 kn->data_type =
313 KSTAT_DATA_INT64;
314 break;
315 case KSTAT_DATA_ULONG:
316 kn->data_type =
317 KSTAT_DATA_UINT64;
318 break;
319 #endif /* _LP64 */
320 case KSTAT_DATA_STRING:
321 if (KSTAT_NAMED_STR_PTR(kn) == NULL)
322 break;
324 * If the string lies outside of kbuf
325 * copy it there and update the pointer.
327 if (KSTAT_NAMED_STR_PTR(kn) <
328 (char *)kbuf ||
329 KSTAT_NAMED_STR_PTR(kn) +
330 KSTAT_NAMED_STR_BUFLEN(kn) >
331 (char *)kbuf + kbufsize + 1) {
332 bcopy(KSTAT_NAMED_STR_PTR(kn),
333 strbuf,
334 KSTAT_NAMED_STR_BUFLEN(kn));
336 KSTAT_NAMED_STR_PTR(kn) =
337 strbuf;
338 strbuf +=
339 KSTAT_NAMED_STR_BUFLEN(kn);
340 ASSERT(strbuf <=
341 (char *)kbuf +
342 kbufsize + 1);
345 KSTAT_NAMED_STR_PTR(kn) =
346 (char *)user_kstat.ks_data +
347 (KSTAT_NAMED_STR_PTR(kn) -
348 (char *)kbuf);
349 ASSERT(KSTAT_NAMED_STR_PTR(kn) +
350 KSTAT_NAMED_STR_BUFLEN(kn) <=
351 ((char *)user_kstat.ks_data +
352 ubufsize));
353 ASSERT(KSTAT_NAMED_STR_PTR(kn) >=
354 (char *)((kstat_named_t *)
355 user_kstat.ks_data +
356 user_kstat.ks_ndata));
357 break;
358 default:
359 break;
362 break;
365 if (error == 0 &&
366 copyout(kbuf, user_kstat.ks_data, copysize))
367 error = EFAULT;
368 kmem_free(kbuf, kbufsize + 1);
370 out:
372 * We have modified the ks_ndata, ks_data_size, ks_flags, and
373 * ks_snaptime fields of the user kstat; now copy it back to userland.
375 switch (model) {
376 #ifdef _MULTI_DATAMODEL
377 case DDI_MODEL_ILP32:
378 if (kbufsize > UINT32_MAX) {
379 error = EOVERFLOW;
380 break;
382 user_kstat32.ks_ndata = user_kstat.ks_ndata;
383 user_kstat32.ks_data_size = (size32_t)kbufsize;
384 user_kstat32.ks_flags = user_kstat.ks_flags;
385 user_kstat32.ks_snaptime = user_kstat.ks_snaptime;
386 if (copyout(&user_kstat32, user_ksp, sizeof (kstat32_t)) &&
387 error == 0)
388 error = EFAULT;
389 break;
390 #endif
391 default:
392 case DDI_MODEL_NONE:
393 if (copyout(&user_kstat, user_ksp, sizeof (kstat_t)) &&
394 error == 0)
395 error = EFAULT;
396 break;
399 return (error);
402 static int
403 write_kstat_data(int *rvalp, void *user_ksp, int flag, cred_t *cred)
405 kstat_t user_kstat, *ksp;
406 void *buf = NULL;
407 size_t bufsize;
408 int error = 0;
410 if (secpolicy_sys_config(cred, B_FALSE) != 0)
411 return (EPERM);
413 switch (ddi_model_convert_from(flag & FMODELS)) {
414 #ifdef _MULTI_DATAMODEL
415 kstat32_t user_kstat32;
417 case DDI_MODEL_ILP32:
418 if (copyin(user_ksp, &user_kstat32, sizeof (kstat32_t)))
419 return (EFAULT);
421 * These are the only fields we actually look at.
423 user_kstat.ks_kid = user_kstat32.ks_kid;
424 user_kstat.ks_data = (void *)(uintptr_t)user_kstat32.ks_data;
425 user_kstat.ks_data_size = (size_t)user_kstat32.ks_data_size;
426 user_kstat.ks_ndata = user_kstat32.ks_ndata;
427 break;
428 #endif
429 default:
430 case DDI_MODEL_NONE:
431 if (copyin(user_ksp, &user_kstat, sizeof (kstat_t)))
432 return (EFAULT);
435 bufsize = user_kstat.ks_data_size;
436 buf = kmem_alloc(bufsize + 1, KM_NOSLEEP);
437 if (buf == NULL)
438 return (EAGAIN);
440 if (copyin(user_kstat.ks_data, buf, bufsize)) {
441 kmem_free(buf, bufsize + 1);
442 return (EFAULT);
445 ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid());
446 if (ksp == NULL) {
447 kmem_free(buf, bufsize + 1);
448 return (ENXIO);
450 if (ksp->ks_flags & KSTAT_FLAG_INVALID) {
451 kstat_rele(ksp);
452 kmem_free(buf, bufsize + 1);
453 return (EAGAIN);
455 if (!(ksp->ks_flags & KSTAT_FLAG_WRITABLE)) {
456 kstat_rele(ksp);
457 kmem_free(buf, bufsize + 1);
458 return (EACCES);
462 * With KSTAT_FLAG_VAR_SIZE, one must call the kstat's update callback
463 * routine to ensure ks_data_size is up to date.
464 * In this case it makes sense to do it anyhow, as it will be shortly
465 * followed by a KSTAT_SNAPSHOT().
467 KSTAT_ENTER(ksp);
468 error = KSTAT_UPDATE(ksp, KSTAT_READ);
469 if (error || user_kstat.ks_data_size != ksp->ks_data_size ||
470 user_kstat.ks_ndata != ksp->ks_ndata) {
471 KSTAT_EXIT(ksp);
472 kstat_rele(ksp);
473 kmem_free(buf, bufsize + 1);
474 return (error ? error : EINVAL);
478 * We have to ensure that we don't accidentally change the type of
479 * existing kstat_named statistics when writing over them.
480 * Since read_kstat_data() modifies some of the types on their way
481 * out, we need to be sure to handle these types seperately.
483 if (ksp->ks_type == KSTAT_TYPE_NAMED) {
484 void *kbuf;
485 kstat_named_t *kold;
486 kstat_named_t *knew = buf;
487 int i;
489 #ifdef _MULTI_DATAMODEL
490 int model = ddi_model_convert_from(flag & FMODELS);
491 #endif
494 * Since ksp->ks_data may be NULL, we need to take a snapshot
495 * of the published data to look at the types.
497 kbuf = kmem_alloc(bufsize + 1, KM_NOSLEEP);
498 if (kbuf == NULL) {
499 KSTAT_EXIT(ksp);
500 kstat_rele(ksp);
501 kmem_free(buf, bufsize + 1);
502 return (EAGAIN);
504 error = KSTAT_SNAPSHOT(ksp, kbuf, KSTAT_READ);
505 if (error) {
506 KSTAT_EXIT(ksp);
507 kstat_rele(ksp);
508 kmem_free(kbuf, bufsize + 1);
509 kmem_free(buf, bufsize + 1);
510 return (error);
512 kold = kbuf;
515 * read_kstat_data() changes the types of
516 * KSTAT_DATA_LONG / KSTAT_DATA_ULONG, so we need to
517 * make sure that these (modified) types are considered
518 * valid.
520 for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++) {
521 switch (kold->data_type) {
522 #ifdef _MULTI_DATAMODEL
523 case KSTAT_DATA_LONG:
524 switch (model) {
525 case DDI_MODEL_ILP32:
526 if (knew->data_type ==
527 KSTAT_DATA_INT32) {
528 knew->value.l =
529 (long)knew->value.i32;
530 knew->data_type =
531 KSTAT_DATA_LONG;
533 break;
534 default:
535 case DDI_MODEL_NONE:
536 #ifdef _LP64
537 if (knew->data_type ==
538 KSTAT_DATA_INT64) {
539 knew->value.l =
540 (long)knew->value.i64;
541 knew->data_type =
542 KSTAT_DATA_LONG;
544 #endif /* _LP64 */
545 break;
547 break;
548 case KSTAT_DATA_ULONG:
549 switch (model) {
550 case DDI_MODEL_ILP32:
551 if (knew->data_type ==
552 KSTAT_DATA_UINT32) {
553 knew->value.ul =
554 (ulong_t)knew->value.ui32;
555 knew->data_type =
556 KSTAT_DATA_ULONG;
558 break;
559 default:
560 case DDI_MODEL_NONE:
561 #ifdef _LP64
562 if (knew->data_type ==
563 KSTAT_DATA_UINT64) {
564 knew->value.ul =
565 (ulong_t)knew->value.ui64;
566 knew->data_type =
567 KSTAT_DATA_ULONG;
569 #endif /* _LP64 */
570 break;
572 break;
573 #endif /* _MULTI_DATAMODEL */
574 case KSTAT_DATA_STRING:
575 if (knew->data_type != KSTAT_DATA_STRING) {
576 KSTAT_EXIT(ksp);
577 kstat_rele(ksp);
578 kmem_free(kbuf, bufsize + 1);
579 kmem_free(buf, bufsize + 1);
580 return (EINVAL);
583 #ifdef _MULTI_DATAMODEL
584 if (model == DDI_MODEL_ILP32)
585 KSTAT_NAMED_STR_PTR(knew) =
586 (char *)(uintptr_t)
587 knew->value.str.addr.ptr32;
588 #endif
590 * Nothing special for NULL
592 if (KSTAT_NAMED_STR_PTR(knew) == NULL)
593 break;
596 * Check to see that the pointers all point
597 * to within the buffer and after the array
598 * of kstat_named_t's.
600 if (KSTAT_NAMED_STR_PTR(knew) <
601 (char *)
602 ((kstat_named_t *)user_kstat.ks_data +
603 ksp->ks_ndata)) {
604 KSTAT_EXIT(ksp);
605 kstat_rele(ksp);
606 kmem_free(kbuf, bufsize + 1);
607 kmem_free(buf, bufsize + 1);
608 return (EINVAL);
610 if (KSTAT_NAMED_STR_PTR(knew) +
611 KSTAT_NAMED_STR_BUFLEN(knew) >
612 ((char *)user_kstat.ks_data +
613 ksp->ks_data_size)) {
614 KSTAT_EXIT(ksp);
615 kstat_rele(ksp);
616 kmem_free(kbuf, bufsize + 1);
617 kmem_free(buf, bufsize + 1);
618 return (EINVAL);
622 * Update the pointers within the buffer
624 KSTAT_NAMED_STR_PTR(knew) =
625 (char *)buf +
626 (KSTAT_NAMED_STR_PTR(knew) -
627 (char *)user_kstat.ks_data);
628 break;
629 default:
630 break;
634 kold = kbuf;
635 knew = buf;
638 * Now make sure the types are what we expected them to be.
640 for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++)
641 if (kold->data_type != knew->data_type) {
642 KSTAT_EXIT(ksp);
643 kstat_rele(ksp);
644 kmem_free(kbuf, bufsize + 1);
645 kmem_free(buf, bufsize + 1);
646 return (EINVAL);
649 kmem_free(kbuf, bufsize + 1);
652 error = KSTAT_SNAPSHOT(ksp, buf, KSTAT_WRITE);
653 if (!error)
654 error = KSTAT_UPDATE(ksp, KSTAT_WRITE);
655 *rvalp = kstat_chain_id;
656 KSTAT_EXIT(ksp);
657 kstat_rele(ksp);
658 kmem_free(buf, bufsize + 1);
659 return (error);
662 /*ARGSUSED*/
663 static int
664 kstat_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr, int *rvalp)
666 int rc = 0;
668 switch (cmd) {
670 case KSTAT_IOC_CHAIN_ID:
671 *rvalp = kstat_chain_id;
672 break;
674 case KSTAT_IOC_READ:
675 rc = read_kstat_data(rvalp, (void *)data, flag);
676 break;
678 case KSTAT_IOC_WRITE:
679 rc = write_kstat_data(rvalp, (void *)data, flag, cr);
680 break;
682 default:
683 /* invalid request */
684 rc = EINVAL;
686 return (rc);
689 /* ARGSUSED */
690 static int
691 kstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
692 void **result)
694 switch (infocmd) {
695 case DDI_INFO_DEVT2DEVINFO:
696 *result = kstat_devi;
697 return (DDI_SUCCESS);
698 case DDI_INFO_DEVT2INSTANCE:
699 *result = NULL;
700 return (DDI_SUCCESS);
702 return (DDI_FAILURE);
705 static int
706 kstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
708 if (cmd != DDI_ATTACH)
709 return (DDI_FAILURE);
711 if (ddi_create_minor_node(devi, "kstat", S_IFCHR,
712 0, DDI_PSEUDO, 0) == DDI_FAILURE) {
713 ddi_remove_minor_node(devi, NULL);
714 return (DDI_FAILURE);
716 kstat_devi = devi;
717 return (DDI_SUCCESS);
720 static int
721 kstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
723 if (cmd != DDI_DETACH)
724 return (DDI_FAILURE);
726 ddi_remove_minor_node(devi, NULL);
727 return (DDI_SUCCESS);
730 static struct cb_ops kstat_cb_ops = {
731 nulldev, /* open */
732 nulldev, /* close */
733 nodev, /* strategy */
734 nodev, /* print */
735 nodev, /* dump */
736 nodev, /* read */
737 nodev, /* write */
738 kstat_ioctl, /* ioctl */
739 nodev, /* devmap */
740 nodev, /* mmap */
741 nodev, /* segmap */
742 nochpoll, /* poll */
743 ddi_prop_op, /* prop_op */
744 0, /* streamtab */
745 D_NEW | D_MP /* Driver compatibility flag */
748 static struct dev_ops kstat_ops = {
749 DEVO_REV, /* devo_rev, */
750 0, /* refcnt */
751 kstat_info, /* get_dev_info */
752 nulldev, /* identify */
753 nulldev, /* probe */
754 kstat_attach, /* attach */
755 kstat_detach, /* detach */
756 nodev, /* reset */
757 &kstat_cb_ops, /* driver operations */
758 NULL, /* no bus operations */
759 NULL, /* power */
760 ddi_quiesce_not_needed, /* quiesce */
763 static struct modldrv modldrv = {
764 &mod_driverops, "kernel statistics driver", &kstat_ops,
767 static struct modlinkage modlinkage = {
768 MODREV_1, &modldrv, NULL
772 _init(void)
774 return (mod_install(&modlinkage));
778 _fini(void)
780 return (mod_remove(&modlinkage));
784 _info(struct modinfo *modinfop)
786 return (mod_info(&modlinkage, modinfop));