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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
35 #include <sys/nsctl/nsctl.h>
36 #include <sys/nsctl/sd_bcache.h>
38 #include "sdbc_stats.h"
44 static sdbcstat_t
*sdbc_top
;
45 kstat_t
*sdbc_global
= NULL
;
48 int sdbc_value_check(sdbcstat_t
*);
49 int sdbc_validate(kstat_t
*);
50 uint32_t sdbc_getdelta(sdbcstat_t
*, char *);
52 void sdbc_addstat(sdbcstat_t
*);
53 sdbcstat_t
*sdbc_delstat(sdbcstat_t
*);
54 void center(int, char *);
57 * sdbc_discover() - looks for new statistics to be monitored.
58 * Verifies that any statistics found are now already being
63 sdbc_discover(kstat_ctl_t
*kc
)
65 static int validated
= 0;
69 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
71 char kname
[KSTAT_STRLEN
+ 1];
73 sdbcstat_t
*sdbcstat
= NULL
;
76 if (strcmp(ksp
->ks_module
, SDBC_KSTAT_MODULE
) != 0 ||
77 strncmp(ksp
->ks_name
, SDBC_KSTAT_CDSTATS
, 2) != 0)
80 if (kstat_read(kc
, ksp
, NULL
) == -1)
84 * Validate kstat structure
87 if (sdbc_validate(ksp
))
96 for (cur
= sdbc_top
; cur
; cur
= cur
->next
) {
97 char *cur_vname
, *tst_vname
;
99 cur_vname
= kstat_value(cur
->pre_set
,
100 SDBC_CDKSTAT_VOL_NAME
);
102 tst_vname
= kstat_value(ksp
,
103 SDBC_CDKSTAT_VOL_NAME
);
105 if (strncmp(cur_vname
, tst_vname
, NAMED_LEN
) == 0)
110 * Initialize new record
112 sdbcstat
= (sdbcstat_t
*)calloc(1, sizeof (sdbcstat_t
));
114 kinst
= ksp
->ks_instance
;
119 sdbcstat
->pre_set
= kstat_retrieve(kc
, ksp
);
121 if (sdbcstat
->pre_set
== NULL
)
124 sdbcstat
->collected
|= GOT_SET_KSTAT
;
129 (void) sprintf(kname
, "%s%d", SDBC_IOKSTAT_CDSTATS
, kinst
);
131 io_ksp
= kstat_lookup(kc
, SDBC_KSTAT_MODULE
, kinst
, kname
);
132 sdbcstat
->pre_io
= kstat_retrieve(kc
, io_ksp
);
134 if (sdbcstat
->pre_io
== NULL
)
137 sdbcstat
->collected
|= GOT_IO_KSTAT
;
141 * Check if we got a complete set of stats
143 if (sdbcstat
== NULL
)
146 if (SDBC_COMPLETE(sdbcstat
->collected
)) {
147 (void) sdbc_delstat(sdbcstat
);
151 sdbc_addstat(sdbcstat
);
154 if (sdbc_top
== NULL
)
161 * sdbc_update() - updates all of the statistics currently being monitored.
165 sdbc_update(kstat_ctl_t
*kc
)
170 /* Update global kstat information */
171 ksp
= kstat_lookup(kc
, SDBC_KSTAT_MODULE
, -1, SDBC_KSTAT_GSTATS
);
177 kstat_free(sdbc_global
);
179 sdbc_global
= kstat_retrieve(kc
, ksp
);
181 for (cur
= sdbc_top
; cur
!= NULL
; cur
= cur
->next
) {
183 char *kname
, *cname
, *pname
;
185 kstat_t
*set_ksp
, *io_ksp
;
192 if (cur
->cur_set
!= NULL
) {
193 kstat_free(cur
->pre_set
);
194 kstat_free(cur
->pre_io
);
196 cur
->pre_set
= cur
->cur_set
;
197 cur
->pre_io
= cur
->cur_io
;
203 kinst
= cur
->pre_set
->ks_instance
;
204 kname
= cur
->pre_set
->ks_name
;
206 set_ksp
= kstat_lookup(kc
, SDBC_KSTAT_MODULE
, kinst
, kname
);
208 if ((cur
->cur_set
= kstat_retrieve(kc
, set_ksp
)) == NULL
)
211 cur
->collected
|= GOT_SET_KSTAT
;
216 pname
= kstat_value(cur
->pre_set
, SDBC_CDKSTAT_VOL_NAME
);
217 cname
= kstat_value(cur
->cur_set
, SDBC_CDKSTAT_VOL_NAME
);
219 if (strncmp(pname
, cname
, NAMED_LEN
) != 0)
225 kinst
= cur
->pre_io
->ks_instance
;
226 kname
= cur
->pre_io
->ks_name
;
228 io_ksp
= kstat_lookup(kc
, SDBC_KSTAT_MODULE
, kinst
, kname
);
230 if ((cur
->cur_io
= kstat_retrieve(kc
, io_ksp
)) == NULL
)
233 cur
->collected
|= GOT_IO_KSTAT
;
240 * sdbc_report() - outputs statistics for the statistics currently being
241 * monitored. Deletes statistics for volumes that have been disabled.
247 vslist_t
*vslist
= vs_top
;
248 sdbcstat_t
*cur
, *pre
= NULL
;
250 if (sdbc_top
== NULL
)
253 for (cur
= sdbc_top
; cur
!= NULL
; ) { /* CSTYLED */
254 static uint32_t linesout
= 0;
257 char volname
[NAMED_LEN
+ 1];
258 char rmode
[STAT_HDR_SIZE
];
259 char wmode
[STAT_HDR_SIZE
];
261 /* Parse volume name */
262 (void) strncpy(volname
, kstat_value(cur
->pre_set
,
263 SDBC_CDKSTAT_VOL_NAME
), NAMED_LEN
);
264 volname
[NAMED_LEN
] = '\0';
266 /* Check to see if the user specified this volume */
267 for (vslist
= vs_top
; vslist
!= NULL
; vslist
= vslist
->next
)
268 if (strcmp(volname
, vslist
->volname
) == 0)
271 if (vs_top
!= NULL
&& vslist
== NULL
)
274 /* Check if volume is offline and zflag applies */
275 if (zflag
&& sdbc_value_check(cur
) == 0)
278 /* Output volume name */
281 (void) printf(DATA_C16
, volname
);
283 if (SDBC_COMPLETE(cur
->collected
)) {
284 sdbcstat_t
*next
= sdbc_delstat(cur
);
287 cur
= sdbc_top
= next
;
289 cur
= pre
->next
= next
;
291 (void) printf(" <<volume disabled>>\n");
295 offline
= kstat_value(cur
->cur_set
, SDBC_CDKSTAT_FAILED
);
297 (void) printf(" <<volume offline>>\n");
302 /* Type/status flags */
303 if (dflags
& FLAGS
) {
305 uint32_t *dhint
, *nhint
;
308 dhint
= kstat_value(cur
->cur_set
, SDBC_CDKSTAT_CDHINTS
);
309 nhint
= kstat_value(sdbc_global
, SDBC_GKSTAT_NODEHINTS
);
315 hints
&= (NSC_FORCED_WRTHRU
| NSC_NO_FORCED_WRTHRU
|
319 if (hints
& NSC_NOCACHE
)
320 (void) strcpy(rmode
, "D");
322 (void) strcpy(rmode
, "C");
324 if ((hints
& NSC_FORCED_WRTHRU
) || (hints
& NSC_WRTHRU
))
325 (void) strcpy(wmode
, "D");
327 (void) strcpy(wmode
, "C");
329 (void) printf(DATA_C2
, rmode
);
330 (void) printf(DATA_C2
, wmode
);
333 /* Output set information */
345 * sdbc_header() - outputs an appropriate header by referencing the
346 * global variables dflsgs
354 if (hflags
== HEADERS_EXL
)
355 if ((linesout
% DISPLAY_LINES
) != 0)
358 if (hflags
== HEADERS_BOR
)
362 if (hflags
& HEADERS_ATT
)
363 if (hflags
& HEADERS_OUT
)
366 hflags
|= HEADERS_OUT
;
371 /* first line header */
372 if (! (dflags
& SUMMARY
) && dflags
!= FLAGS
) {
374 (void) printf(VOL_HDR_FMT
, " ");
376 if (dflags
& FLAGS
) {
377 (void) printf(STAT_HDR_FMT
, " ");
378 (void) printf(STAT_HDR_FMT
, " ");
384 size
= KPS_HDR_SIZE
* 2 + HIT_HDR_SIZE
;
385 center(size
, "- read -");
389 if (dflags
& WRITE
) {
392 size
= KPS_HDR_SIZE
* 2 + HIT_HDR_SIZE
;
393 center(size
, "- write -");
401 /* second line header */
402 (void) printf(VOL_HDR_FMT
, "volume");
404 if (dflags
& FLAGS
) {
405 (void) printf(STAT_HDR_FMT
, "rd");
406 (void) printf(STAT_HDR_FMT
, "wr");
409 if (dflags
& SUMMARY
) {
410 (void) printf(KPS_HDR_FMT
, "ckps");
411 (void) printf(KPS_HDR_FMT
, "dkps");
412 (void) printf(HIT_HDR_FMT
, HIT_HDR_TXT
);
418 (void) printf(KPS_HDR_FMT
, "ckps");
419 (void) printf(KPS_HDR_FMT
, "dkps");
420 (void) printf(HIT_HDR_FMT
, RHIT_HDR_TXT
);
423 if (dflags
& WRITE
) {
424 (void) printf(KPS_HDR_FMT
, "ckps");
425 (void) printf(KPS_HDR_FMT
, "dkps");
426 (void) printf(HIT_HDR_FMT
, WHIT_HDR_TXT
);
429 if (dflags
& DESTAGED
)
430 (void) printf(KPS_HDR_FMT
, "dstg");
432 if (dflags
& WRCANCEL
)
433 (void) printf(KPS_HDR_FMT
, "cwrl");
440 * sdbc_getstat() - find cache stat by name matching
443 * char *vn - the volume name to match against
445 * sdbcstat_t * - the matching strcture, NULL if not found
448 sdbc_getstat(char *vn
)
450 sdbcstat_t
*cur
, *pre
= NULL
;
452 for (cur
= sdbc_top
; cur
; ) { /* CSTYLED */
454 kstat_value(cur
->pre_set
, SDBC_CDKSTAT_VOL_NAME
);
456 if (SDBC_COMPLETE(cur
->collected
)) {
457 sdbcstat_t
*next
= sdbc_delstat(cur
);
460 cur
= sdbc_top
= next
;
462 cur
= pre
->next
= next
;
467 if (strncmp(volname
, vn
, NAMED_LEN
) == 0)
478 * sdbc_addstat() - adds a fully populated sdbcstat_t structure
479 * to the linked list of currently monitored kstats. The structure
480 * will be added in alphabetical order, using the volume name as the
484 * sdbcstat_t *sdbcstat - to be added to the list.
488 sdbc_addstat(sdbcstat_t
*sdbcstat
)
492 if (sdbc_top
== NULL
) {
497 for (cur
= sdbc_top
; cur
!= NULL
; cur
= cur
->next
) {
498 char *cur_vname
, *nxt_vname
, *tst_vname
;
500 cur_vname
= kstat_value(cur
->pre_set
,
501 SDBC_CDKSTAT_VOL_NAME
);
502 tst_vname
= kstat_value(sdbcstat
->pre_set
,
503 SDBC_CDKSTAT_VOL_NAME
);
505 if (strncmp(cur_vname
, tst_vname
, NAMED_LEN
) > 0) {
509 sdbcstat
->next
= cur
;
515 * If we get to the last item in the list, then just
516 * add this one to the end
518 if (cur
->next
== NULL
) {
519 cur
->next
= sdbcstat
;
523 nxt_vname
= kstat_value(cur
->next
->pre_set
,
524 SDBC_CDKSTAT_VOL_NAME
);
526 if (strncmp(nxt_vname
, tst_vname
, NAMED_LEN
) > 0) {
527 sdbcstat
->next
= cur
->next
;
528 cur
->next
= sdbcstat
;
535 * sdbc_delstat() - deallocate memory for the structure being
539 * sdbcstat_t *sdbcstat - structure to be deallocated
542 * sdbcstat_t * - pointer to the "next" structures in the
543 * linked list. May be NULL if we are removing the last
544 * structure in the linked list.
547 sdbc_delstat(sdbcstat_t
*sdbcstat
)
550 sdbcstat_t
*next
= sdbcstat
->next
;
552 kstat_free(sdbcstat
->pre_set
);
553 kstat_free(sdbcstat
->pre_io
);
554 kstat_free(sdbcstat
->cur_set
);
555 kstat_free(sdbcstat
->cur_io
);
564 * sdbc_value_check() - Checks for activity, supports -z switch
567 * sdbcstat_t *sdbcstat - structure to be checked
574 sdbc_value_check(sdbcstat_t
*sdbcstat
)
576 if (SDBC_COMPLETE(sdbcstat
->collected
))
579 if (sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_CACHE_READ
) != 0)
582 if (sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_DISK_READ
) != 0)
585 if (sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_CACHE_WRITE
) != 0)
588 if (sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_DISK_WRITE
) != 0)
591 if (sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_WRCANCELNS
) != 0)
594 if (io_value_check(sdbcstat
->pre_io
->ks_data
,
595 sdbcstat
->cur_io
->ks_data
) != 0)
602 * sdbc_validate() - validates the structure of the kstats by attempting to
603 * lookup fields used by this module
606 * kstat_t *ksp - kstat to be examined
609 * 1 - one or more fields missing
610 * 0 - all fields present
613 sdbc_validate(kstat_t
*ksp
)
615 if (! kstat_value(ksp
, SDBC_CDKSTAT_VOL_NAME
) ||
616 ! kstat_value(ksp
, SDBC_CDKSTAT_FAILED
) ||
617 ! kstat_value(ksp
, SDBC_CDKSTAT_CDHINTS
) ||
618 ! kstat_value(ksp
, SDBC_CDKSTAT_CACHE_READ
) ||
619 ! kstat_value(ksp
, SDBC_CDKSTAT_DISK_READ
) ||
620 ! kstat_value(ksp
, SDBC_CDKSTAT_CACHE_WRITE
) ||
621 ! kstat_value(ksp
, SDBC_CDKSTAT_DISK_WRITE
) ||
622 ! kstat_value(ksp
, SDBC_CDKSTAT_DESTAGED
) ||
623 ! kstat_value(ksp
, SDBC_CDKSTAT_WRCANCELNS
))
630 * sdbc_getvalues() - populates a values structure with data obtained from the
634 * sdbcstat_t *sdbcstat - pointer to the structure containing the kstats
635 * sdbcvals_t *vals - pointer to the structure that will receive the values
636 * int flags - flags that describe adjustments made to the values
643 sdbc_getvalues(sdbcstat_t
*sdbcstat
, sdbcvals_t
*vals
, int flags
)
653 if (sdbcstat
== NULL
)
656 cur
= sdbcstat
->cur_io
->ks_data
;
657 pre
= sdbcstat
->pre_io
->ks_data
;
659 hr_etime
= hrtime_delta(pre
->rlastupdate
, cur
->rlastupdate
);
660 etime
= hr_etime
/ (double)NANOSEC
;
664 FBA_SIZE(sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_CACHE_READ
));
666 FBA_SIZE(sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_DISK_READ
));
669 vals
->total_reads
= vals
->cache_read
+ vals
->disk_read
;
671 if (vals
->cache_read
== 0)
672 vals
->read_hit
= 0.0;
675 ((float)vals
->cache_read
/ vals
->total_reads
) * 100.0;
679 FBA_SIZE(sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_CACHE_WRITE
));
681 FBA_SIZE(sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_DISK_WRITE
));
683 vals
->total_writes
= vals
->cache_write
+ vals
->disk_write
;
686 FBA_SIZE(sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_DESTAGED
));
688 if (vals
->cache_write
== 0)
689 vals
->write_hit
= 0.0;
691 vals
->write_hit
= ((float)vals
->cache_write
/
692 (vals
->total_writes
- vals
->destaged
)) * 100.0;
695 vals
->write_cancellations
=
696 FBA_SIZE(sdbc_getdelta(sdbcstat
, SDBC_CDKSTAT_WRCANCELNS
));
698 vals
->total_cache
= vals
->cache_read
+ vals
->cache_write
;
699 vals
->total_disk
= vals
->disk_read
+ vals
->disk_write
;
701 /* total cache hit calculation */
705 if (vals
->cache_read
!= 0) {
706 vals
->cache_hit
+= vals
->read_hit
;
710 if (vals
->cache_write
!= 0) {
711 vals
->cache_hit
+= vals
->write_hit
;
716 vals
->cache_hit
/= (float)factors
;
721 if (flags
& SDBC_KBYTES
)
723 if ((flags
& SDBC_INTAVG
) && (etime
> 0))
727 vals
->cache_read
/= divisor
;
728 vals
->disk_read
/= divisor
;
729 vals
->total_reads
/= divisor
;
731 vals
->cache_write
/= divisor
;
732 vals
->disk_write
/= divisor
;
733 vals
->total_writes
/= divisor
;
735 vals
->total_cache
/= divisor
;
736 vals
->total_disk
/= divisor
;
738 vals
->destaged
/= divisor
;
739 vals
->write_cancellations
/= divisor
;
746 * sdbc_getdelta() - calculates the difference between two kstat fields
749 * sdbcstat_t *sdbcstat - the SDBC stat strcture containing the two fields
750 * char *name - the name of the fields
752 * uint32_t value of the differences adjusted for overflow of the data type
755 sdbc_getdelta(sdbcstat_t
*sdbcstat
, char *name
)
760 pre_val
= kstat_value(sdbcstat
->pre_set
, name
);
761 cur_val
= kstat_value(sdbcstat
->cur_set
, name
);
763 return (u32_delta(*pre_val
, *cur_val
));
767 center(int size
, char *hdr
)
776 if (strlen(hdr
) < size
) {
777 lpad
= (size
- strlen(hdr
)) / 2;
782 rpad
= size
- (lpad
+ strlen(hdr
));
786 (void) sprintf(fmt
, "%%%ds%%s%%%ds", lpad
, rpad
);
787 (void) printf(fmt
, " ", hdr
, " ");