xref: /titanic_44/usr/src/cmd/fm/fmd/common/fmd_ustat.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <fmd_ustat.h>
30 #include <fmd_alloc.h>
31 #include <fmd_subr.h>
32 #include <fmd_string.h>
33 #include <fmd_error.h>
34 #include <fmd.h>
35 
36 static fmd_ustat_chunk_t *
37 fmd_ustat_chunk_init(fmd_ustat_t *usp, fmd_stat_t *base, uint_t len)
38 {
39 	fmd_ustat_chunk_t *cp;
40 
41 	cp = fmd_zalloc(sizeof (fmd_ustat_chunk_t), FMD_SLEEP);
42 	cp->usc_base = base;
43 	cp->usc_len = len;
44 	cp->usc_refs = 1;
45 
46 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
47 	fmd_list_append(&usp->us_chunks, cp);
48 
49 	return (cp);
50 }
51 
52 static void
53 fmd_ustat_chunk_hold(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp)
54 {
55 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
56 	cp->usc_refs++;
57 	ASSERT(cp->usc_refs != 0);
58 }
59 
60 static void
61 fmd_ustat_chunk_rele(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp)
62 {
63 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
64 	ASSERT(cp->usc_refs != 0);
65 
66 	if (--cp->usc_refs == 0) {
67 		/*
68 		 * Note that any strings pointed to by FMD_TYPE_STRING stats
69 		 * are freed one-by-one before releasing the chunk.  So here
70 		 * we can just free the chunk and not worry about its content.
71 		 */
72 		fmd_free(cp->usc_base, sizeof (fmd_stat_t) * cp->usc_len);
73 		fmd_list_delete(&usp->us_chunks, cp);
74 		fmd_free(cp, sizeof (fmd_ustat_chunk_t));
75 	}
76 }
77 
78 fmd_ustat_t *
79 fmd_ustat_create(void)
80 {
81 	fmd_ustat_t *usp = fmd_zalloc(sizeof (fmd_ustat_t), FMD_SLEEP);
82 
83 	(void) pthread_rwlock_init(&usp->us_lock, NULL);
84 	usp->us_hashlen = fmd.d_str_buckets;
85 	usp->us_hash = fmd_zalloc(sizeof (void *) * usp->us_hashlen, FMD_SLEEP);
86 
87 	return (usp);
88 }
89 
90 void
91 fmd_ustat_destroy(fmd_ustat_t *usp)
92 {
93 	fmd_ustat_elem_t *ep, *np;
94 	uint_t i;
95 
96 	(void) pthread_rwlock_wrlock(&usp->us_lock);
97 
98 	for (i = 0; i < usp->us_hashlen; i++) {
99 		for (ep = usp->us_hash[i]; ep != NULL; ep = np) {
100 			if (ep->use_stat->fmds_type == FMD_TYPE_STRING)
101 				fmd_strfree(ep->use_stat->fmds_value.str);
102 
103 			if (ep->use_chunk != NULL)
104 				fmd_ustat_chunk_rele(usp, ep->use_chunk);
105 
106 			np = ep->use_next;
107 			fmd_free(ep, sizeof (fmd_ustat_elem_t));
108 		}
109 	}
110 
111 	ASSERT(usp->us_chunks.l_next == NULL);
112 	ASSERT(usp->us_chunks.l_prev == NULL);
113 
114 	fmd_free(usp->us_hash, sizeof (void *) * usp->us_hashlen);
115 	fmd_free(usp, sizeof (fmd_ustat_t));
116 }
117 
118 int
119 fmd_ustat_snapshot(fmd_ustat_t *usp, fmd_ustat_snap_t *uss)
120 {
121 	const fmd_ustat_elem_t *ep;
122 	fmd_stat_t *sp;
123 	uint_t i;
124 
125 	(void) pthread_rwlock_wrlock(&usp->us_lock);
126 
127 	uss->uss_buf = sp = malloc(sizeof (fmd_stat_t) * usp->us_nelems);
128 	uss->uss_len = usp->us_nelems;
129 
130 	if (uss->uss_buf == NULL) {
131 		(void) pthread_rwlock_unlock(&usp->us_lock);
132 		return (fmd_set_errno(EFMD_STAT_NOMEM));
133 	}
134 
135 	for (i = 0; i < usp->us_hashlen; i++) {
136 		for (ep = usp->us_hash[i]; ep != NULL; ep = ep->use_next) {
137 			bcopy(ep->use_stat, sp, sizeof (fmd_stat_t));
138 			if (sp->fmds_type == FMD_TYPE_STRING &&
139 			    sp->fmds_value.str != NULL)
140 				sp->fmds_value.str = strdup(sp->fmds_value.str);
141 			sp++;
142 		}
143 	}
144 
145 	ASSERT(sp == uss->uss_buf + uss->uss_len);
146 	(void) pthread_rwlock_unlock(&usp->us_lock);
147 	return (0);
148 }
149 
150 static void
151 fmd_ustat_delete_locked(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp, int strfree)
152 {
153 	ASSERT(RW_WRITE_HELD(&usp->us_lock));
154 
155 	for (; n-- != 0; sp++) {
156 		uint_t h = fmd_strhash(sp->fmds_name) % usp->us_hashlen;
157 		fmd_ustat_elem_t *ep, **pp = &usp->us_hash[h];
158 
159 		for (ep = *pp; ep != NULL; ep = ep->use_next) {
160 			if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) != 0)
161 				pp = &ep->use_next;
162 			else
163 				break;
164 		}
165 
166 		if (ep == NULL)
167 			continue; /* silently ignore unregistered entries */
168 
169 		if (strfree && ep->use_stat->fmds_type == FMD_TYPE_STRING)
170 			fmd_strfree(ep->use_stat->fmds_value.str);
171 
172 		if (ep->use_chunk != NULL)
173 			fmd_ustat_chunk_rele(usp, ep->use_chunk);
174 
175 		*pp = ep->use_next;
176 		fmd_free(ep, sizeof (fmd_ustat_elem_t));
177 		usp->us_nelems--;
178 	}
179 }
180 
181 fmd_stat_t *
182 fmd_ustat_insert(fmd_ustat_t *usp, uint_t flags,
183     uint_t n, fmd_stat_t *template, fmd_stat_t **epp)
184 {
185 	fmd_stat_t *stats, *sp;
186 	fmd_ustat_chunk_t *cp;
187 	uint_t i;
188 
189 	int checkid = flags & FMD_USTAT_VALIDATE;
190 	int has_str = 0;
191 	int err = 0;
192 
193 	if (flags & FMD_USTAT_ALLOC) {
194 		sp = stats = fmd_alloc(sizeof (fmd_stat_t) * n, FMD_SLEEP);
195 		bcopy(template, stats, sizeof (fmd_stat_t) * n);
196 	} else
197 		sp = stats = template;
198 
199 	(void) pthread_rwlock_wrlock(&usp->us_lock);
200 
201 	if (flags & FMD_USTAT_ALLOC)
202 		cp = fmd_ustat_chunk_init(usp, stats, n);
203 	else
204 		cp = NULL;
205 
206 	for (i = 0; i < n; i++, sp++) {
207 		char *p, *q = sp->fmds_name + sizeof (sp->fmds_name);
208 		fmd_ustat_elem_t *ep;
209 		uint_t h;
210 
211 		/*
212 		 * Since a module may be passing in this statistic and our
213 		 * names are represented by a fixed-size array, scan fmds_name
214 		 * to ensure it has a \0 somewhere before we attempt strcmps.
215 		 */
216 		for (p = sp->fmds_name; p < q; p++) {
217 			if (*p == '\0')
218 				break;
219 		}
220 
221 		if (p == q)
222 			q[-1] = '\0'; /* nul-terminate for subsequent message */
223 
224 		if (p == q || fmd_strbadid(sp->fmds_name, checkid) != NULL) {
225 			fmd_error(EFMD_STAT_BADNAME, "'%s' does not conform to "
226 			    "statistic naming rules\n", sp->fmds_name);
227 			err = fmd_set_errno(EFMD_STAT_BADNAME);
228 			break;
229 		}
230 
231 		if (sp->fmds_type > FMD_TYPE_SIZE) {
232 			fmd_error(EFMD_STAT_BADTYPE, "'%s' statistic type %u "
233 			    "is not valid\n", sp->fmds_name, sp->fmds_type);
234 			err = fmd_set_errno(EFMD_STAT_BADTYPE);
235 			break;
236 		}
237 
238 		if (sp->fmds_type == FMD_TYPE_STRING)
239 			has_str++; /* flag for second pass; see below */
240 
241 		h = fmd_strhash(sp->fmds_name) % usp->us_hashlen;
242 
243 		for (ep = usp->us_hash[h]; ep != NULL; ep = ep->use_next) {
244 			if (strcmp(sp->fmds_name, ep->use_stat->fmds_name) == 0)
245 				break;
246 		}
247 
248 		if (ep != NULL) {
249 			fmd_error(EFMD_STAT_DUPNAME, "'%s' is already defined "
250 			    "as a statistic name\n", sp->fmds_name);
251 			err = fmd_set_errno(EFMD_STAT_DUPNAME);
252 			break;
253 		}
254 
255 		ep = fmd_alloc(sizeof (fmd_ustat_elem_t), FMD_SLEEP);
256 
257 		ep->use_next = usp->us_hash[h];
258 		usp->us_hash[h] = ep;
259 		ep->use_stat = sp;
260 		ep->use_chunk = cp;
261 
262 		if (cp != NULL)
263 			fmd_ustat_chunk_hold(usp, cp);
264 
265 		usp->us_nelems++;
266 	}
267 
268 	/*
269 	 * If an error occurred, delete all the stats inserted by successful
270 	 * iterations of the loop [0 .. i-1].  If 'epp' is non-NULL, store a
271 	 * copy of the input stat pointer that caused the error there.  When
272 	 * the delete is done, if we allocated a chunk, there should be only
273 	 * one reference remaining (from the initial fmd_ustat_chunk_init()).
274 	 */
275 	if (err != 0) {
276 		fmd_ustat_delete_locked(usp, i, stats, FMD_B_FALSE);
277 		ASSERT(cp == NULL || cp->usc_refs == 1);
278 		if (epp != NULL)
279 			*epp = template + i;
280 
281 	} else if (has_str) {
282 		/*
283 		 * If no error occurred and one or more string stats are being
284 		 * inserted, make a second pass through 'stats' duplicating any
285 		 * initial strings so that fmd_stat_setstr() can alloc/free.
286 		 */
287 		for (sp = stats, i = 0; i < n; i++, sp++) {
288 			if (sp->fmds_type == FMD_TYPE_STRING &&
289 			    sp->fmds_value.str != NULL) {
290 				sp->fmds_value.str = fmd_strdup(
291 				    sp->fmds_value.str, FMD_SLEEP);
292 			}
293 		}
294 	}
295 
296 	if (cp != NULL)
297 		fmd_ustat_chunk_rele(usp, cp);
298 
299 	(void) pthread_rwlock_unlock(&usp->us_lock);
300 	return (err ? NULL : stats);
301 }
302 
303 void
304 fmd_ustat_delete(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp)
305 {
306 	(void) pthread_rwlock_wrlock(&usp->us_lock);
307 	fmd_ustat_delete_locked(usp, n, sp, FMD_B_TRUE);
308 	(void) pthread_rwlock_unlock(&usp->us_lock);
309 }
310