OpenSolaris_b135/cmd/avs/dsstat/ii_stats.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/mutex.h>

#include <kstat.h>

#include <sys/unistat/spcs_s.h>
#include <sys/nsctl/dsw.h>
#include "../../../uts/common/avs/ns/dsw/dsw_dev.h"
#include <sys/nsctl/dsw_dev.h>

#include "sdbc_stats.h"
#include "ii_stats.h"

#include "dsstat.h"
#include "common.h"
#include "report.h"

static iistat_t *ii_top = NULL;

void ii_add_stat(iistat_t *);
iistat_t *ii_del_stat(iistat_t *);

int ii_value_check(iistat_t *iistat);
int ii_validate(kstat_t *ksp);
int ii_vol_selected(kstat_t *);

/*
 * ii_discover() - looks for new statistics to be monitored.
 * Verifies that any statistics found are now already being
 * monitored.
 *
 */
int
ii_discover(kstat_ctl_t *kc)
{
	static int validated = 0;

	kstat_t *ksp;

	/* Loop on all kstats */
	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
		char *kname;
		iistat_t *cur;
		iistat_t *iistat = NULL;
		kstat_t *mst_ksp;
		kstat_t *shd_ksp;
		kstat_t *bmp_ksp;
		kstat_t *ovr_ksp;

		/* Search for II set */
		if (strcmp(ksp->ks_class, II_KSTAT_CLASS) != 0)
			continue;

		if (kstat_read(kc, ksp, NULL) == -1)
			continue;

		/*
		 * Validate kstat structure
		 */
		if (! validated) {
			if (ii_validate(ksp))
				return (EINVAL);

			validated++;
		}

		/*
		 * Duplicate check
		 */
		for (cur = ii_top; cur != NULL; cur = cur->next) {
			char *cur_vname, *tst_vname;
			uint32_t cur_inst, tst_inst;

			cur_vname = cur->pre_set->ks_name;
			cur_inst = cur->pre_set->ks_instance;

			tst_vname = ksp->ks_name;
			tst_inst = ksp->ks_instance;

			if (strcmp(cur_vname, tst_vname) == 0 &&
			    cur_inst == tst_inst)
				goto next;
		}

		/*
		 * Initialize new record
		 */
		iistat = (iistat_t *)calloc(1, sizeof (iistat_t));

		/*
		 * Set kstat
		 */
		iistat->pre_set = kstat_retrieve(kc, ksp);

		if (iistat->pre_set == NULL)
			goto next;

		iistat->collected |= GOT_SETSTAT;

		/*
		 * Master kstat
		 */
		kname = kstat_value(iistat->pre_set, DSW_SKSTAT_MSTIO);

		mst_ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);
		iistat->pre_mst = kstat_retrieve(kc, mst_ksp);

		if (iistat->pre_mst == NULL)
			goto next;

		iistat->collected |= GOT_MSTSTAT;

		/*
		 * Shadow kstat
		 */
		kname = kstat_value(iistat->pre_set, DSW_SKSTAT_SHDIO);

		shd_ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);
		iistat->pre_shd = kstat_retrieve(kc, shd_ksp);

		if (iistat->pre_shd == NULL)
			goto next;

		iistat->collected |= GOT_SHDSTAT;

		/*
		 * Bitmap kstat
		 */
		kname = kstat_value(iistat->pre_set, DSW_SKSTAT_BMPIO);

		bmp_ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);
		iistat->pre_bmp = kstat_retrieve(kc, bmp_ksp);

		if (iistat->pre_bmp == NULL)
			goto next;

		iistat->collected |= GOT_BMPSTAT;

		/*
		 * Overflow kstat
		 */
		kname = kstat_value(iistat->pre_set, DSW_SKSTAT_OVRIO);

		ovr_ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);
		iistat->pre_ovr = kstat_retrieve(kc, ovr_ksp);

		if (iistat->pre_ovr == NULL)
			goto next;

		iistat->collected |= GOT_OVRSTAT;

next:
		/*
		 * Check if we got a complete set of stats
		 */
		if (iistat == NULL)
			continue;

		if (IIMG_COMPLETE(iistat->collected)) {
			(void) ii_del_stat(iistat);
			continue;
		}

		/*
		 * Add to linked list
		 */
		ii_add_stat(iistat);
	}

	if (ii_top == NULL)
		return (EAGAIN);

	return (0);
}

