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