dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fm / fmd / common / fmd_ustat.c
blob8cb53383da300ef526fbfdcaf3327422de082ec2
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #pragma ident "%Z%%M% %I% %E% SMI"
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>
37 static fmd_ustat_chunk_t *
38 fmd_ustat_chunk_init(fmd_ustat_t *usp, fmd_stat_t *base, uint_t len)
40 fmd_ustat_chunk_t *cp;
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;
47 ASSERT(RW_WRITE_HELD(&usp->us_lock));
48 fmd_list_append(&usp->us_chunks, cp);
50 return (cp);
53 static void
54 fmd_ustat_chunk_hold(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp)
56 ASSERT(RW_WRITE_HELD(&usp->us_lock));
57 cp->usc_refs++;
58 ASSERT(cp->usc_refs != 0);
61 static void
62 fmd_ustat_chunk_rele(fmd_ustat_t *usp, fmd_ustat_chunk_t *cp)
64 ASSERT(RW_WRITE_HELD(&usp->us_lock));
65 ASSERT(cp->usc_refs != 0);
67 if (--cp->usc_refs == 0) {
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.
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));
79 fmd_ustat_t *
80 fmd_ustat_create(void)
82 fmd_ustat_t *usp = fmd_zalloc(sizeof (fmd_ustat_t), FMD_SLEEP);
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);
88 return (usp);
91 void
92 fmd_ustat_destroy(fmd_ustat_t *usp)
94 fmd_ustat_elem_t *ep, *np;
95 uint_t i;
97 (void) pthread_rwlock_wrlock(&usp->us_lock);
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);
104 if (ep->use_chunk != NULL)
105 fmd_ustat_chunk_rele(usp, ep->use_chunk);
107 np = ep->use_next;
108 fmd_free(ep, sizeof (fmd_ustat_elem_t));
112 ASSERT(usp->us_chunks.l_next == NULL);
113 ASSERT(usp->us_chunks.l_prev == NULL);
115 fmd_free(usp->us_hash, sizeof (void *) * usp->us_hashlen);
116 fmd_free(usp, sizeof (fmd_ustat_t));
120 fmd_ustat_snapshot(fmd_ustat_t *usp, fmd_ustat_snap_t *uss)
122 const fmd_ustat_elem_t *ep;
123 fmd_stat_t *sp;
124 uint_t i;
126 (void) pthread_rwlock_wrlock(&usp->us_lock);
128 uss->uss_buf = sp = malloc(sizeof (fmd_stat_t) * usp->us_nelems);
129 uss->uss_len = usp->us_nelems;
131 if (uss->uss_buf == NULL) {
132 (void) pthread_rwlock_unlock(&usp->us_lock);
133 return (fmd_set_errno(EFMD_STAT_NOMEM));
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++;
146 ASSERT(sp == uss->uss_buf + uss->uss_len);
147 (void) pthread_rwlock_unlock(&usp->us_lock);
148 return (0);
151 static void
152 fmd_ustat_delete_locked(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp, int strfree)
154 ASSERT(RW_WRITE_HELD(&usp->us_lock));
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];
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;
167 if (ep == NULL)
168 continue; /* silently ignore unregistered entries */
170 if (strfree && ep->use_stat->fmds_type == FMD_TYPE_STRING)
171 fmd_strfree(ep->use_stat->fmds_value.str);
173 if (ep->use_chunk != NULL)
174 fmd_ustat_chunk_rele(usp, ep->use_chunk);
176 *pp = ep->use_next;
177 fmd_free(ep, sizeof (fmd_ustat_elem_t));
178 usp->us_nelems--;
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)
186 fmd_stat_t *stats, *sp;
187 fmd_ustat_chunk_t *cp;
188 uint_t i;
190 int checkid = flags & FMD_USTAT_VALIDATE;
191 int has_str = 0;
192 int err = 0;
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;
200 (void) pthread_rwlock_wrlock(&usp->us_lock);
202 if (flags & FMD_USTAT_ALLOC)
203 cp = fmd_ustat_chunk_init(usp, stats, n);
204 else
205 cp = NULL;
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;
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.
217 for (p = sp->fmds_name; p < q; p++) {
218 if (*p == '\0')
219 break;
222 if (p == q)
223 q[-1] = '\0'; /* nul-terminate for subsequent message */
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;
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;
239 if (sp->fmds_type == FMD_TYPE_STRING)
240 has_str++; /* flag for second pass; see below */
242 h = fmd_strhash(sp->fmds_name) % usp->us_hashlen;
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;
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;
256 ep = fmd_alloc(sizeof (fmd_ustat_elem_t), FMD_SLEEP);
258 ep->use_next = usp->us_hash[h];
259 usp->us_hash[h] = ep;
260 ep->use_stat = sp;
261 ep->use_chunk = cp;
263 if (cp != NULL)
264 fmd_ustat_chunk_hold(usp, cp);
266 usp->us_nelems++;
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()).
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;
282 } else if (has_str) {
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.
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);
297 if (cp != NULL)
298 fmd_ustat_chunk_rele(usp, cp);
300 (void) pthread_rwlock_unlock(&usp->us_lock);
301 return (err ? NULL : stats);
304 void
305 fmd_ustat_delete(fmd_ustat_t *usp, uint_t n, fmd_stat_t *sp)
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);
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.
316 void
317 fmd_ustat_delete_references(fmd_ustat_t *usp)
319 fmd_ustat_elem_t *ep, **pp;
320 uint_t i;
322 (void) pthread_rwlock_wrlock(&usp->us_lock);
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;
331 if (ep->use_stat->fmds_type == FMD_TYPE_STRING)
332 fmd_strfree(ep->use_stat->fmds_value.str);
334 *pp = ep->use_next;
335 fmd_free(ep, sizeof (fmd_ustat_elem_t));
336 usp->us_nelems--;
340 (void) pthread_rwlock_unlock(&usp->us_lock);