/*
 * ii_update() - updates all of the statistics currently being monitored.
 *
 */
int
ii_update(kstat_ctl_t *kc)
{
	iistat_t *cur;

	for (cur = ii_top; cur != NULL; cur = cur->next) {
		char volname[KSTAT_STRLEN + 1];
		char *kname;

		kstat_t *ksp = NULL;

		cur->collected = 0;

		/*
		 * Age off old stats
		 */
		if (cur->cur_set != NULL) {
			kstat_free(cur->pre_set);
			kstat_free(cur->pre_mst);
			kstat_free(cur->pre_shd);
			kstat_free(cur->pre_bmp);

			cur->pre_set = cur->cur_set;
			cur->pre_mst = cur->cur_mst;
			cur->pre_shd = cur->cur_shd;
			cur->pre_bmp = cur->cur_bmp;

			if (cur->cur_ovr != NULL) {
				kstat_free(cur->pre_ovr);
				cur->pre_ovr = cur->cur_ovr;
			}
		}

		/*
		 * Set kstat
		 */
		(void) strncpy(volname, cur->pre_set->ks_name, KSTAT_STRLEN);
		volname[KSTAT_STRLEN] = '\0';

		ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, volname);

		if ((cur->cur_set = kstat_retrieve(kc, ksp)) == NULL)
			continue;

		cur->collected |= GOT_SETSTAT;

		/*
		 * Validate set
		 */
		if (strcmp(cur->pre_set->ks_name, cur->cur_set->ks_name) != 0 ||
		    cur->pre_set->ks_instance != cur->cur_set->ks_instance)
			continue;

		/*
		 * Master kstat
		 */
		kname = kstat_value(cur->cur_set, DSW_SKSTAT_MSTIO);

		ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);

		if ((cur->cur_mst = kstat_retrieve(kc, ksp)) == NULL)
			continue;

		cur->collected |= GOT_MSTSTAT;

		/*
		 * Shadow kstat
		 */
		kname = kstat_value(cur->cur_set, DSW_SKSTAT_SHDIO);

		ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);

		if ((cur->cur_shd = kstat_retrieve(kc, ksp)) == NULL)
			continue;

		cur->collected |= GOT_SHDSTAT;

		/*
		 * Bitmap kstat
		 */
		kname = kstat_value(cur->pre_set, DSW_SKSTAT_BMPIO);

		ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);

		if ((cur->cur_bmp = kstat_retrieve(kc, ksp)) == NULL)
			continue;

		cur->collected |= GOT_BMPSTAT;

		/*
		 * Overflow kstat
		 */
		kname = kstat_value(cur->cur_set, DSW_SKSTAT_OVRIO);

		ksp = kstat_lookup(kc, II_KSTAT_MODULE, -1, kname);

		if (ksp == NULL) {
			if (cur->pre_ovr != NULL) {
				kstat_free(cur->pre_ovr);
				cur->pre_ovr = NULL;
			}
			if (cur->cur_ovr != NULL) {
				kstat_free(cur->cur_ovr);
				cur->cur_ovr = NULL;
			}
			continue;
		}

		if (cur->pre_ovr == NULL) {
			if ((cur->pre_ovr = kstat_retrieve(kc, ksp)) == NULL)
				continue;
		} else {
			if ((cur->cur_ovr = kstat_retrieve(kc, ksp)) == NULL)
				continue;
		}

		cur->collected |= GOT_OVRSTAT;
	}

	return (0);
}

/*
 * ii_report() - outputs statistics for the statistics currently being
 * monitored.  Deletes statistics for volumes that have been disabled.
 *
 */
