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