xref: /onnv-gate/usr/src/cmd/fm/fmd/common/fmd_ustat.c (revision 1193:e784a8fa27da)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
22*1193Smws 
230Sstevel@tonic-gate /*
24*1193Smws  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
250Sstevel@tonic-gate  * Use is subject to license terms.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <fmd_ustat.h>
310Sstevel@tonic-gate #include <fmd_alloc.h>
320Sstevel@tonic-gate #include <fmd_subr.h>
330Sstevel@tonic-gate #include <fmd_string.h>
340Sstevel@tonic-gate #include <fmd_error.h>
350Sstevel@tonic-gate #include <fmd.h>
360Sstevel@tonic-gate 
370Sstevel@tonic-gate static fmd_ustat_chunk_t *
fmd_ustat_chunk_init(fmd_ustat_t * usp,fmd_stat_t * base,uint_t len)380Sstevel@tonic-gate fmd_ustat_chunk_init(fmd_ustat_t *usp, fmd_stat_t *base, uint_t len)
390Sstevel@tonic-gate {
400Sstevel@tonic-gate 	fmd_ustat_chunk_t *cp;
410Sstevel@tonic-gate 
420Sstevel@tonic-gate 	cp = fmd_zalloc(sizeof (fmd_ustat_chunk_t), FMD_SLEEP);
430Sstevel@tonic-gate 	cp->usc_base = base;
440Sstevel@tonic-gate 	cp->usc_len = len;
450Sstevel@tonic-gate 	cp->usc_refs = 1;
460Sstevel@tonic-gate 
470Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
480Sstevel@tonic-gate 	fmd_list_append(&usp->us_chunks, cp);
490Sstevel@tonic-gate 
500Sstevel@tonic-gate 	return (cp);
510Sstevel@tonic-gate }
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static void
fmd_ustat_chunk_hold(fmd_ustat_t * usp,fmd_ustat_chunk_t * cp)540Sstevel@tonic-gate fmd_ustat_chunk_hold(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp)
550Sstevel@tonic-gate {
560Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
570Sstevel@tonic-gate 	cp->usc_refs++;
580Sstevel@tonic-gate 	ASSERT(cp->usc_refs != 0);
590Sstevel@tonic-gate }
600Sstevel@tonic-gate 
610Sstevel@tonic-gate static void
fmd_ustat_chunk_rele(fmd_ustat_t * usp,fmd_ustat_chunk_t * cp)620Sstevel@tonic-gate fmd_ustat_chunk_rele(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp)
630Sstevel@tonic-gate {
640Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
650Sstevel@tonic-gate 	ASSERT(cp->usc_refs != 0);
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	if (--cp->usc_refs == 0) {
680Sstevel@tonic-gate 		/*
690Sstevel@tonic-gate 		 * Note that any strings pointed to by FMD_TYPE_STRING stats
700Sstevel@tonic-gate 		 * are freed one-by-one before releasing the chunk.  So here
710Sstevel@tonic-gate 		 * we can just free the chunk and not worry about its content.
720Sstevel@tonic-gate 		 */
730Sstevel@tonic-gate 		fmd_free(cp->usc_base, sizeof (fmd_stat_t) * cp->usc_len);
740Sstevel@tonic-gate 		fmd_list_delete(&usp->us_chunks, cp);
750Sstevel@tonic-gate 		fmd_free(cp, sizeof (fmd_ustat_chunk_t));
760Sstevel@tonic-gate 	}
770Sstevel@tonic-gate }
780Sstevel@tonic-gate 
790Sstevel@tonic-gate fmd_ustat_t *
fmd_ustat_create(void)800Sstevel@tonic-gate fmd_ustat_create(void)
810Sstevel@tonic-gate {
820Sstevel@tonic-gate 	fmd_ustat_t *usp = fmd_zalloc(sizeof (fmd_ustat_t), FMD_SLEEP);
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 	(void) pthread_rwlock_init(&usp->us_lock, NULL);
850Sstevel@tonic-gate 	usp->us_hashlen = fmd.d_str_buckets;
860Sstevel@tonic-gate 	usp->us_hash = fmd_zalloc(sizeof (void *) * usp->us_hashlen, FMD_SLEEP);
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 	return (usp);
890Sstevel@tonic-gate }
900Sstevel@tonic-gate 
910Sstevel@tonic-gate void
fmd_ustat_destroy(fmd_ustat_t * usp)920Sstevel@tonic-gate fmd_ustat_destroy(fmd_ustat_t *usp)
930Sstevel@tonic-gate {
940Sstevel@tonic-gate 	fmd_ustat_elem_t *ep, *np;
950Sstevel@tonic-gate 	uint_t i;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&usp->us_lock);
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	for (i = 0; i < usp->us_hashlen; i++) {
1000Sstevel@tonic-gate 		for (ep = usp->us_hash[i]; ep != NULL; ep = np) {
1010Sstevel@tonic-gate 			if (ep->use_stat->fmds_type == FMD_TYPE_STRING)
1020Sstevel@tonic-gate 				fmd_strfree(ep->use_stat->fmds_value.str);
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 			if (ep->use_chunk != NULL)
1050Sstevel@tonic-gate 				fmd_ustat_chunk_rele(usp, ep->use_chunk);
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 			np = ep->use_next;
1080Sstevel@tonic-gate 			fmd_free(ep, sizeof (fmd_ustat_elem_t));
1090Sstevel@tonic-gate 		}
1100Sstevel@tonic-gate 	}
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	ASSERT(usp->us_chunks.l_next == NULL);
1130Sstevel@tonic-gate 	ASSERT(usp->us_chunks.l_prev == NULL);
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	fmd_free(usp->us_hash, sizeof (void *) * usp->us_hashlen);
1160Sstevel@tonic-gate 	fmd_free(usp, sizeof (fmd_ustat_t));
1170Sstevel@tonic-gate }
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate int
fmd_ustat_snapshot(fmd_ustat_t * usp,fmd_ustat_snap_t * uss)1200Sstevel@tonic-gate fmd_ustat_snapshot(fmd_ustat_t *usp, fmd_ustat_snap_t *uss)
1210Sstevel@tonic-gate {
1220Sstevel@tonic-gate 	const fmd_ustat_elem_t *ep;
1230Sstevel@tonic-gate 	fmd_stat_t *sp;
1240Sstevel@tonic-gate 	uint_t i;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&usp->us_lock);
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate 	uss->uss_buf = sp = malloc(sizeof (fmd_stat_t) * usp->us_nelems);
1290Sstevel@tonic-gate 	uss->uss_len = usp->us_nelems;
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	if (uss->uss_buf == NULL) {
1320Sstevel@tonic-gate 		(void) pthread_rwlock_unlock(&usp->us_lock);
1330Sstevel@tonic-gate 		return (fmd_set_errno(EFMD_STAT_NOMEM));
1340Sstevel@tonic-gate 	}
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	for (i = 0; i < usp->us_hashlen; i++) {
1370Sstevel@tonic-gate 		for (ep = usp->us_hash[i]; ep != NULL; ep = ep->use_next) {
1380Sstevel@tonic-gate 			bcopy(ep->use_stat, sp, sizeof (fmd_stat_t));
1390Sstevel@tonic-gate 			if (sp->fmds_type == FMD_TYPE_STRING &&
1400Sstevel@tonic-gate 			    sp->fmds_value.str != NULL)
1410Sstevel@tonic-gate 				sp->fmds_value.str = strdup(sp->fmds_value.str);
1420Sstevel@tonic-gate 			sp++;
1430Sstevel@tonic-gate 		}
1440Sstevel@tonic-gate 	}
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	ASSERT(sp == uss->uss_buf + uss->uss_len);
1470Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&usp->us_lock);
1480Sstevel@tonic-gate 	return (0);
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate static void
fmd_ustat_delete_locked(fmd_ustat_t * usp,uint_t n,fmd_stat_t * sp,int strfree)1520Sstevel@tonic-gate fmd_ustat_delete_locked(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp, int strfree)
1530Sstevel@tonic-gate {
1540Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	for (; n-- != 0; sp++) {
1570Sstevel@tonic-gate 		uint_t h = fmd_strhash(sp->fmds_name) % usp->us_hashlen;
1580Sstevel@tonic-gate 		fmd_ustat_elem_t *ep, **pp = &usp->us_hash[h];
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 		for (ep = *pp; ep != NULL; ep = ep->use_next) {
1610Sstevel@tonic-gate 			if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) != 0)
1620Sstevel@tonic-gate 				pp = &ep->use_next;
1630Sstevel@tonic-gate 			else
1640Sstevel@tonic-gate 				break;
1650Sstevel@tonic-gate 		}
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 		if (ep == NULL)
1680Sstevel@tonic-gate 			continue; /* silently ignore unregistered entries */
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 		if (strfree && ep->use_stat->fmds_type == FMD_TYPE_STRING)
1710Sstevel@tonic-gate 			fmd_strfree(ep->use_stat->fmds_value.str);
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 		if (ep->use_chunk != NULL)
1740Sstevel@tonic-gate 			fmd_ustat_chunk_rele(usp, ep->use_chunk);
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 		*pp = ep->use_next;
1770Sstevel@tonic-gate 		fmd_free(ep, sizeof (fmd_ustat_elem_t));
1780Sstevel@tonic-gate 		usp->us_nelems--;
1790Sstevel@tonic-gate 	}
1800Sstevel@tonic-gate }
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate fmd_stat_t *
fmd_ustat_insert(fmd_ustat_t * usp,uint_t flags,uint_t n,fmd_stat_t * template,fmd_stat_t ** epp)1830Sstevel@tonic-gate fmd_ustat_insert(fmd_ustat_t *usp, uint_t flags,
1840Sstevel@tonic-gate     uint_t n, fmd_stat_t *template, fmd_stat_t **epp)
1850Sstevel@tonic-gate {
1860Sstevel@tonic-gate 	fmd_stat_t *stats, *sp;
1870Sstevel@tonic-gate 	fmd_ustat_chunk_t *cp;
1880Sstevel@tonic-gate 	uint_t i;
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	int checkid = flags & FMD_USTAT_VALIDATE;
1910Sstevel@tonic-gate 	int has_str = 0;
1920Sstevel@tonic-gate 	int err = 0;
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	if (flags & FMD_USTAT_ALLOC) {
1950Sstevel@tonic-gate 		sp = stats = fmd_alloc(sizeof (fmd_stat_t) * n, FMD_SLEEP);
1960Sstevel@tonic-gate 		bcopy(template, stats, sizeof (fmd_stat_t) * n);
1970Sstevel@tonic-gate 	} else
1980Sstevel@tonic-gate 		sp = stats = template;
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&usp->us_lock);
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	if (flags & FMD_USTAT_ALLOC)
2030Sstevel@tonic-gate 		cp = fmd_ustat_chunk_init(usp, stats, n);
2040Sstevel@tonic-gate 	else
2050Sstevel@tonic-gate 		cp = NULL;
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	for (i = 0; i < n; i++, sp++) {
2080Sstevel@tonic-gate 		char *p, *q = sp->fmds_name + sizeof (sp->fmds_name);
2090Sstevel@tonic-gate 		fmd_ustat_elem_t *ep;
2100Sstevel@tonic-gate 		uint_t h;
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 		/*
2130Sstevel@tonic-gate 		 * Since a module may be passing in this statistic and our
2140Sstevel@tonic-gate 		 * names are represented by a fixed-size array, scan fmds_name
2150Sstevel@tonic-gate 		 * to ensure it has a \0 somewhere before we attempt strcmps.
2160Sstevel@tonic-gate 		 */
2170Sstevel@tonic-gate 		for (p = sp->fmds_name; p < q; p++) {
2180Sstevel@tonic-gate 			if (*p == '\0')
2190Sstevel@tonic-gate 				break;
2200Sstevel@tonic-gate 		}
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 		if (p == q)
2230Sstevel@tonic-gate 			q[-1] = '\0'; /* nul-terminate for subsequent message */
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 		if (p == q || fmd_strbadid(sp->fmds_name, checkid) != NULL) {
2260Sstevel@tonic-gate 			fmd_error(EFMD_STAT_BADNAME, "'%s' does not conform to "
2270Sstevel@tonic-gate 			    "statistic naming rules\n", sp->fmds_name);
2280Sstevel@tonic-gate 			err = fmd_set_errno(EFMD_STAT_BADNAME);
2290Sstevel@tonic-gate 			break;
2300Sstevel@tonic-gate 		}
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 		if (sp->fmds_type > FMD_TYPE_SIZE) {
2330Sstevel@tonic-gate 			fmd_error(EFMD_STAT_BADTYPE, "'%s' statistic type %u "
2340Sstevel@tonic-gate 			    "is not valid\n", sp->fmds_name, sp->fmds_type);
2350Sstevel@tonic-gate 			err = fmd_set_errno(EFMD_STAT_BADTYPE);
2360Sstevel@tonic-gate 			break;
2370Sstevel@tonic-gate 		}
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 		if (sp->fmds_type == FMD_TYPE_STRING)
2400Sstevel@tonic-gate 			has_str++; /* flag for second pass; see below */
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 		h = fmd_strhash(sp->fmds_name) % usp->us_hashlen;
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 		for (ep = usp->us_hash[h]; ep != NULL; ep = ep->use_next) {
2450Sstevel@tonic-gate 			if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) == 0)
2460Sstevel@tonic-gate 				break;
2470Sstevel@tonic-gate 		}
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 		if (ep != NULL) {
2500Sstevel@tonic-gate 			fmd_error(EFMD_STAT_DUPNAME, "'%s' is already defined "
2510Sstevel@tonic-gate 			    "as a statistic name\n", sp->fmds_name);
2520Sstevel@tonic-gate 			err = fmd_set_errno(EFMD_STAT_DUPNAME);
2530Sstevel@tonic-gate 			break;
2540Sstevel@tonic-gate 		}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 		ep = fmd_alloc(sizeof (fmd_ustat_elem_t), FMD_SLEEP);
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 		ep->use_next = usp->us_hash[h];
2590Sstevel@tonic-gate 		usp->us_hash[h] = ep;
2600Sstevel@tonic-gate 		ep->use_stat = sp;
2610Sstevel@tonic-gate 		ep->use_chunk = cp;
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 		if (cp != NULL)
2640Sstevel@tonic-gate 			fmd_ustat_chunk_hold(usp, cp);
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 		usp->us_nelems++;
2670Sstevel@tonic-gate 	}
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	/*
2700Sstevel@tonic-gate 	 * If an error occurred, delete all the stats inserted by successful
2710Sstevel@tonic-gate 	 * iterations of the loop [0 .. i-1].  If 'epp' is non-NULL, store a
2720Sstevel@tonic-gate 	 * copy of the input stat pointer that caused the error there.  When
2730Sstevel@tonic-gate 	 * the delete is done, if we allocated a chunk, there should be only
2740Sstevel@tonic-gate 	 * one reference remaining (from the initial fmd_ustat_chunk_init()).
2750Sstevel@tonic-gate 	 */
2760Sstevel@tonic-gate 	if (err != 0) {
2770Sstevel@tonic-gate 		fmd_ustat_delete_locked(usp, i, stats, FMD_B_FALSE);
2780Sstevel@tonic-gate 		ASSERT(cp == NULL || cp->usc_refs == 1);
2790Sstevel@tonic-gate 		if (epp != NULL)
2800Sstevel@tonic-gate 			*epp = template + i;
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	} else if (has_str) {
2830Sstevel@tonic-gate 		/*
2840Sstevel@tonic-gate 		 * If no error occurred and one or more string stats are being
2850Sstevel@tonic-gate 		 * inserted, make a second pass through 'stats' duplicating any
2860Sstevel@tonic-gate 		 * initial strings so that fmd_stat_setstr() can alloc/free.
2870Sstevel@tonic-gate 		 */
2880Sstevel@tonic-gate 		for (sp = stats, i = 0; i < n; i++, sp++) {
2890Sstevel@tonic-gate 			if (sp->fmds_type == FMD_TYPE_STRING &&
2900Sstevel@tonic-gate 			    sp->fmds_value.str != NULL) {
2910Sstevel@tonic-gate 				sp->fmds_value.str = fmd_strdup(
2920Sstevel@tonic-gate 				    sp->fmds_value.str, FMD_SLEEP);
2930Sstevel@tonic-gate 			}
2940Sstevel@tonic-gate 		}
2950Sstevel@tonic-gate 	}
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	if (cp != NULL)
2980Sstevel@tonic-gate 		fmd_ustat_chunk_rele(usp, cp);
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&usp->us_lock);
3010Sstevel@tonic-gate 	return (err ? NULL : stats);
3020Sstevel@tonic-gate }
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate void
fmd_ustat_delete(fmd_ustat_t * usp,uint_t n,fmd_stat_t * sp)3050Sstevel@tonic-gate fmd_ustat_delete(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp)
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate 	(void) pthread_rwlock_wrlock(&usp->us_lock);
3080Sstevel@tonic-gate 	fmd_ustat_delete_locked(usp, n, sp, FMD_B_TRUE);
3090Sstevel@tonic-gate 	(void) pthread_rwlock_unlock(&usp->us_lock);
3100Sstevel@tonic-gate }
311*1193Smws 
312*1193Smws /*
313*1193Smws  * Delete all statistics that are references to external memory (that is, all
314*1193Smws  * statistics inserted with FMD_STAT_NOALLOC), i.e. a NULL ep->use_chunk.
315*1193Smws  */
316*1193Smws void
fmd_ustat_delete_references(fmd_ustat_t * usp)317*1193Smws fmd_ustat_delete_references(fmd_ustat_t *usp)
318*1193Smws {
319*1193Smws 	fmd_ustat_elem_t *ep, **pp;
320*1193Smws 	uint_t i;
321*1193Smws 
322*1193Smws 	(void) pthread_rwlock_wrlock(&usp->us_lock);
323*1193Smws 
324*1193Smws 	for (i = 0; i < usp->us_hashlen; i++) {
325*1193Smws 		for (pp = &usp->us_hash[i], ep = *pp; ep != NULL; ep = *pp) {
326*1193Smws 			if (ep->use_chunk != NULL) {
327*1193Smws 				pp = &ep->use_next;
328*1193Smws 				continue;
329*1193Smws 			}
330*1193Smws 
331*1193Smws 			if (ep->use_stat->fmds_type == FMD_TYPE_STRING)
332*1193Smws 				fmd_strfree(ep->use_stat->fmds_value.str);
333*1193Smws 
334*1193Smws 			*pp = ep->use_next;
335*1193Smws 			fmd_free(ep, sizeof (fmd_ustat_elem_t));
336*1193Smws 			usp->us_nelems--;
337*1193Smws 		}
338*1193Smws 	}
339*1193Smws 
340*1193Smws 	(void) pthread_rwlock_unlock(&usp->us_lock);
341*1193Smws }
342