int
ii_report()
{
	uint32_t *flags;
	int padsz = 0;
	char pad[20] = {0};
	iistat_t *cur, *pre = NULL;

	if (ii_top == NULL) {
		return (0);
	}

	/* Create padding string for secondary report lines */
	if (dflags & FLAGS) {
		padsz += STAT_HDR_SIZE;
		padsz += STAT_HDR_SIZE;
	}

	if (dflags & PCTS)
		padsz += PCT_HDR_SIZE;

	if (padsz) {
		char fmt[20];
		(void) sprintf(fmt, "%%%ds", padsz);
		(void) sprintf(pad, fmt, "");
	}

	for (cur = ii_top; cur; /* CSTYLED */) {
		int first = 1;
		char data[20] = {0};

		/* Check to see if this is this a complete */
		if (IIMG_COMPLETE(cur->collected)) {
			char *c;
			char vol[(NAMED_LEN * 4) + 1] = {0};
			int offset;
			iistat_t *next;

			/* notify user of set being disabled */
			c = kstat_value(cur->pre_set, DSW_SKSTAT_SETA);
			(void) strncpy(vol, c, NAMED_LEN);
			c = kstat_value(cur->pre_set, DSW_SKSTAT_SETB);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->pre_set, DSW_SKSTAT_SETC);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->pre_set, DSW_SKSTAT_SETD);
			(void) strncat(vol, c, NAMED_LEN);

			offset = strlen(vol) - NAMED_LEN;

			if (offset < 0)
				offset = 0;

			(void) printf(DATA_C16, vol + offset);
			(void) printf(" %s\n", II_DISABLED);

			/* free memory and remove stat from list */
			next = ii_del_stat(cur);

			if (! pre)
				cur = ii_top = next;
			else
				cur = pre->next = next;

			continue;
		}

		/* Check to see if the user specified this volume */
		if (! ii_vol_selected(cur->pre_set))
			goto next;

		/* Check to see if zflag applies */
		if (zflag && ii_value_check(cur) == 0)
			goto next;

		/* Calculate flags */
		flags = kstat_value(cur->cur_set, DSW_SKSTAT_FLAGS);

		if (dflags & FLAGS) {

			char c[STAT_HDR_SIZE];
			char vtype[STAT_HDR_SIZE];
			char vstat[STAT_HDR_SIZE];

			if (*flags & DSW_GOLDEN)
				(void) strcpy(c, II_INDEPENDENT);
			else
				(void) strcpy(c, II_DEPENDENT);

			(void) sprintf(vtype, DATA_C2, c);
			(void) strcat(data, vtype);

			if (*flags & DSW_COPYINGP)
				(void) strcpy(c, II_COPYING);
			else
				(void) strcpy(c, NO_INFO);


			(void) sprintf(vstat, DATA_C2, c);
			(void) strcat(data, vstat);
		}

		/* Calculate sync needed precentage */
		if (dflags & PCTS) {
			char snpct[10];
			uint32_t *chkbits;
			uint32_t *cpybits;
			uint32_t *shdbits;
			uint32_t *volsize;
			float pct;

			cpybits =
			    kstat_value(cur->cur_set, DSW_SKSTAT_COPYBITS);

			shdbits =
			    kstat_value(cur->cur_set, DSW_SKSTAT_SHDBITS);

			volsize =
			    kstat_value(cur->cur_set, DSW_SKSTAT_SIZE);

			*volsize /= DSW_SIZE;

			chkbits = *cpybits >= *shdbits ? cpybits : shdbits;

			pct = ((float)*chkbits / *volsize) * 100.0;

			(void) sprintf(snpct, DATA_F62, pct);

			(void) strcat(data, snpct);
		}

		/* Master statistics */
		if (rflags & IIMG_MST) {
			char *c;
			char vol[(NAMED_LEN * 4) + 1] = {0};
			int offset;

			c = kstat_value(cur->cur_set, DSW_SKSTAT_MSTA);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_MSTB);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_MSTC);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_MSTD);
			(void) strncat(vol, c, NAMED_LEN);

			offset = strlen(vol) - NAMED_LEN;

			if (offset < 0)
				offset = 0;

			header();
			(void) printf(DATA_C16, vol + offset);
			(void) printf("%s", data);
			(void) printf(ROLE_INF_FMT, II_MASTER);

			if (*flags & DSW_MSTOFFLINE) {
				(void) printf(" <<offline>>");
				linesout++;
			} else {
				io_report(cur->cur_mst, cur->pre_mst,
				    sdbc_getstat(vol + offset));
			}

			(void) printf("\n");

			if (first) {
				(void) strcpy(data, strlen(pad) > 0 ? pad : "");
				first = 0;
			}
		}

		/* Shadow statistics */
		if (rflags & IIMG_SHD) {
			char *c;
			char vol[(NAMED_LEN * 4) + 1] = {0};
			int offset;

			c = kstat_value(cur->cur_set, DSW_SKSTAT_SETA);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_SETB);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_SETC);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_SETD);
			(void) strncat(vol, c, NAMED_LEN);

			offset = strlen(vol) - NAMED_LEN;

			if (offset < 0)
				offset = 0;

			header();
			(void) printf(DATA_C16, vol + offset);
			(void) printf("%s", data);
			(void) printf(ROLE_INF_FMT, II_SHADOW);

			if (*flags & DSW_SHDOFFLINE) {
				(void) printf(" <<offline>>");
				linesout++;
			} else {
				io_report(cur->cur_shd, cur->pre_shd,
				    sdbc_getstat(vol + offset));
			}

			(void) printf("\n");

			if (first) {
				(void) strcpy(data, strlen(pad) > 0 ? pad : "");
				first = 0;
			}
		}

		/* Bitmap statistics */
		if (rflags & IIMG_BMP) {
			char *c;
			char vol[(NAMED_LEN * 4) + 1] = {0};
			int offset;

			c = kstat_value(cur->cur_set, DSW_SKSTAT_BMPA);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_BMPB);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_BMPC);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_BMPD);
			(void) strncat(vol, c, NAMED_LEN);

			offset = strlen(vol) - NAMED_LEN;

			if (offset < 0)
				offset = 0;

			header();
			(void) printf(DATA_C16, vol + offset);
			(void) printf("%s", data);
			(void) printf(ROLE_INF_FMT, II_BITMAP);

			if (*flags & DSW_BMPOFFLINE) {
				(void) printf(" <<offline>>");
				linesout++;
			} else {
				io_report(cur->cur_bmp, cur->pre_bmp,
				    sdbc_getstat(vol + offset));
			}
			(void) printf("\n");

			if (first) {
				(void) strcpy(data, strlen(pad) > 0 ? pad : "");
				first = 0;
			}
		}

		/* Overflow statistics */
		if (rflags & IIMG_OVR) {
			char *c;
			char msg[20] = {0};
			char vol[(NAMED_LEN * 4) + 1] = {0};
			int offset;

			if (cur->cur_ovr == NULL && cur->pre_ovr != NULL)
				(void) strcpy(msg, " <<attached>>");

			if (! (cur->collected & GOT_OVRSTAT))
				(void) strcpy(msg, " <<not attached>>");

			c = kstat_value(cur->cur_set, DSW_SKSTAT_OVRA);
			(void) strncpy(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_OVRB);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_OVRC);
			(void) strncat(vol, c, NAMED_LEN);
			c = kstat_value(cur->cur_set, DSW_SKSTAT_OVRD);
			(void) strncat(vol, c, NAMED_LEN);

			offset = strlen(vol) - NAMED_LEN;

			if (offset < 0)
				offset = 0;

			header();
			(void) printf(DATA_C16, vol + offset);
			(void) printf("%s", data);
			(void) printf(ROLE_INF_FMT, II_OVERFLOW);

			if (strlen(msg)) {
				(void) printf("%s\n", msg);
				linesout++;
				goto next;
			}

			if (*flags & DSW_OVROFFLINE) {
				(void) printf(" <<offline>>");
				linesout++;
			} else {
				io_report(cur->cur_ovr, cur->pre_ovr,
				    sdbc_getstat(vol + offset));
			}

			(void) printf("\n");

			if (first) {
				(void) strcpy(data, strlen(pad) > 0 ? pad : "");
				first = 0;
			}
		}


next:
		pre = cur;
		cur = cur->next;
	}

	return (0);
}

/*
 * ii_add_stat() - adds a fully populated iistat_t structure
 * to the linked list of currently monitored kstats.  The structure
 * will be added in alphabetical order, using the volume name of
 * the shadow volume as the key.
 *
 */
void
ii_add_stat(iistat_t *iistat)
{

	iistat_t *cur;

	if (ii_top == NULL) {
		ii_top = iistat;
		return;
	}

	for (cur = ii_top; cur != NULL; cur = cur->next) {
		if (strcmp(cur->pre_set->ks_name,
		    iistat->pre_set->ks_name) <= 0) {
			/*
			 * If we get to the last item in the list, then just
			 * add this one to the end
			 */
			if (cur->next == NULL) {
				cur->next = iistat;
				return;
			}

			if (strcmp(cur->next->pre_set->ks_name,
			    iistat->pre_set->ks_name) > 0) {
				iistat->next = cur->next;
				cur->next = iistat;
				return;
			}
		} else {
			if (cur == ii_top)
				ii_top = iistat;

			iistat->next = cur;

			return;
		}
	}
}

/*
 * ii_del_stat() - deallocate memory for the structure being
 * passed in.
 *
 * parameters
 * 	iistat_t *iistat - structure to be deallocated
 *
 * returns
 * 	iistat_t * - pointer to the "next" structures in the
 * 	linked list. May be NULL if we are removing the last
 * 	structure in the linked list.
 *
 */
iistat_t *
ii_del_stat(iistat_t *iistat)
{

	iistat_t *next = iistat->next;

	kstat_free(iistat->pre_set);
	kstat_free(iistat->pre_mst);
	kstat_free(iistat->pre_shd);
	kstat_free(iistat->pre_bmp);
	kstat_free(iistat->pre_ovr);
	kstat_free(iistat->cur_set);
	kstat_free(iistat->cur_mst);
	kstat_free(iistat->cur_shd);
	kstat_free(iistat->cur_bmp);
	kstat_free(iistat->cur_ovr);

	free(iistat);

	return (next);
}

int
ii_value_check(iistat_t *iistat)
{
	if (IIMG_COMPLETE(iistat->collected))
		return (1);

	if (io_value_check(iistat->pre_mst->ks_data,
	    iistat->cur_mst->ks_data)) {
		return (1);
	}

	if (io_value_check(iistat->pre_shd->ks_data,
	    iistat->cur_shd->ks_data)) {
		return (1);
	}

	if (io_value_check(iistat->pre_bmp->ks_data,
	    iistat->cur_bmp->ks_data)) {
		return (1);
	}

	if (iistat->pre_ovr && iistat->cur_ovr) {
		if (io_value_check(iistat->pre_ovr->ks_data,
		    iistat->cur_ovr->ks_data)) {
			return (1);
		}
	}

	return (0);
}

int
ii_validate(kstat_t *ksp)
{
	if (! kstat_value(ksp, DSW_SKSTAT_MSTIO) ||
	    ! kstat_value(ksp, DSW_SKSTAT_SHDIO) ||
	    ! kstat_value(ksp, DSW_SKSTAT_BMPIO) ||
	    ! kstat_value(ksp, DSW_SKSTAT_OVRIO) ||
	    ! kstat_value(ksp, DSW_SKSTAT_FLAGS) ||
	    ! kstat_value(ksp, DSW_SKSTAT_MSTA) ||
	    ! kstat_value(ksp, DSW_SKSTAT_SETA) ||
	    ! kstat_value(ksp, DSW_SKSTAT_BMPA) ||
	    ! kstat_value(ksp, DSW_SKSTAT_OVRA) ||
	    ! kstat_value(ksp, DSW_SKSTAT_SHDBITS) ||
	    ! kstat_value(ksp, DSW_SKSTAT_COPYBITS) ||
	    ! kstat_value(ksp, DSW_SKSTAT_SIZE))
		return (1);

	return (0);
}

int
ii_vol_selected(kstat_t *ksp)
{
	vslist_t *vslist = vs_top;

	for (vslist = vs_top; vslist != NULL; vslist = vslist->next) {
		char *vn;
		int off = 0;

		vn = ksp->ks_name;

		if ((off = strlen(vn) - NAMED_LEN) <= 0) {
			off = 0;
		}

		if (strcmp(vslist->volname, &vn[off]) == 0) {
			break;
		}
	}

	if (vs_top != NULL && vslist == NULL) {
		return (0);
	} else {
		return (1);
	}
}